diff --git a/.github/workflows/publish_npm.yml b/.github/workflows/publish_npm.yml index e398f0e1d..9a44f652b 100644 --- a/.github/workflows/publish_npm.yml +++ b/.github/workflows/publish_npm.yml @@ -21,7 +21,6 @@ jobs: packages/create, packages/dbos-cloud, packages/dbos-compiler, - packages/dbos-openapi, packages/dbos-sqs, packages/dbos-kafkajs, ] diff --git a/package-lock.json b/package-lock.json index 26ee8f535..6e7938b0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,6 @@ "knex": "3.1.0", "koa": "^2.15.0", "lodash": "4.17.21", - "openapi-types": "12.1.3", "pg": "8.11.3", "pg-protocol": "^1.6.1", "reflect-metadata": "^0.2.2", @@ -2147,10 +2146,6 @@ "resolved": "packages/dbos-kafkajs", "link": true }, - "node_modules/@dbos-inc/dbos-openapi": { - "resolved": "packages/dbos-openapi", - "link": true - }, "node_modules/@dbos-inc/dbos-random": { "resolved": "packages/communicator-random", "link": true @@ -4664,10 +4659,6 @@ "pretty-format": "^29.0.0" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "license": "MIT" - }, "node_modules/@types/jsonwebtoken": { "version": "9.0.6", "license": "MIT", @@ -9207,6 +9198,7 @@ }, "node_modules/json5": { "version": "2.2.3", + "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -10050,6 +10042,7 @@ }, "node_modules/normalize-path": { "version": "3.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10173,10 +10166,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/openapi-types": { - "version": "12.1.3", - "license": "MIT" - }, "node_modules/optionator": { "version": "0.9.4", "dev": true, @@ -12110,52 +12099,6 @@ } } }, - "node_modules/ts-json-schema-generator": { - "version": "1.5.1", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.15", - "commander": "^12.0.0", - "glob": "^8.0.3", - "json5": "^2.2.3", - "normalize-path": "^3.0.0", - "safe-stable-stringify": "^2.4.3", - "typescript": "~5.4.2" - }, - "bin": { - "ts-json-schema-generator": "bin/ts-json-schema-generator" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/ts-json-schema-generator/node_modules/glob": { - "version": "8.1.0", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ts-json-schema-generator/node_modules/minimatch": { - "version": "5.1.6", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ts-morph": { "version": "22.0.0", "license": "MIT", @@ -12416,6 +12359,7 @@ }, "node_modules/typescript": { "version": "5.4.5", + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -13497,6 +13441,7 @@ "packages/dbos-openapi": { "name": "@dbos-inc/dbos-openapi", "version": "0.0.0-placeholder", + "extraneous": true, "license": "MIT", "dependencies": { "commander": "^11.0.0", @@ -13517,21 +13462,6 @@ "ts-jest": "^29.1.1" } }, - "packages/dbos-openapi/node_modules/@types/supertest": { - "version": "2.0.16", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/superagent": "*" - } - }, - "packages/dbos-openapi/node_modules/commander": { - "version": "11.1.0", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, "packages/dbos-sqs": { "name": "@dbos-inc/dbos-sqs", "version": "0.0.0-placeholder", diff --git a/package.json b/package.json index bb0f42d74..de5e442b5 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,6 @@ "knex": "3.1.0", "koa": "^2.15.0", "lodash": "4.17.21", - "openapi-types": "12.1.3", "pg": "8.11.3", "pg-protocol": "^1.6.1", "reflect-metadata": "^0.2.2", diff --git a/packages/create/cli.ts b/packages/create/cli.ts index 49460eb6a..a7d115546 100755 --- a/packages/create/cli.ts +++ b/packages/create/cli.ts @@ -26,9 +26,8 @@ program throw new Error(`Unexpected arguments: ${command.args.join(',')}; Did you forget '--'?`); } let {appName, template} = options; - if (appName || template) { - appName = appName || 'dbos-hello-app'; - template = template || 'hello'; + if (template) { + appName = template; } else { const templates = listTemplates(); @@ -41,7 +40,7 @@ program appName = await input( { message: 'What is the application/directory name to create?', - default: template, // Default to the template name + default: appName || template, validate: isValidApplicationName, }); } diff --git a/packages/create/templates/dbos-drizzle/README.md b/packages/create/templates/dbos-drizzle/README.md new file mode 100644 index 000000000..fc915c891 --- /dev/null +++ b/packages/create/templates/dbos-drizzle/README.md @@ -0,0 +1,23 @@ +# DBOS Hello with Drizzle + +This is a [DBOS app](https://docs.dbos.dev/) bootstrapped with `npx @dbos-inc/create` and using [Drizzle](https://docs.dbos.dev/typescript/tutorials/orms/using-drizzle). + +## Getting Started + +Run these commands to build your app, set up its database, then launch it: + +```bash +npm run build +npx dbos migrate +npm run start +``` + +To see that it's working, visit this URL in your browser: [`http://localhost:3000/`](http://localhost:3000/). + +Congratulations! You just launched a DBOS application. + +## Next Steps + +- To add more functionality to this application, modify `src/operations.ts`, then rebuild and restart it. Alternatively, `npm run dev` uses `nodemon` to automatically rebuild and restart the app when source files change, using instructions specified in `nodemon.json`. +- For a detailed tutorial, check out the [programming guide](https://docs.dbos.dev/typescript/programming-guide). +- To learn more about DBOS, take a look at [the documentation](https://docs.dbos.dev/) or [source code](https://github.com/dbos-inc/dbos-transact-ts). diff --git a/packages/create/templates/hello-drizzle/dbos-config.yaml b/packages/create/templates/dbos-drizzle/dbos-config.yaml similarity index 100% rename from packages/create/templates/hello-drizzle/dbos-config.yaml rename to packages/create/templates/dbos-drizzle/dbos-config.yaml diff --git a/packages/create/templates/hello-drizzle/drizzle.config.ts b/packages/create/templates/dbos-drizzle/drizzle.config.ts similarity index 100% rename from packages/create/templates/hello-drizzle/drizzle.config.ts rename to packages/create/templates/dbos-drizzle/drizzle.config.ts diff --git a/packages/create/templates/hello-drizzle/drizzle/0000_hello.sql b/packages/create/templates/dbos-drizzle/drizzle/0000_hello.sql similarity index 100% rename from packages/create/templates/hello-drizzle/drizzle/0000_hello.sql rename to packages/create/templates/dbos-drizzle/drizzle/0000_hello.sql diff --git a/packages/create/templates/hello-drizzle/drizzle/meta/0000_snapshot.json b/packages/create/templates/dbos-drizzle/drizzle/meta/0000_snapshot.json similarity index 100% rename from packages/create/templates/hello-drizzle/drizzle/meta/0000_snapshot.json rename to packages/create/templates/dbos-drizzle/drizzle/meta/0000_snapshot.json diff --git a/packages/create/templates/hello-drizzle/drizzle/meta/_journal.json b/packages/create/templates/dbos-drizzle/drizzle/meta/_journal.json similarity index 100% rename from packages/create/templates/hello-drizzle/drizzle/meta/_journal.json rename to packages/create/templates/dbos-drizzle/drizzle/meta/_journal.json diff --git a/packages/create/templates/hello-contexts/eslint.config.js b/packages/create/templates/dbos-drizzle/eslint.config.js similarity index 100% rename from packages/create/templates/hello-contexts/eslint.config.js rename to packages/create/templates/dbos-drizzle/eslint.config.js diff --git a/packages/create/templates/hello-contexts/gitignore.template b/packages/create/templates/dbos-drizzle/gitignore.template similarity index 100% rename from packages/create/templates/hello-contexts/gitignore.template rename to packages/create/templates/dbos-drizzle/gitignore.template diff --git a/packages/create/templates/hello-contexts/jest.config.js b/packages/create/templates/dbos-drizzle/jest.config.js similarity index 100% rename from packages/create/templates/hello-contexts/jest.config.js rename to packages/create/templates/dbos-drizzle/jest.config.js diff --git a/packages/create/templates/dbos-drizzle/nodemon.json b/packages/create/templates/dbos-drizzle/nodemon.json new file mode 100644 index 000000000..3c8e43aa3 --- /dev/null +++ b/packages/create/templates/dbos-drizzle/nodemon.json @@ -0,0 +1,7 @@ +{ + "watch": ["src/"], + "ext": "ts,json", + "ignore": ["src/**/*.test.ts"], + "exec": "npm run build && npm run start" +} + diff --git a/packages/create/templates/hello-drizzle/package.json b/packages/create/templates/dbos-drizzle/package.json similarity index 95% rename from packages/create/templates/hello-drizzle/package.json rename to packages/create/templates/dbos-drizzle/package.json index 8411858da..5a6d786f9 100644 --- a/packages/create/templates/hello-drizzle/package.json +++ b/packages/create/templates/dbos-drizzle/package.json @@ -1,5 +1,5 @@ { - "name": "dbos-hello-drizzle", + "name": "dbos-drizzle", "version": "0.0.1", "scripts": { "build": "tsc", diff --git a/packages/create/templates/hello-drizzle/src/operations.test.ts b/packages/create/templates/dbos-drizzle/src/operations.test.ts similarity index 100% rename from packages/create/templates/hello-drizzle/src/operations.test.ts rename to packages/create/templates/dbos-drizzle/src/operations.test.ts diff --git a/packages/create/templates/hello-drizzle/src/operations.ts b/packages/create/templates/dbos-drizzle/src/operations.ts similarity index 100% rename from packages/create/templates/hello-drizzle/src/operations.ts rename to packages/create/templates/dbos-drizzle/src/operations.ts diff --git a/packages/create/templates/hello-drizzle/src/schema.ts b/packages/create/templates/dbos-drizzle/src/schema.ts similarity index 100% rename from packages/create/templates/hello-drizzle/src/schema.ts rename to packages/create/templates/dbos-drizzle/src/schema.ts diff --git a/packages/create/templates/hello-contexts/start_postgres_docker.js b/packages/create/templates/dbos-drizzle/start_postgres_docker.js similarity index 100% rename from packages/create/templates/hello-contexts/start_postgres_docker.js rename to packages/create/templates/dbos-drizzle/start_postgres_docker.js diff --git a/packages/create/templates/hello-contexts/tsconfig.json b/packages/create/templates/dbos-drizzle/tsconfig.json similarity index 100% rename from packages/create/templates/hello-contexts/tsconfig.json rename to packages/create/templates/dbos-drizzle/tsconfig.json diff --git a/packages/create/templates/dbos-knex/README.md b/packages/create/templates/dbos-knex/README.md new file mode 100644 index 000000000..1b87cecc6 --- /dev/null +++ b/packages/create/templates/dbos-knex/README.md @@ -0,0 +1,23 @@ +# DBOS Hello + +This is a [DBOS app](https://docs.dbos.dev/) bootstrapped with `npx @dbos-inc/create` and using [Knex](https://docs.dbos.dev/typescript/tutorials/orms/using-knex). + +## Getting Started + +Run these commands to build your app, set up its database, then launch it: + +```bash +npm run build +npx dbos migrate +npm run start +``` + +To see that it's working, visit this URL in your browser: [`http://localhost:3000/`](http://localhost:3000/). + +Congratulations! You just launched a DBOS application. + +## Next Steps + +- To add more functionality to this application, modify `src/main.ts`, then rebuild and restart it. Alternatively, `npm run dev` uses `nodemon` to automatically rebuild and restart the app when source files change, using instructions specified in `nodemon.json`. +- For a detailed tutorial, check out the [programming guide](https://docs.dbos.dev/typescript/programming-guide). +- To learn more about DBOS, take a look at [the documentation](https://docs.dbos.dev/) or [source code](https://github.com/dbos-inc/dbos-transact-ts). diff --git a/packages/create/templates/hello-express/dbos-config.yaml b/packages/create/templates/dbos-knex/dbos-config.yaml similarity index 100% rename from packages/create/templates/hello-express/dbos-config.yaml rename to packages/create/templates/dbos-knex/dbos-config.yaml diff --git a/packages/create/templates/hello-drizzle/eslint.config.js b/packages/create/templates/dbos-knex/eslint.config.js similarity index 100% rename from packages/create/templates/hello-drizzle/eslint.config.js rename to packages/create/templates/dbos-knex/eslint.config.js diff --git a/packages/create/templates/hello-drizzle/gitignore.template b/packages/create/templates/dbos-knex/gitignore.template similarity index 100% rename from packages/create/templates/hello-drizzle/gitignore.template rename to packages/create/templates/dbos-knex/gitignore.template diff --git a/packages/create/templates/hello-drizzle/jest.config.js b/packages/create/templates/dbos-knex/jest.config.js similarity index 100% rename from packages/create/templates/hello-drizzle/jest.config.js rename to packages/create/templates/dbos-knex/jest.config.js diff --git a/packages/create/templates/hello-contexts/knexfile.js b/packages/create/templates/dbos-knex/knexfile.js similarity index 100% rename from packages/create/templates/hello-contexts/knexfile.js rename to packages/create/templates/dbos-knex/knexfile.js diff --git a/packages/create/templates/hello-contexts/migrations/20240212161006_create_dbos_hello_tables.js b/packages/create/templates/dbos-knex/migrations/20240212161006_create_dbos_hello_tables.js similarity index 100% rename from packages/create/templates/hello-contexts/migrations/20240212161006_create_dbos_hello_tables.js rename to packages/create/templates/dbos-knex/migrations/20240212161006_create_dbos_hello_tables.js diff --git a/packages/create/templates/dbos-knex/nodemon.json b/packages/create/templates/dbos-knex/nodemon.json new file mode 100644 index 000000000..3c8e43aa3 --- /dev/null +++ b/packages/create/templates/dbos-knex/nodemon.json @@ -0,0 +1,7 @@ +{ + "watch": ["src/"], + "ext": "ts,json", + "ignore": ["src/**/*.test.ts"], + "exec": "npm run build && npm run start" +} + diff --git a/packages/create/templates/hello-contexts/package.json b/packages/create/templates/dbos-knex/package.json similarity index 81% rename from packages/create/templates/hello-contexts/package.json rename to packages/create/templates/dbos-knex/package.json index 95de0e9fd..3a4c52ed9 100644 --- a/packages/create/templates/hello-contexts/package.json +++ b/packages/create/templates/dbos-knex/package.json @@ -1,11 +1,10 @@ { - "name": "dbos-hello", + "name": "dbos-knex", "version": "0.0.1", "scripts": { "build": "tsc", - "test": "npx dbos rollback && npx dbos migrate && jest", + "test": "npx dbos migrate && jest", "lint": "eslint src", - "lint-fix": "eslint --fix src", "dev": "nodemon", "start": "npx dbos start" }, @@ -22,6 +21,7 @@ }, "dependencies": { "@dbos-inc/dbos-sdk": "file:../../../..", + "express": "^4.21.2", "knex": "3.1.0" } } diff --git a/packages/create/templates/hello-express/src/main.test.ts b/packages/create/templates/dbos-knex/src/main.test.ts similarity index 100% rename from packages/create/templates/hello-express/src/main.test.ts rename to packages/create/templates/dbos-knex/src/main.test.ts diff --git a/packages/create/templates/dbos-knex/src/main.ts b/packages/create/templates/dbos-knex/src/main.ts new file mode 100644 index 000000000..1c579176d --- /dev/null +++ b/packages/create/templates/dbos-knex/src/main.ts @@ -0,0 +1,94 @@ +// Welcome to DBOS! + +// This is a sample "Hello" app built with DBOS and Knex. +// It greets visitors and keeps track of how many times each visitor has been greeted. + +import express, { Request, Response } from 'express'; +import { DBOS } from "@dbos-inc/dbos-sdk"; + +export interface dbos_hello { + name: string; + greet_count: number; +} + +export class Hello { + // This transaction uses DBOS and Knex to perform database operations. + // It retrieves and increments the number of times a user has been greeted. + @DBOS.transaction() + static async helloTransaction(user: string) { + const query = "INSERT INTO dbos_hello (name, greet_count) VALUES (?, 1) ON CONFLICT (name) DO UPDATE SET greet_count = dbos_hello.greet_count + 1 RETURNING greet_count;"; + const { rows } = (await DBOS.knexClient.raw(query, [user])) as { rows: dbos_hello[] }; + const greet_count = rows[0].greet_count; + const greeting = `Hello, ${user}! You have been greeted ${greet_count} times.`; + return makeHTML(greeting); + } +} + +// Let's create an HTML + CSS Readme for our app. +function readme() { + return makeHTML( + `Visit the route /greeting/{name} to be greeted!
+ For example, visit /greeting/Mike
+ The counter increments with each page visit.` + ); +} + +function makeHTML(message: string) { + const page = ` + + + + DBOS Template App + + + +

Welcome to DBOS!

+

` + message + `

+

+ To learn how to run this app yourself, visit our + Quickstart. +

+ Then, to learn how to build crashproof apps, continue to our + Programming Guide.
+

+ + `; + return page; +} + +// Let's create an HTTP server using Express.js +export const app = express(); +app.use(express.json()); + +// Serve the Readme from the root path +app.get('/', (req: Request, res: Response) => { + res.send(readme()); +}); + +// Serve the transaction from the /greeting/:name path +app.get('/greeting/:name', (req: Request, res: Response) => { + const { name } = req.params; + Hello.helloTransaction(name) + .then(result => res.send(result)) + .catch(error => { + console.error(error); + res.status(500).send('Internal Server Error'); + }); +}); + +// Finally, launch DBOS and start the server +async function main() { + await DBOS.launch({expressApp: app}); + const PORT = 3000; + const ENV = process.env.NODE_ENV || 'development'; + + app.listen(PORT, () => { + console.log(`🚀 Server is running on http://localhost:${PORT}`); + console.log(`🌟 Environment: ${ENV}`); + }); +} + +// Only start the server when this file is run directly from Node +if (require.main === module) { + main().catch(console.log); +} \ No newline at end of file diff --git a/packages/create/templates/hello-drizzle/start_postgres_docker.js b/packages/create/templates/dbos-knex/start_postgres_docker.js similarity index 100% rename from packages/create/templates/hello-drizzle/start_postgres_docker.js rename to packages/create/templates/dbos-knex/start_postgres_docker.js diff --git a/packages/create/templates/hello-drizzle/tsconfig.json b/packages/create/templates/dbos-knex/tsconfig.json similarity index 100% rename from packages/create/templates/hello-drizzle/tsconfig.json rename to packages/create/templates/dbos-knex/tsconfig.json diff --git a/packages/create/templates/dbos-prisma/README.md b/packages/create/templates/dbos-prisma/README.md new file mode 100644 index 000000000..71e598d7f --- /dev/null +++ b/packages/create/templates/dbos-prisma/README.md @@ -0,0 +1,23 @@ +# DBOS Hello with Prisma + +This is a [DBOS app](https://docs.dbos.dev/) bootstrapped with `npx @dbos-inc/create` and using [Prisma](https://docs.dbos.dev/typescript/tutorials/orms/using-prisma). + +## Getting Started + +Run these commands to build your app, set up its database, then launch it: + +```bash +npm run build +npx dbos migrate +npm run start +``` + +To see that it's working, visit this URL in your browser: [`http://localhost:3000/`](http://localhost:3000/). + +Congratulations! You just launched a DBOS application. + +## Next Steps + +- To add more functionality to this application, modify `src/operations.ts`, then rebuild and restart it. Alternatively, `npm run dev` uses `nodemon` to automatically rebuild and restart the app when source files change, using instructions specified in `nodemon.json`. +- For a detailed tutorial, check out the [programming guide](https://docs.dbos.dev/typescript/programming-guide). +- To learn more about DBOS, take a look at [the documentation](https://docs.dbos.dev/) or [source code](https://github.com/dbos-inc/dbos-transact-ts). diff --git a/packages/create/templates/hello-prisma/dbos-config.yaml b/packages/create/templates/dbos-prisma/dbos-config.yaml similarity index 100% rename from packages/create/templates/hello-prisma/dbos-config.yaml rename to packages/create/templates/dbos-prisma/dbos-config.yaml diff --git a/packages/create/templates/hello-express/eslint.config.js b/packages/create/templates/dbos-prisma/eslint.config.js similarity index 100% rename from packages/create/templates/hello-express/eslint.config.js rename to packages/create/templates/dbos-prisma/eslint.config.js diff --git a/packages/create/templates/hello-prisma/generate_env.js b/packages/create/templates/dbos-prisma/generate_env.js similarity index 100% rename from packages/create/templates/hello-prisma/generate_env.js rename to packages/create/templates/dbos-prisma/generate_env.js diff --git a/packages/create/templates/hello-prisma/gitignore.template b/packages/create/templates/dbos-prisma/gitignore.template similarity index 100% rename from packages/create/templates/hello-prisma/gitignore.template rename to packages/create/templates/dbos-prisma/gitignore.template diff --git a/packages/create/templates/hello-express/jest.config.js b/packages/create/templates/dbos-prisma/jest.config.js similarity index 100% rename from packages/create/templates/hello-express/jest.config.js rename to packages/create/templates/dbos-prisma/jest.config.js diff --git a/packages/create/templates/hello-prisma/nodemon.json b/packages/create/templates/dbos-prisma/nodemon.json similarity index 61% rename from packages/create/templates/hello-prisma/nodemon.json rename to packages/create/templates/dbos-prisma/nodemon.json index 502fc99fb..fb8f44814 100644 --- a/packages/create/templates/hello-prisma/nodemon.json +++ b/packages/create/templates/dbos-prisma/nodemon.json @@ -2,7 +2,7 @@ "watch": ["src/", "prisma/"], "ext": "ts,json", "ignore": ["src/**/*.test.ts"], - "exec": "npm run build && npx dbos migrate && npm run start" + "exec": "npm run build && npm run start" } \ No newline at end of file diff --git a/packages/create/templates/hello-prisma/package.json b/packages/create/templates/dbos-prisma/package.json similarity index 95% rename from packages/create/templates/hello-prisma/package.json rename to packages/create/templates/dbos-prisma/package.json index 554b7e372..e277999a8 100644 --- a/packages/create/templates/hello-prisma/package.json +++ b/packages/create/templates/dbos-prisma/package.json @@ -1,5 +1,5 @@ { - "name": "dbos-hello-prisma", + "name": "dbos-prisma", "version": "0.0.1", "scripts": { "build": "node generate_env.js && npx prisma generate && tsc", diff --git a/packages/create/templates/hello-prisma/prisma/migrations/20240604184654_init/migration.sql b/packages/create/templates/dbos-prisma/prisma/migrations/20240604184654_init/migration.sql similarity index 100% rename from packages/create/templates/hello-prisma/prisma/migrations/20240604184654_init/migration.sql rename to packages/create/templates/dbos-prisma/prisma/migrations/20240604184654_init/migration.sql diff --git a/packages/create/templates/hello-prisma/prisma/migrations/migration_lock.toml b/packages/create/templates/dbos-prisma/prisma/migrations/migration_lock.toml similarity index 100% rename from packages/create/templates/hello-prisma/prisma/migrations/migration_lock.toml rename to packages/create/templates/dbos-prisma/prisma/migrations/migration_lock.toml diff --git a/packages/create/templates/hello-prisma/prisma/schema.prisma b/packages/create/templates/dbos-prisma/prisma/schema.prisma similarity index 100% rename from packages/create/templates/hello-prisma/prisma/schema.prisma rename to packages/create/templates/dbos-prisma/prisma/schema.prisma diff --git a/packages/create/templates/hello-prisma/src/operations.test.ts b/packages/create/templates/dbos-prisma/src/operations.test.ts similarity index 100% rename from packages/create/templates/hello-prisma/src/operations.test.ts rename to packages/create/templates/dbos-prisma/src/operations.test.ts diff --git a/packages/create/templates/hello-prisma/src/operations.ts b/packages/create/templates/dbos-prisma/src/operations.ts similarity index 100% rename from packages/create/templates/hello-prisma/src/operations.ts rename to packages/create/templates/dbos-prisma/src/operations.ts diff --git a/packages/create/templates/hello-express/start_postgres_docker.js b/packages/create/templates/dbos-prisma/start_postgres_docker.js old mode 100755 new mode 100644 similarity index 100% rename from packages/create/templates/hello-express/start_postgres_docker.js rename to packages/create/templates/dbos-prisma/start_postgres_docker.js diff --git a/packages/create/templates/hello-prisma/tsconfig.json b/packages/create/templates/dbos-prisma/tsconfig.json similarity index 100% rename from packages/create/templates/hello-prisma/tsconfig.json rename to packages/create/templates/dbos-prisma/tsconfig.json diff --git a/packages/create/templates/dbos-typeorm/README.md b/packages/create/templates/dbos-typeorm/README.md new file mode 100644 index 000000000..a8a186c3e --- /dev/null +++ b/packages/create/templates/dbos-typeorm/README.md @@ -0,0 +1,23 @@ +# DBOS Hello with TypeORM + +This is a [DBOS app](https://docs.dbos.dev/) bootstrapped with `npx @dbos-inc/create` and using [TypeORM](https://docs.dbos.dev/typescript/tutorials/orms/using-typeorm). + +## Getting Started + +Run these commands to build your app, set up its database, then launch it: + +```bash +npm run build +npx dbos migrate +npm run start +``` + +To see that it's working, visit this URL in your browser: [`http://localhost:3000/`](http://localhost:3000/). + +Congratulations! You just launched a DBOS application. + +## Next Steps + +- To add more functionality to this application, modify `src/main.ts`, then rebuild and restart it. Alternatively, `npm run dev` uses `nodemon` to automatically rebuild and restart the app when source files change, using instructions specified in `nodemon.json`. +- For a detailed tutorial, check out the [programming guide](https://docs.dbos.dev/typescript/programming-guide). +- To learn more about DBOS, take a look at [the documentation](https://docs.dbos.dev/) or [source code](https://github.com/dbos-inc/dbos-transact-ts). diff --git a/packages/create/templates/hello-typeorm/datasource.ts b/packages/create/templates/dbos-typeorm/datasource.ts similarity index 100% rename from packages/create/templates/hello-typeorm/datasource.ts rename to packages/create/templates/dbos-typeorm/datasource.ts diff --git a/packages/create/templates/hello-typeorm/dbos-config.yaml b/packages/create/templates/dbos-typeorm/dbos-config.yaml similarity index 100% rename from packages/create/templates/hello-typeorm/dbos-config.yaml rename to packages/create/templates/dbos-typeorm/dbos-config.yaml diff --git a/packages/create/templates/hello-typeorm/entities/DBOSHello.ts b/packages/create/templates/dbos-typeorm/entities/DBOSHello.ts similarity index 100% rename from packages/create/templates/hello-typeorm/entities/DBOSHello.ts rename to packages/create/templates/dbos-typeorm/entities/DBOSHello.ts diff --git a/packages/create/templates/hello-prisma/eslint.config.js b/packages/create/templates/dbos-typeorm/eslint.config.js similarity index 100% rename from packages/create/templates/hello-prisma/eslint.config.js rename to packages/create/templates/dbos-typeorm/eslint.config.js diff --git a/packages/create/templates/hello-express/gitignore.template b/packages/create/templates/dbos-typeorm/gitignore.template similarity index 100% rename from packages/create/templates/hello-express/gitignore.template rename to packages/create/templates/dbos-typeorm/gitignore.template diff --git a/packages/create/templates/hello-prisma/jest.config.js b/packages/create/templates/dbos-typeorm/jest.config.js similarity index 100% rename from packages/create/templates/hello-prisma/jest.config.js rename to packages/create/templates/dbos-typeorm/jest.config.js diff --git a/packages/create/templates/hello-typeorm/migrations/1714934318136-DBOSHello.ts b/packages/create/templates/dbos-typeorm/migrations/1714934318136-DBOSHello.ts similarity index 100% rename from packages/create/templates/hello-typeorm/migrations/1714934318136-DBOSHello.ts rename to packages/create/templates/dbos-typeorm/migrations/1714934318136-DBOSHello.ts diff --git a/packages/create/templates/dbos-typeorm/nodemon.json b/packages/create/templates/dbos-typeorm/nodemon.json new file mode 100644 index 000000000..5a2898243 --- /dev/null +++ b/packages/create/templates/dbos-typeorm/nodemon.json @@ -0,0 +1,8 @@ +{ + "watch": ["src/", "entities/"], + "ext": "ts,json", + "ignore": ["src/**/*.test.ts"], + "exec": "npm run build && npm run start" + } + + \ No newline at end of file diff --git a/packages/create/templates/hello-typeorm/package.json b/packages/create/templates/dbos-typeorm/package.json similarity index 95% rename from packages/create/templates/hello-typeorm/package.json rename to packages/create/templates/dbos-typeorm/package.json index 0ee01a55d..22f2d9973 100644 --- a/packages/create/templates/hello-typeorm/package.json +++ b/packages/create/templates/dbos-typeorm/package.json @@ -1,5 +1,5 @@ { - "name": "dbos-hello-typeorm", + "name": "dbos-typeorm", "version": "0.0.1", "scripts": { "build": "tsc", diff --git a/packages/create/templates/hello-typeorm/src/operations.test.ts b/packages/create/templates/dbos-typeorm/src/operations.test.ts similarity index 100% rename from packages/create/templates/hello-typeorm/src/operations.test.ts rename to packages/create/templates/dbos-typeorm/src/operations.test.ts diff --git a/packages/create/templates/hello-typeorm/src/operations.ts b/packages/create/templates/dbos-typeorm/src/operations.ts similarity index 100% rename from packages/create/templates/hello-typeorm/src/operations.ts rename to packages/create/templates/dbos-typeorm/src/operations.ts diff --git a/packages/create/templates/hello-nextjs/start_postgres_docker.js b/packages/create/templates/dbos-typeorm/start_postgres_docker.js old mode 100755 new mode 100644 similarity index 100% rename from packages/create/templates/hello-nextjs/start_postgres_docker.js rename to packages/create/templates/dbos-typeorm/start_postgres_docker.js diff --git a/packages/create/templates/hello-typeorm/tsconfig.json b/packages/create/templates/dbos-typeorm/tsconfig.json similarity index 100% rename from packages/create/templates/hello-typeorm/tsconfig.json rename to packages/create/templates/dbos-typeorm/tsconfig.json diff --git a/packages/create/templates/hello-contexts/.vscode/extensions.json b/packages/create/templates/hello-contexts/.vscode/extensions.json deleted file mode 100644 index e4e81f058..000000000 --- a/packages/create/templates/hello-contexts/.vscode/extensions.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - // Automatically recommend the DBOS extension to VSCode users. - // Documentation on extensions.json: http://go.microsoft.com/fwlink/?LinkId=827846 - "recommendations": [ - "dbos-inc.dbos-ttdbg" - ] -} \ No newline at end of file diff --git a/packages/create/templates/hello-contexts/.vscode/launch.json b/packages/create/templates/hello-contexts/.vscode/launch.json deleted file mode 100644 index 3977fca42..000000000 --- a/packages/create/templates/hello-contexts/.vscode/launch.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - // Automatically configure the VSCode debugger for DBOS projects. - // Documentation on launch.json: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node-terminal", - "request": "launch", - "name": "Local Debug", - "command": "npx dbos start", - "preLaunchTask": "npm: build", - }, - { - "type": "node-terminal", - "request": "launch", - "name": "Time Travel Debug", - "command": "npx dbos debug -x ${command:dbos-ttdbg.get-proxy-url} -u ${command:dbos-ttdbg.pick-workflow-id}", - "preLaunchTask": "npm: build" - } - ] - } \ No newline at end of file diff --git a/packages/create/templates/hello-contexts/.vscode/tasks.json b/packages/create/templates/hello-contexts/.vscode/tasks.json deleted file mode 100644 index 793379743..000000000 --- a/packages/create/templates/hello-contexts/.vscode/tasks.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - // Configure the build command for DBOS projects using VSCode. - // Documentation on tasks.json: https://code.visualstudio.com/docs/editor/tasks - "version": "2.0.0", - "tasks": [ - { - "label": "npm: build", - "type": "npm", - "script": "build", - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": [ - "$tsc" - ] - } - ] - } \ No newline at end of file diff --git a/packages/create/templates/hello-contexts/README.md b/packages/create/templates/hello-contexts/README.md deleted file mode 100644 index ddd98a2a3..000000000 --- a/packages/create/templates/hello-contexts/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# DBOS Hello - -*NOTE:* This is an older version of the DBOS `hello` app that uses a previous API based on passing [DBOS contexts](https://docs.dbos.dev/typescript/reference/transactapi/oldapi/contexts) around as function arguments. - -This is a [DBOS app](https://docs.dbos.dev/) bootstrapped with `npx @dbos-inc/create` and using [Knex](https://docs.dbos.dev/typescript/tutorials/orms/using-knex). - -## Getting Started - -Before you can launch your app, you need a database. -DBOS works with any Postgres database, but to make things easier, we've provided a script that starts a Docker Postgres container and creates a database. -Run: - -```bash -node start_postgres_docker.js -``` - -If successful, the script should print `Database started successfully!`. - -Next, you can build and run the app in one step under `nodemon`: - -```bash -npm run dev -``` - -To see that it's working, visit this URL in your browser: [`http://localhost:3000/greeting/dbos`](http://localhost:3000/greeting/dbos). -You should get this message: `Hello, dbos! You have been greeted 1 times.` -Each time you refresh the page, the counter should go up by one! - -Congratulations! You just launched a DBOS application. - -## Production build - -In production, instead of using `nodemon`, the following separate steps should be used to build, run database setup, and start the app. - -```bash -npm run build -``` - -Then, run a schema migration to create some tables: - -```bash -npx dbos migrate -``` - -If successful, the migration should print `Migration successful!`. - -Finally, run the app: - -```bash -npx dbos start -``` - -## The application - -The core of the application resides in `src/operations.ts`. It declares an "hello world" DBOS workflow served at `/greetings/:user`. To add more functionality, modify `src/operations.ts`. If you used `npm run dev`, it will automatically rebuild and restart. - -## Running in DBOS Cloud - -To deploy this app to DBOS Cloud, first install the DBOS Cloud CLI (example with [npm](https://www.npmjs.com/)): - -```shell -npm i -g @dbos-inc/dbos-cloud -``` - -Then, run this command to deploy your app: - -```shell -dbos-cloud app deploy -``` - -functionality to this application, modify `src/operations.ts`. If you used `npm run dev`, it will automatically rebuild and restart. - -## Next Steps - -- For a detailed tutorial, check out our [programming quickstart](https://docs.dbos.dev/getting-started/quickstart-programming). -- To learn more about DBOS, take a look at [our documentation](https://docs.dbos.dev/) or our [source code](https://github.com/dbos-inc/dbos-transact). diff --git a/packages/create/templates/hello-contexts/dbos-config.yaml b/packages/create/templates/hello-contexts/dbos-config.yaml deleted file mode 100644 index 3d4d04da6..000000000 --- a/packages/create/templates/hello-contexts/dbos-config.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# To enable auto-completion and validation for this file in VSCode, install the RedHat YAML extension -# https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml - -# yaml-language-server: $schema=https://raw.githubusercontent.com/dbos-inc/dbos-transact/main/dbos-config.schema.json - -language: node -database: - hostname: localhost - port: 5432 - username: postgres - password: ${PGPASSWORD} - connectionTimeoutMillis: 3000 - app_db_client: knex - migrate: - - npx knex migrate:latest - rollback: - - npx knex migrate:rollback -runtimeConfig: - entrypoints: - - dist/operations.js \ No newline at end of file diff --git a/packages/create/templates/hello-contexts/nodemon.json b/packages/create/templates/hello-contexts/nodemon.json deleted file mode 100644 index 43fb9eab5..000000000 --- a/packages/create/templates/hello-contexts/nodemon.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "watch": ["src/","migrations/"], - "ext": "ts,json", - "ignore": ["src/**/*.test.ts"], - "exec": "npm run build && npx dbos migrate && npm run start" -} - diff --git a/packages/create/templates/hello-contexts/src/operations.test.ts b/packages/create/templates/hello-contexts/src/operations.test.ts deleted file mode 100644 index 36823f92b..000000000 --- a/packages/create/templates/hello-contexts/src/operations.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { TestingRuntime, createTestingRuntime } from "@dbos-inc/dbos-sdk"; -import { Hello, dbos_hello } from "./operations"; -import request from "supertest"; - -describe("operations-test", () => { - let testRuntime: TestingRuntime; - - beforeAll(async () => { - testRuntime = await createTestingRuntime(); - }); - - afterAll(async () => { - await testRuntime.destroy(); - }); - - /** - * Test the transaction. - */ - test("test-transaction", async () => { - const res = await testRuntime.invoke(Hello).helloTransaction("dbos"); - expect(res).toMatch("Hello, dbos! You have been greeted"); - - // Check the greet count. - const rows = await testRuntime.queryUserDB("SELECT * FROM dbos_hello WHERE name=$1", "dbos"); - expect(rows[0].greet_count).toBe(1); - }); - - /** - * Test the HTTP endpoint. - */ - test("test-endpoint", async () => { - const res = await request(testRuntime.getHandlersCallback()).get( - "/greeting/dbos" - ); - expect(res.statusCode).toBe(200); - expect(res.text).toMatch("Hello, dbos! You have been greeted"); - }); -}); diff --git a/packages/create/templates/hello-contexts/src/operations.ts b/packages/create/templates/hello-contexts/src/operations.ts deleted file mode 100644 index 5abad379a..000000000 --- a/packages/create/templates/hello-contexts/src/operations.ts +++ /dev/null @@ -1,66 +0,0 @@ -// Welcome to DBOS! - -// This is a sample "Hello" app built with DBOS. -// It greets visitors and keeps track of how many times each visitor has been greeted. -// To run this app, visit our Quickstart: https://docs.dbos.dev/getting-started/quickstart - -import { HandlerContext, TransactionContext, Transaction, GetApi, ArgSource, ArgSources } from "@dbos-inc/dbos-sdk"; -import { Knex } from "knex"; - -// The schema of the database table used in this example. -export interface dbos_hello { - name: string; - greet_count: number; -} - -export class Hello { - // Serve this function from HTTP GET requests at the /greeting endpoint with 'user' as a path parameter - // The @Transaction() decorator ensures that this function runs as a database transaction. - @GetApi("/greeting/:user") - @Transaction() // Run this function as a database transaction - static async helloTransaction(ctxt: TransactionContext, @ArgSource(ArgSources.URL) user: string) { - // Retrieve and increment the number of times this user has been greeted. - const query = "INSERT INTO dbos_hello (name, greet_count) VALUES (?, 1) ON CONFLICT (name) DO UPDATE SET greet_count = dbos_hello.greet_count + 1 RETURNING greet_count;"; - const { rows } = await ctxt.client.raw(query, [user]) as { rows: dbos_hello[] }; - const greet_count = rows[0].greet_count; - const greeting = `Hello, ${user}! You have been greeted ${greet_count} times.`; - return Hello.makeHTML(greeting); - } - - // Let's declare helper functions to serve static HTML - - // Serve a quick readme for the app at the / endpoint - @GetApi('/') - static async readme(_ctxt: HandlerContext) { - const message = Hello.makeHTML( - `Visit the route /greeting/{name} to be greeted!
- For example, visit /greeting/Mike
- The counter increments with each page visit.` - ); - return Promise.resolve(message); - } - - // A helper function to create HTML pages with some styling - static makeHTML(message: string) { - const page = ` - - - - DBOS Template App - - - -

Welcome to DBOS!

-

` + message + `

-

- To learn how to run this app yourself, visit our - Quickstart. -

- Then, to learn how to build crashproof apps, continue to our - Programming Guide.
-

- - `; - return page; - } -} diff --git a/packages/create/templates/hello-drizzle/.vscode/extensions.json b/packages/create/templates/hello-drizzle/.vscode/extensions.json deleted file mode 100644 index e4e81f058..000000000 --- a/packages/create/templates/hello-drizzle/.vscode/extensions.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - // Automatically recommend the DBOS extension to VSCode users. - // Documentation on extensions.json: http://go.microsoft.com/fwlink/?LinkId=827846 - "recommendations": [ - "dbos-inc.dbos-ttdbg" - ] -} \ No newline at end of file diff --git a/packages/create/templates/hello-drizzle/.vscode/launch.json b/packages/create/templates/hello-drizzle/.vscode/launch.json deleted file mode 100644 index 3977fca42..000000000 --- a/packages/create/templates/hello-drizzle/.vscode/launch.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - // Automatically configure the VSCode debugger for DBOS projects. - // Documentation on launch.json: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node-terminal", - "request": "launch", - "name": "Local Debug", - "command": "npx dbos start", - "preLaunchTask": "npm: build", - }, - { - "type": "node-terminal", - "request": "launch", - "name": "Time Travel Debug", - "command": "npx dbos debug -x ${command:dbos-ttdbg.get-proxy-url} -u ${command:dbos-ttdbg.pick-workflow-id}", - "preLaunchTask": "npm: build" - } - ] - } \ No newline at end of file diff --git a/packages/create/templates/hello-drizzle/.vscode/tasks.json b/packages/create/templates/hello-drizzle/.vscode/tasks.json deleted file mode 100644 index 793379743..000000000 --- a/packages/create/templates/hello-drizzle/.vscode/tasks.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - // Configure the build command for DBOS projects using VSCode. - // Documentation on tasks.json: https://code.visualstudio.com/docs/editor/tasks - "version": "2.0.0", - "tasks": [ - { - "label": "npm: build", - "type": "npm", - "script": "build", - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": [ - "$tsc" - ] - } - ] - } \ No newline at end of file diff --git a/packages/create/templates/hello-drizzle/README.md b/packages/create/templates/hello-drizzle/README.md deleted file mode 100644 index 04c3203d0..000000000 --- a/packages/create/templates/hello-drizzle/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# DBOS Hello with Drizzle - -This is a [DBOS app](https://docs.dbos.dev/) bootstrapped with `npx @dbos-inc/create` and using [Drizzle](https://docs.dbos.dev/typescript/tutorials/orms/using-drizzle). - -## Getting Started - -Before you can launch your app, you need a database. -DBOS works with any Postgres database, but to make things easier, we've provided a script that starts a Docker Postgres container and creates a database. -Run: - -```bash -node start_postgres_docker.js -``` - -If successful, the script should print `Database started successfully!`. - -Next, build the app: - -```bash -npm run build -``` - -Then, run a schema migration to create some tables: - -```bash -npx dbos migrate -``` - -If successful, the migration should print `Migration successful!`. - -Finally, run the app: - -```bash -npx dbos start -``` - -To see that it's working, visit this URL in your browser: [`http://localhost:3000/greeting/dbos`](http://localhost:3000/greeting/dbos). -You should get this message: `Hello, dbos! You have been greeted 1 times.` -Each time you refresh the page, the counter should go up by one! - -Congratulations! You just launched a DBOS application. - -## Next Steps - -- To add more functionality to this application, modify `src/operations.ts`, then rebuild and restart it. Alternatively, `npm run dev` uses `nodemon` to automatically rebuild and restart the app when source files change, using instructions specified in `nodemon.json`. -- For a detailed tutorial, check out our [programming quickstart](https://docs.dbos.dev/typescript/programming-guide). -- To learn how to deploy your application to DBOS Cloud, visit our [cloud quickstart](https://docs.dbos.dev/quickstart) -- To learn more about DBOS, take a look at [our documentation](https://docs.dbos.dev/) or our [source code](https://github.com/dbos-inc). diff --git a/packages/create/templates/hello-drizzle/nodemon.json b/packages/create/templates/hello-drizzle/nodemon.json deleted file mode 100644 index 43fb9eab5..000000000 --- a/packages/create/templates/hello-drizzle/nodemon.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "watch": ["src/","migrations/"], - "ext": "ts,json", - "ignore": ["src/**/*.test.ts"], - "exec": "npm run build && npx dbos migrate && npm run start" -} - diff --git a/packages/create/templates/hello-express/README.md b/packages/create/templates/hello-express/README.md deleted file mode 100644 index 49716be48..000000000 --- a/packages/create/templates/hello-express/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# DBOS Hello - -This is a [DBOS app](https://docs.dbos.dev/) bootstrapped with `npx @dbos-inc/create`, using [Express.js](https://expressjs.com/) and [Knex](https://docs.dbos.dev/typescript/tutorials/orms/using-knex) to interact with postgres. - -## Getting Started - -Before you can launch your app, you need a database. -DBOS works with any Postgres database, but to make things easier, we've provided a script that starts a Docker Postgres container and creates a database. -Run: - -```bash -node start_postgres_docker.js -``` - -If successful, the script should print `Database started successfully!`. - -Next, you can build and run the app in one step under `nodemon`: - -```bash -npm run dev -``` - -To see that it's working, visit this URL in your browser: [`http://localhost:3000/greeting/dbos`](http://localhost:3000/greeting/dbos). -You should get this message: `Hello, dbos! You have been greeted 1 times.` -Each time you refresh the page, the counter should go up by one! - -Congratulations! You just launched a DBOS application. - -## Production build - -In production, instead of using `nodemon`, the following separate steps should be used to build, run database setup, and start the app. - -```bash -npm run build -``` - -Then, run a schema migration to create some tables: - -```bash -npx dbos migrate -``` - -If successful, the migration should print `Migration successful!`. - -Finally, run the app: - -```bash -npx dbos start -``` - -## The application - -In `src/main.ts`, the Express app object is created and configured to serve an "hello world" DBOS workflow on `/greetings/:user`. This file also hosts the code of said DBOS workflow: an `Hello` class with a single `helloTransaction` method. - - -Then the `main()` function declares the code to start a DBOS instance and an Express application. When you pass the Express app object as parameter to `DBOS.launch()`, DBOS will wrap all routes with an [OpenTelemetry](https://opentelemetry.io/) tracing middleware and tie HTTP traces to DBOS workflow traces. - -To add more functionality to this application, modify `src/main.ts`. If you used `npm run dev`, it will automatically rebuild and restart. - -## Running in DBOS Cloud - -To deploy this app to DBOS Cloud, first install the DBOS Cloud CLI (example with [npm](https://www.npmjs.com/)): - -```shell -npm i -g @dbos-inc/dbos-cloud -``` - -Then, run this command to deploy your app: - -```shell -dbos-cloud app deploy -``` - -## Next Steps - -- For a detailed tutorial, check out our [programming quickstart](https://docs.dbos.dev/typescript/programming-guide). -- To learn more about DBOS, take a look at [our documentation](https://docs.dbos.dev/) or our [source code](https://github.com/dbos-inc). diff --git a/packages/create/templates/hello-express/knexfile.js b/packages/create/templates/hello-express/knexfile.js deleted file mode 100644 index c315c00ad..000000000 --- a/packages/create/templates/hello-express/knexfile.js +++ /dev/null @@ -1,20 +0,0 @@ -const { parseConfigFile } = require('@dbos-inc/dbos-sdk'); - -const [dbosConfig, ] = parseConfigFile(); - -const config = { - client: 'pg', - connection: { - host: dbosConfig.poolConfig.host, - port: dbosConfig.poolConfig.port, - user: dbosConfig.poolConfig.user, - password: dbosConfig.poolConfig.password, - database: dbosConfig.poolConfig.database, - ssl: dbosConfig.poolConfig.ssl, - }, - migrations: { - directory: './migrations' - } -}; - -module.exports = config; diff --git a/packages/create/templates/hello-express/migrations/20240212161006_create_dbos_hello_tables.js b/packages/create/templates/hello-express/migrations/20240212161006_create_dbos_hello_tables.js deleted file mode 100644 index 2a1117be2..000000000 --- a/packages/create/templates/hello-express/migrations/20240212161006_create_dbos_hello_tables.js +++ /dev/null @@ -1,18 +0,0 @@ -const { Knex } = require("knex"); - -exports.up = async function(knex) { - await knex.schema.createTable('dbos_hello', table => { - table.text('name').primary(); - table.integer('greet_count').defaultTo(0); - }); - - return knex.schema.createTable('dbos_greetings', table => { - table.text('greeting_name'); - table.text('greeting_note_content'); - }); -}; - -exports.down = async function(knex) { - await knex.schema.dropTable('dbos_greetings'); - return knex.schema.dropTable('dbos_hello'); -}; diff --git a/packages/create/templates/hello-express/nodemon.json b/packages/create/templates/hello-express/nodemon.json deleted file mode 100644 index a5a7faa72..000000000 --- a/packages/create/templates/hello-express/nodemon.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "watch": ["src/","migrations/"], - "ext": "ts,json", - "ignore": ["src/**/*.test.ts"], - "exec": "npm run build && npx dbos migrate && node dist/main.js" -} - diff --git a/packages/create/templates/hello-express/package.json b/packages/create/templates/hello-express/package.json deleted file mode 100644 index 9384ad389..000000000 --- a/packages/create/templates/hello-express/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "dbos-hello-express", - "version": "0.0.1", - "scripts": { - "build": "tsc", - "test": "npx dbos rollback && npx dbos migrate && jest", - "dev": "nodemon", - "start": "npx dbos start" - }, - "devDependencies": { - "@types/jest": "^29.5.12", - "@types/supertest": "^2.0.16", - "jest": "^29.7.0", - "nodemon": "^3.1.0", - "supertest": "^7.0.0", - "ts-jest": "^29.1.2", - "typescript": "^5.4.5" - }, - "dependencies": { - "@dbos-inc/dbos-sdk": "file:../../../..", - "express": "^4.21.1", - "knex": "3.1.0" - } -} diff --git a/packages/create/templates/hello-express/src/main.ts b/packages/create/templates/hello-express/src/main.ts deleted file mode 100644 index 7872a881f..000000000 --- a/packages/create/templates/hello-express/src/main.ts +++ /dev/null @@ -1,99 +0,0 @@ -// Welcome to DBOS! - -// This is a sample "Hello" app built with DBOS, Express.js, and Knex. -// It greets visitors and keeps track of how many times each visitor has been greeted. - -// First let's import express and DBOS -import express from "express"; -import { DBOS } from "@dbos-inc/dbos-sdk"; - -// Then, let's declare a type representing the "dbos_hello" database table -export interface dbos_hello { - name: string; - greet_count: number; -} - -// Now let's define a class with some static functions. -// DBOS uses TypeScript decorators to automatically make your functions reliable, so they need to be static. -export class Hello { - // This function greets a user and increments the greet count in the database. - // The @DBOS.transaction() decorator ensures that this function runs as a database transaction. - @DBOS.transaction() - static async helloTransaction(user: string) { - const query = "INSERT INTO dbos_hello (name, greet_count) VALUES (?, 1) ON CONFLICT (name) DO UPDATE SET greet_count = dbos_hello.greet_count + 1 RETURNING greet_count;"; - const { rows } = (await DBOS.knexClient.raw(query, [user])) as { rows: dbos_hello[] }; - const greet_count = rows[0].greet_count; - const greeting = `Hello, ${user}! You have been greeted ${greet_count} times.`; - return Hello.makeHTML(greeting); - } - - // Finally, we will declare helper functions to serve static HTML to user.s - - static async readme() { - const message = Hello.makeHTML( - `Visit the route /greeting/{name} to be greeted!
- For example, visit /greeting/Mike
- The counter increments with each page visit.` - ); - return Promise.resolve(message); - } - - // A helper function to create HTML pages with some styling - static makeHTML(message: string) { - const page = ` - - - - DBOS Template App - - - -

Welcome to DBOS!

-

` + message + `

-

- To learn how to run this app yourself, visit our - Quickstart. -

- Then, to learn how to build crashproof apps, continue to our - Programming Guide.
-

- - `; - return page; - } -} - -// Now, let's create an Express app and define some routes. -export const app = express(); -// Parse JSON payloads and make it available to req.body -app.use(express.json()); - -// We'll serve the README at the root of the app -app.get("/", async (_, res) => { - res.send(await Hello.readme()); -}); - -// Serve this function from HTTP GET requests at the /greeting endpoint with 'user' as a path parameter -// The handler will in turn call a reliable DBOS operation (helloTransaction) to greet the user -app.get("/greeting/:user", async (req, res) => { - const { user } = req.params; - res.send(await Hello.helloTransaction(user)); -}); - -// Finally, let's start the server -async function main() { - await DBOS.launch({expressApp: app}); - - const PORT = DBOS.runtimeConfig?.port ?? 3000; - const ENV = process.env.NODE_ENV || 'development'; - - app.listen(PORT, () => { - console.log(`🚀 Server is running on http://localhost:${PORT}`); - console.log(`🌟 Environment: ${ENV}`); - }); -} - -// Only start the server when this file is run directly from Node -if (require.main === module) { - main().catch(console.log); -} diff --git a/packages/create/templates/hello-express/tsconfig.json b/packages/create/templates/hello-express/tsconfig.json deleted file mode 100644 index 5488d6185..000000000 --- a/packages/create/templates/hello-express/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -/* Visit https://aka.ms/tsconfig to read more about this file */ -{ - "compilerOptions": { - "target": "esnext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - "module": "Node16", /* Specify what module code is generated. */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - "declarationMap": true, /* Create sourcemaps for d.ts files. */ - "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ - "newLine": "lf", /* Set the newline character for emitting files. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - "strict": true, /* Enable all strict type-checking options. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - }, - "include": [ /* Specifies an array of filenames or patterns to include in the program. */ - "src" - ], - "exclude": [ - "**/*.test.ts", - "dist" - ] -} diff --git a/packages/create/templates/hello-nextjs/.gitignore b/packages/create/templates/hello-nextjs/.gitignore deleted file mode 100644 index 5ef6a5207..000000000 --- a/packages/create/templates/hello-nextjs/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/packages/create/templates/hello-nextjs/README.md b/packages/create/templates/hello-nextjs/README.md deleted file mode 100644 index 836031e62..000000000 --- a/packages/create/templates/hello-nextjs/README.md +++ /dev/null @@ -1,100 +0,0 @@ -# DBOS Background Job - -This is a [DBOS app](https://docs.dbos.dev/) bootstrapped with `npx @dbos-inc/create`, using [Express.js](https://expressjs.com/) and [Knex](https://docs.dbos.dev/typescript/tutorials/orms/using-knex) to interact with postgres. - -## Getting Started - -Before you can launch your app, you need a database. -DBOS works with any Postgres database, but to make things easier, we've provided a script that starts a Docker Postgres container and creates a database. -Run: - -```bash -node start_postgres_docker.js -``` - -If successful, the script should print `Database started successfully!`. - -``` - -## Production build - -In production, instead of using `nodemon`, the following separate steps should be used to build, run database setup, and start the app. - -```bash -npm run build -``` - -Then, run a schema migration to create some tables: - -```bash -npx dbos migrate -``` - -If successful, the migration should print `Migration successful!`. - -Finally, run the app: - -```bash -npm run start -``` - -To see that it's working, visit this URL in your browser: [`http://localhost:3000/`](http://localhost:3000/). - -Click on the "Start Background Job" button. - -You should this message: `Your background task has completed step 0 of 9.` -As the background job completes steps, the step counter should go up by one! - -Click on "Crash the application" button. - -The step counter stops increasing. -On the command line, you will see that the application has stopped. -Start the application again. -```bash -npm run start -``` -In the browser, you will see that the execution of steps resumes where it left off and eventually completes. - -Congratulations! You just run a DBOS application. - -## The application - -- In `src/action/dbosWorkflow.tsx`, the DBOS workflow is created. When called, it increments a counter in the database and returns a greeting. - -- The code below in the same file, launches the dbos runtime. This is required for DBOS runtime to start. Do not remove the code -``` -if (process.env.NEXT_PHASE !== "phase-production-build") { - await DBOS.launch(); -} -``` -- The workflow for the background job is called by the called the GET method in app/tasks/route.ts. - -- The GET is called by the component in src/components/BackGroundTask.tsx. It calls the route /tasks. - -- The component is called from the main UI page.tsx. - -- This is how the DBOS workflow is wired to the NextJs application. - - - DBOS will wrap all routes with an [OpenTelemetry](https://opentelemetry.io/) tracing middleware and tie HTTP traces to DBOS workflow traces. - -To add more functionality to this application, modify `src/operations.ts`. If you used `npm run dev`, it will automatically rebuild and restart. - -## Running in DBOS Cloud - -To deploy this app to DBOS Cloud, first install the DBOS Cloud CLI (example with [npm](https://www.npmjs.com/)): - -```shell -npm i -g @dbos-inc/dbos-cloud -``` - -Then, run this command to deploy your app: - -```shell -dbos-cloud app deploy -``` - -## Next Steps - -- For a detailed tutorial, check out our [programming quickstart](https://docs.dbos.dev/typescript/programming-guide). -- To learn more about DBOS, take a look at [our documentation](https://docs.dbos.dev/) or our [source code](https://github.com/dbos-inc). \ No newline at end of file diff --git a/packages/create/templates/hello-nextjs/dbos-config.yaml b/packages/create/templates/hello-nextjs/dbos-config.yaml deleted file mode 100644 index 2e2640530..000000000 --- a/packages/create/templates/hello-nextjs/dbos-config.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# To enable auto-completion and validation for this file in VSCode, install the RedHat YAML extension -# https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml - -# yaml-language-server: $schema=https://raw.githubusercontent.com/dbos-inc/dbos-transact/main/dbos-config.schema.json - -language: node -database: - hostname: localhost - port: 5432 - username: postgres - password: ${PGPASSWORD} - connectionTimeoutMillis: 3000 - app_db_client: knex - migrate: - - npx knex migrate:latest -runtimeConfig: - start: - - "npm run start" diff --git a/packages/create/templates/hello-nextjs/eslint.config.mjs b/packages/create/templates/hello-nextjs/eslint.config.mjs deleted file mode 100644 index c85fb67c4..000000000 --- a/packages/create/templates/hello-nextjs/eslint.config.mjs +++ /dev/null @@ -1,16 +0,0 @@ -import { dirname } from "path"; -import { fileURLToPath } from "url"; -import { FlatCompat } from "@eslint/eslintrc"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -const compat = new FlatCompat({ - baseDirectory: __dirname, -}); - -const eslintConfig = [ - ...compat.extends("next/core-web-vitals", "next/typescript"), -]; - -export default eslintConfig; diff --git a/packages/create/templates/hello-nextjs/gitignore.template b/packages/create/templates/hello-nextjs/gitignore.template deleted file mode 100644 index 5ef6a5207..000000000 --- a/packages/create/templates/hello-nextjs/gitignore.template +++ /dev/null @@ -1,41 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/packages/create/templates/hello-nextjs/knexfile.js b/packages/create/templates/hello-nextjs/knexfile.js deleted file mode 100644 index c315c00ad..000000000 --- a/packages/create/templates/hello-nextjs/knexfile.js +++ /dev/null @@ -1,20 +0,0 @@ -const { parseConfigFile } = require('@dbos-inc/dbos-sdk'); - -const [dbosConfig, ] = parseConfigFile(); - -const config = { - client: 'pg', - connection: { - host: dbosConfig.poolConfig.host, - port: dbosConfig.poolConfig.port, - user: dbosConfig.poolConfig.user, - password: dbosConfig.poolConfig.password, - database: dbosConfig.poolConfig.database, - ssl: dbosConfig.poolConfig.ssl, - }, - migrations: { - directory: './migrations' - } -}; - -module.exports = config; diff --git a/packages/create/templates/hello-nextjs/migrations/20240212161006_create_dbos_hello_tables.js b/packages/create/templates/hello-nextjs/migrations/20240212161006_create_dbos_hello_tables.js deleted file mode 100644 index 2a1117be2..000000000 --- a/packages/create/templates/hello-nextjs/migrations/20240212161006_create_dbos_hello_tables.js +++ /dev/null @@ -1,18 +0,0 @@ -const { Knex } = require("knex"); - -exports.up = async function(knex) { - await knex.schema.createTable('dbos_hello', table => { - table.text('name').primary(); - table.integer('greet_count').defaultTo(0); - }); - - return knex.schema.createTable('dbos_greetings', table => { - table.text('greeting_name'); - table.text('greeting_note_content'); - }); -}; - -exports.down = async function(knex) { - await knex.schema.dropTable('dbos_greetings'); - return knex.schema.dropTable('dbos_hello'); -}; diff --git a/packages/create/templates/hello-nextjs/next.config.ts b/packages/create/templates/hello-nextjs/next.config.ts deleted file mode 100644 index f356255f5..000000000 --- a/packages/create/templates/hello-nextjs/next.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - /* config options here */ - webpack: (config) => { - // Treat @dbos-inc/dbos-sdk as an external package for client builds - config.externals = [ - ...config.externals, - { - "@dbos-inc/dbos-sdk": "commonjs @dbos-inc/dbos-sdk", - }, - ]; - - return config; - }, -}; - -export default nextConfig; diff --git a/packages/create/templates/hello-nextjs/package.json b/packages/create/templates/hello-nextjs/package.json deleted file mode 100644 index 732d9b0ff..000000000 --- a/packages/create/templates/hello-nextjs/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "hello-nextjs", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint" - }, - "dependencies": { - "@dbos-inc/dbos-sdk": "^1.31.17-preview", - "next": "15.0.3", - "react": "19.0.0-rc-66855b96-20241106", - "react-dom": "19.0.0-rc-66855b96-20241106", - "typeorm": "^0.3.20", - "webpack": "^5.96.1" - }, - "devDependencies": { - "@babel/cli": "^7.25.9", - "@babel/core": "^7.26.0", - "@babel/preset-env": "^7.26.0", - "@types/node": "^20", - "@types/react": "^18", - "@types/react-dom": "^18", - "eslint": "^8", - "eslint-config-next": "15.0.3", - "postcss": "^8", - "tailwindcss": "^3.4.1", - "typescript": "^5" - } -} diff --git a/packages/create/templates/hello-nextjs/postcss.config.mjs b/packages/create/templates/hello-nextjs/postcss.config.mjs deleted file mode 100644 index 1a69fd2a4..000000000 --- a/packages/create/templates/hello-nextjs/postcss.config.mjs +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('postcss-load-config').Config} */ -const config = { - plugins: { - tailwindcss: {}, - }, -}; - -export default config; diff --git a/packages/create/templates/hello-nextjs/public/file.svg b/packages/create/templates/hello-nextjs/public/file.svg deleted file mode 100644 index 004145cdd..000000000 --- a/packages/create/templates/hello-nextjs/public/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/create/templates/hello-nextjs/public/globe.svg b/packages/create/templates/hello-nextjs/public/globe.svg deleted file mode 100644 index 567f17b0d..000000000 --- a/packages/create/templates/hello-nextjs/public/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/create/templates/hello-nextjs/public/next.svg b/packages/create/templates/hello-nextjs/public/next.svg deleted file mode 100644 index 5174b28c5..000000000 --- a/packages/create/templates/hello-nextjs/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/create/templates/hello-nextjs/public/vercel.svg b/packages/create/templates/hello-nextjs/public/vercel.svg deleted file mode 100644 index 770539603..000000000 --- a/packages/create/templates/hello-nextjs/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/create/templates/hello-nextjs/public/window.svg b/packages/create/templates/hello-nextjs/public/window.svg deleted file mode 100644 index b2b2a44f6..000000000 --- a/packages/create/templates/hello-nextjs/public/window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/create/templates/hello-nextjs/src/actions/dbosWorkflow.ts b/packages/create/templates/hello-nextjs/src/actions/dbosWorkflow.ts deleted file mode 100644 index 875f3150b..000000000 --- a/packages/create/templates/hello-nextjs/src/actions/dbosWorkflow.ts +++ /dev/null @@ -1,12 +0,0 @@ -"use server"; - -import { DBOS } from "@dbos-inc/dbos-sdk"; -import { dbosWorkflowClass } from "../dbos/operations"; - -console.log("Hello from dbosWorkflow.ts"); - - -export async function dbosBackgroundTask(workflowID: string) { - DBOS.logger.info("Hello from DBOS!"); - return DBOS.startWorkflow(dbosWorkflowClass, {workflowID: workflowID}).backgroundTask(10); -} \ No newline at end of file diff --git a/packages/create/templates/hello-nextjs/src/actions/hello.ts b/packages/create/templates/hello-nextjs/src/actions/hello.ts deleted file mode 100644 index bf90da0b7..000000000 --- a/packages/create/templates/hello-nextjs/src/actions/hello.ts +++ /dev/null @@ -1,15 +0,0 @@ -"use server"; - -import { DBOS } from "@dbos-inc/dbos-sdk"; -import { helloWorkflowClass } from "../dbos/operations"; - - -console.log("Hello from hello.ts"); - -// The exported function is the entry point for the workflow -// The function is exported and not the class because Next does not support exporting classes -export async function helloWorkflow(userName: string) { - DBOS.logger.info("Hello from DBOS!"); - return await helloWorkflowClass.helloDBOS(userName); -} - diff --git a/packages/create/templates/hello-nextjs/src/app/crash/route.ts b/packages/create/templates/hello-nextjs/src/app/crash/route.ts deleted file mode 100644 index b12664074..000000000 --- a/packages/create/templates/hello-nextjs/src/app/crash/route.ts +++ /dev/null @@ -1,8 +0,0 @@ - -export async function GET(request: Request) { - - console.log("Received request Crashing the app"); - - process.exit(1); - -} \ No newline at end of file diff --git a/packages/create/templates/hello-nextjs/src/app/favicon.ico b/packages/create/templates/hello-nextjs/src/app/favicon.ico deleted file mode 100644 index 718d6fea4..000000000 Binary files a/packages/create/templates/hello-nextjs/src/app/favicon.ico and /dev/null differ diff --git a/packages/create/templates/hello-nextjs/src/app/globals.css b/packages/create/templates/hello-nextjs/src/app/globals.css deleted file mode 100644 index 6b717ad34..000000000 --- a/packages/create/templates/hello-nextjs/src/app/globals.css +++ /dev/null @@ -1,21 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -:root { - --background: #ffffff; - --foreground: #171717; -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - -body { - color: var(--foreground); - background: var(--background); - font-family: Arial, Helvetica, sans-serif; -} diff --git a/packages/create/templates/hello-nextjs/src/app/greetings/route.ts b/packages/create/templates/hello-nextjs/src/app/greetings/route.ts deleted file mode 100644 index 7a074b6d9..000000000 --- a/packages/create/templates/hello-nextjs/src/app/greetings/route.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NextResponse } from "next/server"; -import { helloWorkflow } from "../../actions/hello"; -import { DBOS } from "@dbos-inc/dbos-sdk"; - -export async function POST(request: Request) { - const body = await request.json(); - const { userName } = body; - DBOS.logger.info(`Received request with name: ${userName}`); - const response = await helloWorkflow(userName); - return NextResponse.json(response); -} diff --git a/packages/create/templates/hello-nextjs/src/app/layout.tsx b/packages/create/templates/hello-nextjs/src/app/layout.tsx deleted file mode 100644 index a9188fbcb..000000000 --- a/packages/create/templates/hello-nextjs/src/app/layout.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; - -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); - -export const metadata: Metadata = { - title: "Create Next App with DBOS", - description: "Generated by DBOS", -}; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - {children} - - - ); -} diff --git a/packages/create/templates/hello-nextjs/src/app/page.tsx b/packages/create/templates/hello-nextjs/src/app/page.tsx deleted file mode 100644 index c7a21c822..000000000 --- a/packages/create/templates/hello-nextjs/src/app/page.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import Image from "next/image"; -import BackGroundTask from "@/components/client/BackGroundTask"; -import CallDBOSWorkflow from "@/components/client/callDBOSWorkflow"; - -export default function Home() { - return ( -
-
- - -

Welcome to DBOS!

- - -

- DBOS helps you build applications that are resilient to any failure—no matter how many times you crash this app, your background task will always recover from its last completed step in about ten seconds. -

-
- -
-
- -
-
- -
- ); -} diff --git a/packages/create/templates/hello-nextjs/src/app/step/[slug]/route.ts b/packages/create/templates/hello-nextjs/src/app/step/[slug]/route.ts deleted file mode 100644 index 0f7510279..000000000 --- a/packages/create/templates/hello-nextjs/src/app/step/[slug]/route.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { NextResponse } from "next/server"; -import { DBOS } from "@dbos-inc/dbos-sdk"; - -export async function GET(request: Request, { params }: { params: Promise<{ slug: string }> }) { - - const taskId = (await params).slug; - DBOS.logger.info(`Received request to check on taskId: ${taskId}`); - - let step = await DBOS.getEvent(taskId, "steps_event"); - - DBOS.logger.info(`For taskId: ${taskId} we are done with ${step} steps`); - - return NextResponse.json({ "stepsCompleted": step}); - -} \ No newline at end of file diff --git a/packages/create/templates/hello-nextjs/src/app/tasks/[slug]/route.ts b/packages/create/templates/hello-nextjs/src/app/tasks/[slug]/route.ts deleted file mode 100644 index 0b53c64c3..000000000 --- a/packages/create/templates/hello-nextjs/src/app/tasks/[slug]/route.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { NextResponse } from "next/server"; -import { DBOS } from "@dbos-inc/dbos-sdk"; -import { dbosBackgroundTask } from "@/actions/dbosWorkflow"; - -export async function GET(request: Request, { params }: { params: Promise<{ slug: string }> }) { - - const taskId = (await params).slug; - - DBOS.logger.info(`Received request to start background task taskId: ${taskId}`); - - DBOS.logger.info(`Started background task taskId: ${taskId}`); - - await dbosBackgroundTask(taskId) - - return NextResponse.json({ message: "Background task started" }); - -} - diff --git a/packages/create/templates/hello-nextjs/src/components/client/BackGroundTask.tsx b/packages/create/templates/hello-nextjs/src/components/client/BackGroundTask.tsx deleted file mode 100644 index 2307bb31d..000000000 --- a/packages/create/templates/hello-nextjs/src/components/client/BackGroundTask.tsx +++ /dev/null @@ -1,179 +0,0 @@ -"use client"; - -import { useState, useEffect } from "react"; -import { useRouter, useSearchParams } from "next/navigation"; -import { Suspense } from 'react' - -let intervalInitialized = false; - -function generateRandomString(): string { - const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - const array = new Uint8Array(6); - crypto.getRandomValues(array); // Fills the array with cryptographically random values - - return Array.from(array) - .map(x => chars[x % chars.length]) - .join(''); -} - -function BackGroundTask() { - const [isRunning, setIsRunning] = useState(false); - const [currentStep, setCurrentStep] = useState(0); - const [taskId, setTaskid] = useState(""); - const [isReconnecting, setIsReconnecting] = useState(false); - - const router = useRouter(); - const searchParams = useSearchParams(); - - const startBackgroundJob = async () => { - setIsRunning(true); - - setCurrentStep(0); - - let task = taskId - if (taskId === "") { - task = generateRandomString(); - setTaskid(task); - updateQueryParam("id", task); - } - - // start the background job - try { - await fetch(`/tasks/${task}`, { method: "GET" }); - } catch (error) { - console.error("Failed to start job", error); - setIsRunning(false); - } - }; - - const crashApp = async () => { - - if(!isRunning) { - console.log("Not running, nothing to crash"); - return; - } - - setIsRunning(false); - - console.log("Crashing the application"); - - // stop the background job - try { - await fetch("/crash", { method: "GET" }); - } catch (error) { - console.error("Failed to start job", error); - - } - - }; - - // Update the URL query parameter - const updateQueryParam = (key: string, value: string) => { - const params = new URLSearchParams(searchParams.toString()); - params.set(key, value); - const newUrl = `${window.location.pathname}?${params.toString()}`; - router.replace(newUrl); - }; - - // Remove the `id` query parameter from the URL - const clearQueryParam = (key: string) => { - const params = new URLSearchParams(searchParams.toString()); - params.delete(key); - const newUrl = `${window.location.pathname}?${params.toString()}`; - router.replace(newUrl); - }; - - // fetch the current progress - const fetchProgress = async () => { - try { - - if (taskId === "") { - console.log("No task to monitor"); - return; - } - - const response = await fetch(`/step/${taskId}`, { method: "GET" }); - if (!response.ok) { - console.error("Failed to fetch job progress", response.statusText); - setIsReconnecting(true); - return; - } - setIsReconnecting(false); - - const data = await response.json(); - - console.log("Step completed", data.stepsCompleted); - - if (data.stepsCompleted) { - setIsRunning(true); - setCurrentStep(data.stepsCompleted); - - - if (data.stepsCompleted === 10) { - clearQueryParam("id"); - setIsRunning(false); - setTaskid(""); - setCurrentStep(0); - - } - } - } catch (error) { - console.error("Failed to fetch job progress", error); - setIsRunning(false); - setTaskid(""); - setIsReconnecting(true); - } - }; - - // Polling the progress every 2 seconds while the job is running - useEffect(() => { - const idFromUrl = searchParams.get("id"); - if (idFromUrl) { - setTaskid(idFromUrl); - setIsRunning(true); // Assume the job is already running if there's an ID - } - - if (!intervalInitialized) { - const interval = setInterval(fetchProgress, 2000); - intervalInitialized = true; - return () => { - clearInterval(interval); - intervalInitialized = false; - }; - } - }, [searchParams]); - - - return ( -
-
- - -
- -

- {currentStep < 10 - ? `Your background task has completed step ${currentStep} of 10.` - : "Background task completed successfully!"} -

-

- {isReconnecting ? "Reconnecting..." : ""} -

- - -
- ); - -} - -const WrappedBackgroundJobComponent = () => ( - Loading...

}> - -
- ); - -export default WrappedBackgroundJobComponent; diff --git a/packages/create/templates/hello-nextjs/src/components/client/callDBOSWorkflow.tsx b/packages/create/templates/hello-nextjs/src/components/client/callDBOSWorkflow.tsx deleted file mode 100644 index 3ccd581b7..000000000 --- a/packages/create/templates/hello-nextjs/src/components/client/callDBOSWorkflow.tsx +++ /dev/null @@ -1,31 +0,0 @@ -"use client"; - -import { useState } from "react"; - -export default function CallDBOSWorkflow( { wfResult }: { wfResult: string }) { - const [greetingsWfResult, setGreetingsWfResult] = useState(wfResult); - const [loading, setLoading] = useState(false); - - const greetUser = async () => { - setLoading(true); - try { - const response = await fetch("/greetings", { method: "POST", body: JSON.stringify({ userName: "dbosuser" }) }); - const data = await response.json(); - setGreetingsWfResult(data); - } catch (error) { - console.error("Error calling greetings workflow", error); - setGreetingsWfResult("Error calling greetings workflow: " + error); - } finally { - setLoading(false); - } - } - - return( -
- -

{greetingsWfResult}

-
- ); -} diff --git a/packages/create/templates/hello-nextjs/src/dbos/background.ts b/packages/create/templates/hello-nextjs/src/dbos/background.ts deleted file mode 100644 index 58fe70ac8..000000000 --- a/packages/create/templates/hello-nextjs/src/dbos/background.ts +++ /dev/null @@ -1,23 +0,0 @@ - -import { DBOS } from "@dbos-inc/dbos-sdk"; - -export class dbosWorkflowClass { - - @DBOS.transaction() - static async backgroundTaskStep(i : number) { - DBOS.logger.info(`Completed step ${i}`); - } - - @DBOS.workflow() - static async backgroundTask(i: number) { - DBOS.logger.info("Hello from background task!"); - for (let j = 1; j <= i; j++) { - await dbosWorkflowClass.backgroundTaskStep(j); - DBOS.logger.info("Sleeping for 2 seconds"); - await DBOS.sleepSeconds(2); - await DBOS.setEvent("steps_event", j) - } - DBOS.logger.info("Background task complete!"); - } - -} diff --git a/packages/create/templates/hello-nextjs/src/dbos/hello.ts b/packages/create/templates/hello-nextjs/src/dbos/hello.ts deleted file mode 100644 index 583c30bf6..000000000 --- a/packages/create/templates/hello-nextjs/src/dbos/hello.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { DBOS } from "@dbos-inc/dbos-sdk"; -import { Knex } from "knex"; - -export interface dbos_hello { - name: string; - greet_count: number; - } - -function getClient() {return DBOS.knexClient as Knex;} - -export class helloWorkflowClass { - - @DBOS.transaction() - static async helloDBOS(userName: string) { - DBOS.logger.info("Hello from DBOS Transaction!"); - // Retrieve and increment the number of times this user has been greeted. - const query = "INSERT INTO dbos_hello (name, greet_count) VALUES (?, 1) ON CONFLICT (name) DO UPDATE SET greet_count = dbos_hello.greet_count + 1 RETURNING greet_count;"; - const { rows } = await getClient().raw(query, [userName]) as { rows: dbos_hello[] }; - const greet_count = rows[0].greet_count; - const greeting = `Hello! You have been greeted ${greet_count} times.`; - return greeting; - } - -} \ No newline at end of file diff --git a/packages/create/templates/hello-nextjs/src/dbos/operations.ts b/packages/create/templates/hello-nextjs/src/dbos/operations.ts deleted file mode 100644 index b07b7a376..000000000 --- a/packages/create/templates/hello-nextjs/src/dbos/operations.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DBOS } from "@dbos-inc/dbos-sdk"; -export { dbosWorkflowClass } from "./background"; -export { helloWorkflowClass } from "./hello"; - -console.log("Hello from operations.ts"); - -if (process.env.NEXT_PHASE !== "phase-production-build") { - await DBOS.launch() -} \ No newline at end of file diff --git a/packages/create/templates/hello-nextjs/tailwind.config.ts b/packages/create/templates/hello-nextjs/tailwind.config.ts deleted file mode 100644 index 109807be0..000000000 --- a/packages/create/templates/hello-nextjs/tailwind.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Config } from "tailwindcss"; - -export default { - content: [ - "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", - "./src/components/**/*.{js,ts,jsx,tsx,mdx}", - "./src/app/**/*.{js,ts,jsx,tsx,mdx}", - ], - theme: { - extend: { - colors: { - background: "var(--background)", - foreground: "var(--foreground)", - }, - }, - }, - plugins: [], -} satisfies Config; diff --git a/packages/create/templates/hello-nextjs/tsconfig.json b/packages/create/templates/hello-nextjs/tsconfig.json deleted file mode 100644 index ba665454e..000000000 --- a/packages/create/templates/hello-nextjs/tsconfig.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "compilerOptions": { - "experimentalDecorators": true, - "emitDecoratorMetadata": true, - "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -} diff --git a/packages/create/templates/hello-prisma/.vscode/extensions.json b/packages/create/templates/hello-prisma/.vscode/extensions.json deleted file mode 100644 index e4e81f058..000000000 --- a/packages/create/templates/hello-prisma/.vscode/extensions.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - // Automatically recommend the DBOS extension to VSCode users. - // Documentation on extensions.json: http://go.microsoft.com/fwlink/?LinkId=827846 - "recommendations": [ - "dbos-inc.dbos-ttdbg" - ] -} \ No newline at end of file diff --git a/packages/create/templates/hello-prisma/.vscode/launch.json b/packages/create/templates/hello-prisma/.vscode/launch.json deleted file mode 100644 index 3977fca42..000000000 --- a/packages/create/templates/hello-prisma/.vscode/launch.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - // Automatically configure the VSCode debugger for DBOS projects. - // Documentation on launch.json: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node-terminal", - "request": "launch", - "name": "Local Debug", - "command": "npx dbos start", - "preLaunchTask": "npm: build", - }, - { - "type": "node-terminal", - "request": "launch", - "name": "Time Travel Debug", - "command": "npx dbos debug -x ${command:dbos-ttdbg.get-proxy-url} -u ${command:dbos-ttdbg.pick-workflow-id}", - "preLaunchTask": "npm: build" - } - ] - } \ No newline at end of file diff --git a/packages/create/templates/hello-prisma/.vscode/tasks.json b/packages/create/templates/hello-prisma/.vscode/tasks.json deleted file mode 100644 index 793379743..000000000 --- a/packages/create/templates/hello-prisma/.vscode/tasks.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - // Configure the build command for DBOS projects using VSCode. - // Documentation on tasks.json: https://code.visualstudio.com/docs/editor/tasks - "version": "2.0.0", - "tasks": [ - { - "label": "npm: build", - "type": "npm", - "script": "build", - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": [ - "$tsc" - ] - } - ] - } \ No newline at end of file diff --git a/packages/create/templates/hello-prisma/README.md b/packages/create/templates/hello-prisma/README.md deleted file mode 100644 index c9da5b572..000000000 --- a/packages/create/templates/hello-prisma/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# DBOS Hello with Prisma - -This is a [DBOS app](https://docs.dbos.dev/) bootstrapped with `npx @dbos-inc/create` and using [Prisma](https://docs.dbos.dev/typescript/tutorials/orms/using-prisma). - -## Getting Started - -Before you can launch your app, you need a database. -DBOS works with any Postgres database, but to make things easier, we've provided a script that starts a Docker Postgres container and creates a database. -Run: - -```bash -node start_postgres_docker.js -``` - -If successful, the script should print `Database started successfully!`. - -Next, build the app: - -```bash -npm run build -``` - -Then, run a schema migration to create some tables. -Prisma provides rich support for [schema migrations](https://www.prisma.io/docs/orm/prisma-migrate), including automatic generation of migration files from Prisma schema. -Fore more information, see [our docs](https://docs.dbos.dev/typescript/tutorials/orms/using-prisma). - -```bash -npx dbos migrate -``` - -If successful, the migration should print `Migration successful!`. - -Finally, run the app: - -```bash -npx dbos start -``` - -To see that it's working, visit this URL in your browser: [`http://localhost:3000/greeting/dbos`](http://localhost:3000/greeting/dbos). -You should get this message: `Hello, dbos! You have been greeted 1 times.` -Each time you refresh the page, the counter should go up by one! - -Congratulations! You just launched a DBOS application. - -## Next Steps - -- For more information on using Prisma with DBOS, check out [our docs](https://docs.dbos.dev/typescript/tutorials/orms/using-prisma). -- To add more functionality to this application, modify `src/operations.ts`, then rebuild and restart it. Alternatively, `npm run dev` uses `nodemon` to automatically rebuild and restart the app when source files change, using instructions specified in `nodemon.json`. -- For a detailed tutorial, check out our [programming quickstart](https://docs.dbos.dev/typescript/programming-guide). -- To learn how to deploy your application to DBOS Cloud, visit our [cloud quickstart](https://docs.dbos.dev/quickstart/) -- To learn more about DBOS, take a look at [our documentation](https://docs.dbos.dev/) or our [source code](https://github.com/dbos-inc). diff --git a/packages/create/templates/hello-prisma/start_postgres_docker.js b/packages/create/templates/hello-prisma/start_postgres_docker.js deleted file mode 100644 index 7660446a6..000000000 --- a/packages/create/templates/hello-prisma/start_postgres_docker.js +++ /dev/null @@ -1,40 +0,0 @@ -const { execSync } = require('child_process'); - -// Default PostgreSQL port -let port = '5432'; - -// Set the host PostgreSQL port with the -p/--port flag. -process.argv.forEach((val, index) => { - if (val === '-p' || val === '--port') { - if (process.argv[index + 1]) { - port = process.argv[index + 1]; - } - } -}); - -if (!process.env.PGPASSWORD) { - console.error("Error: PGPASSWORD is not set."); - process.exit(1); -} - -try { - execSync(`docker run --rm --name=dbos-db --env=POSTGRES_PASSWORD="${process.env.PGPASSWORD}" --env=PGDATA=/var/lib/postgresql/data --volume=/var/lib/postgresql/data -p ${port}:5432 -d sibedge/postgres-plv8`); - console.log("Waiting for PostgreSQL to start..."); - - let attempts = 30; - const checkDatabase = setInterval(() => { - try { - execSync('docker exec dbos-db psql -U postgres -c "SELECT 1;"', { stdio: 'ignore' }); - console.log("PostgreSQL started!"); - clearInterval(checkDatabase); - console.log("Database started successfully!"); - } catch (error) { - if (--attempts === 0) { - clearInterval(checkDatabase); - console.error("Failed to start PostgreSQL."); - } - } - }, 1000); -} catch (error) { - console.error("Error starting PostgreSQL in Docker:", error.message); -} diff --git a/packages/create/templates/hello-typeorm/.vscode/extensions.json b/packages/create/templates/hello-typeorm/.vscode/extensions.json deleted file mode 100644 index e4e81f058..000000000 --- a/packages/create/templates/hello-typeorm/.vscode/extensions.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - // Automatically recommend the DBOS extension to VSCode users. - // Documentation on extensions.json: http://go.microsoft.com/fwlink/?LinkId=827846 - "recommendations": [ - "dbos-inc.dbos-ttdbg" - ] -} \ No newline at end of file diff --git a/packages/create/templates/hello-typeorm/.vscode/launch.json b/packages/create/templates/hello-typeorm/.vscode/launch.json deleted file mode 100644 index 3977fca42..000000000 --- a/packages/create/templates/hello-typeorm/.vscode/launch.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - // Automatically configure the VSCode debugger for DBOS projects. - // Documentation on launch.json: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node-terminal", - "request": "launch", - "name": "Local Debug", - "command": "npx dbos start", - "preLaunchTask": "npm: build", - }, - { - "type": "node-terminal", - "request": "launch", - "name": "Time Travel Debug", - "command": "npx dbos debug -x ${command:dbos-ttdbg.get-proxy-url} -u ${command:dbos-ttdbg.pick-workflow-id}", - "preLaunchTask": "npm: build" - } - ] - } \ No newline at end of file diff --git a/packages/create/templates/hello-typeorm/.vscode/tasks.json b/packages/create/templates/hello-typeorm/.vscode/tasks.json deleted file mode 100644 index 793379743..000000000 --- a/packages/create/templates/hello-typeorm/.vscode/tasks.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - // Configure the build command for DBOS projects using VSCode. - // Documentation on tasks.json: https://code.visualstudio.com/docs/editor/tasks - "version": "2.0.0", - "tasks": [ - { - "label": "npm: build", - "type": "npm", - "script": "build", - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": [ - "$tsc" - ] - } - ] - } \ No newline at end of file diff --git a/packages/create/templates/hello-typeorm/README.md b/packages/create/templates/hello-typeorm/README.md deleted file mode 100644 index c0a01bcb0..000000000 --- a/packages/create/templates/hello-typeorm/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# DBOS Hello with TypeORM - -This is a [DBOS app](https://docs.dbos.dev/) bootstrapped with `npx @dbos-inc/create` and using [TypeORM](https://docs.dbos.dev/typescript/tutorials/orms/using-typeorm). - -## Getting Started - -Before you can launch your app, you need a database. -DBOS works with any Postgres database, but to make things easier, we've provided a script that starts a Docker Postgres container and creates a database. -Run: - -```bash -node start_postgres_docker.js -``` - -If successful, the script should print `Database started successfully!`. - -Next, build the app: - -```bash -npm run build -``` - -Then, run a schema migration to create some tables. -TypeORM provides rich support for [schema migrations](https://typeorm.io/migrations), including automatic generation of migration files from entity files. -Fore more information, see [our docs](https://docs.dbos.dev/typescript/tutorials/orms/using-typeorm). - -```bash -npx dbos migrate -``` - -If successful, the migration should print `Migration successful!`. - -Finally, run the app: - -```bash -npx dbos start -``` - -To see that it's working, visit this URL in your browser: [`http://localhost:3000/greeting/dbos`](http://localhost:3000/greeting/dbos). -You should get this message: `Hello, dbos! You have been greeted 1 times.` -Each time you refresh the page, the counter should go up by one! - -Congratulations! You just launched a DBOS application. - -## Next Steps - -- For more information on using TypeORM with DBOS, check out [our docs](https://docs.dbos.dev/typescript/tutorials/orms/using-typeorm). -- To add more functionality to this application, modify `src/operations.ts`, then rebuild and restart it. Alternatively, `npm run dev` uses `nodemon` to automatically rebuild and restart the app when source files change, using instructions specified in `nodemon.json`. -- For a detailed tutorial, check out our [programming quickstart](https://docs.dbos.dev/typescript/programming-guide). -- To learn how to deploy your application to DBOS Cloud, visit our [cloud quickstart](https://docs.dbos.dev/quickstart) -- To learn more about DBOS, take a look at [our documentation](https://docs.dbos.dev/) or our [source code](https://github.com/dbos-inc). diff --git a/packages/create/templates/hello-typeorm/eslint.config.js b/packages/create/templates/hello-typeorm/eslint.config.js deleted file mode 100644 index ad1a90dfa..000000000 --- a/packages/create/templates/hello-typeorm/eslint.config.js +++ /dev/null @@ -1,28 +0,0 @@ -const { FlatCompat } = require("@eslint/eslintrc"); -const dbosIncEslintPlugin = require("@dbos-inc/eslint-plugin"); -const typescriptEslint = require("typescript-eslint"); -const typescriptEslintParser = require("@typescript-eslint/parser"); -const globals = require("globals"); -const js = require("@eslint/js"); - -const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended -}); - -module.exports = typescriptEslint.config({ - extends: compat.extends("plugin:@dbos-inc/dbosRecommendedConfig"), - plugins: { "@dbos-inc": dbosIncEslintPlugin }, - - languageOptions: { - parser: typescriptEslintParser, - parserOptions: { project: "./tsconfig.json" }, - globals: { ...globals.node, ...globals.es6 } - }, - - rules: { - "no-secrets/no-secrets": ["error", { "tolerance": 5 }] - }, - - ignores: ["**/*.test.ts"] -}); diff --git a/packages/create/templates/hello-typeorm/gitignore.template b/packages/create/templates/hello-typeorm/gitignore.template deleted file mode 100644 index 9297009be..000000000 --- a/packages/create/templates/hello-typeorm/gitignore.template +++ /dev/null @@ -1,144 +0,0 @@ -# DBOS-specific -.dbos - -# Logs -**/.DS_Store -**/prisma/migrations/* - -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* -dbos_deploy/ - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# VSCode settings -.vscode/settings.json - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* - -# Editor temp/recovery files -*.swp -*.swo diff --git a/packages/create/templates/hello-typeorm/jest.config.js b/packages/create/templates/hello-typeorm/jest.config.js deleted file mode 100644 index 26529263a..000000000 --- a/packages/create/templates/hello-typeorm/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('ts-jest').JestConfigWithTsJest} */ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - testRegex: '((\\.|/)(test|spec))\\.ts?$', - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], - modulePaths: ["./"], -}; diff --git a/packages/create/templates/hello-typeorm/nodemon.json b/packages/create/templates/hello-typeorm/nodemon.json deleted file mode 100644 index 0425bb6ff..000000000 --- a/packages/create/templates/hello-typeorm/nodemon.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "watch": ["src/", "migrations/", "entities/"], - "ext": "ts,json", - "ignore": ["src/**/*.test.ts"], - "exec": "npm run build && npx dbos migrate && npm run start" - } - - \ No newline at end of file diff --git a/packages/create/templates/hello-typeorm/start_postgres_docker.js b/packages/create/templates/hello-typeorm/start_postgres_docker.js deleted file mode 100644 index 7660446a6..000000000 --- a/packages/create/templates/hello-typeorm/start_postgres_docker.js +++ /dev/null @@ -1,40 +0,0 @@ -const { execSync } = require('child_process'); - -// Default PostgreSQL port -let port = '5432'; - -// Set the host PostgreSQL port with the -p/--port flag. -process.argv.forEach((val, index) => { - if (val === '-p' || val === '--port') { - if (process.argv[index + 1]) { - port = process.argv[index + 1]; - } - } -}); - -if (!process.env.PGPASSWORD) { - console.error("Error: PGPASSWORD is not set."); - process.exit(1); -} - -try { - execSync(`docker run --rm --name=dbos-db --env=POSTGRES_PASSWORD="${process.env.PGPASSWORD}" --env=PGDATA=/var/lib/postgresql/data --volume=/var/lib/postgresql/data -p ${port}:5432 -d sibedge/postgres-plv8`); - console.log("Waiting for PostgreSQL to start..."); - - let attempts = 30; - const checkDatabase = setInterval(() => { - try { - execSync('docker exec dbos-db psql -U postgres -c "SELECT 1;"', { stdio: 'ignore' }); - console.log("PostgreSQL started!"); - clearInterval(checkDatabase); - console.log("Database started successfully!"); - } catch (error) { - if (--attempts === 0) { - clearInterval(checkDatabase); - console.error("Failed to start PostgreSQL."); - } - } - }, 1000); -} catch (error) { - console.error("Error starting PostgreSQL in Docker:", error.message); -} diff --git a/packages/create/templates/hello/.vscode/extensions.json b/packages/create/templates/hello/.vscode/extensions.json deleted file mode 100644 index e4e81f058..000000000 --- a/packages/create/templates/hello/.vscode/extensions.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - // Automatically recommend the DBOS extension to VSCode users. - // Documentation on extensions.json: http://go.microsoft.com/fwlink/?LinkId=827846 - "recommendations": [ - "dbos-inc.dbos-ttdbg" - ] -} \ No newline at end of file diff --git a/packages/create/templates/hello/.vscode/launch.json b/packages/create/templates/hello/.vscode/launch.json deleted file mode 100644 index 3977fca42..000000000 --- a/packages/create/templates/hello/.vscode/launch.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - // Automatically configure the VSCode debugger for DBOS projects. - // Documentation on launch.json: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node-terminal", - "request": "launch", - "name": "Local Debug", - "command": "npx dbos start", - "preLaunchTask": "npm: build", - }, - { - "type": "node-terminal", - "request": "launch", - "name": "Time Travel Debug", - "command": "npx dbos debug -x ${command:dbos-ttdbg.get-proxy-url} -u ${command:dbos-ttdbg.pick-workflow-id}", - "preLaunchTask": "npm: build" - } - ] - } \ No newline at end of file diff --git a/packages/create/templates/hello/.vscode/tasks.json b/packages/create/templates/hello/.vscode/tasks.json deleted file mode 100644 index 793379743..000000000 --- a/packages/create/templates/hello/.vscode/tasks.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - // Configure the build command for DBOS projects using VSCode. - // Documentation on tasks.json: https://code.visualstudio.com/docs/editor/tasks - "version": "2.0.0", - "tasks": [ - { - "label": "npm: build", - "type": "npm", - "script": "build", - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": [ - "$tsc" - ] - } - ] - } \ No newline at end of file diff --git a/packages/create/templates/hello/README.md b/packages/create/templates/hello/README.md deleted file mode 100644 index d8a243160..000000000 --- a/packages/create/templates/hello/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# DBOS Hello - -This is a [DBOS app](https://docs.dbos.dev/) bootstrapped with `npx @dbos-inc/create` and using [Knex](https://docs.dbos.dev/typescript/tutorials/orms/using-knex). - -## Getting Started - -Before you can launch your app, you need a database. -DBOS works with any Postgres database, but to make things easier, we've provided a script that starts a Docker Postgres container and creates a database. -Run: - -```bash -node start_postgres_docker.js -``` - -If successful, the script should print `Database started successfully!`. - -To build the app, set up the database, and run, in one step: -```bash -npm run dev -``` -This uses `nodemon`, so if you change your app it will automatically restart with changes. - -To see that it's working, visit this URL in your browser: [`http://localhost:3000/greeting/dbos`](http://localhost:3000/greeting/dbos). -You should get this message: `Hello, dbos! You have been greeted 1 times.` -Each time you refresh the page, the counter should go up by one! - -Congratulations! You just launched a DBOS application. - -### Separate Build and Run Steps - -To build the app: -```bash -npm run build -``` - -Then, run a schema migration to create some tables: - -```bash -npx dbos migrate -``` - -If successful, the migration should print `Migration successful!`. - -Finally, run the app: - -```bash -npx dbos start -``` - -## Next Steps - -- To add more functionality to this application, modify `src/operations.ts`, and save it. If you are using, `npm run dev`, `nodemon` will rebuild and restart the app automatically. -- For a detailed tutorial, check out our [programming quickstart](https://docs.dbos.dev/typescript/programming-guide). -- To learn how to deploy your application to DBOS Cloud, visit our [cloud quickstart](https://docs.dbos.dev/quickstart) -- To learn more about DBOS, take a look at [our documentation](https://docs.dbos.dev/) or our [source code](https://github.com/dbos-inc). diff --git a/packages/create/templates/hello/dbos-config.yaml b/packages/create/templates/hello/dbos-config.yaml deleted file mode 100644 index 3d4d04da6..000000000 --- a/packages/create/templates/hello/dbos-config.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# To enable auto-completion and validation for this file in VSCode, install the RedHat YAML extension -# https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml - -# yaml-language-server: $schema=https://raw.githubusercontent.com/dbos-inc/dbos-transact/main/dbos-config.schema.json - -language: node -database: - hostname: localhost - port: 5432 - username: postgres - password: ${PGPASSWORD} - connectionTimeoutMillis: 3000 - app_db_client: knex - migrate: - - npx knex migrate:latest - rollback: - - npx knex migrate:rollback -runtimeConfig: - entrypoints: - - dist/operations.js \ No newline at end of file diff --git a/packages/create/templates/hello/eslint.config.js b/packages/create/templates/hello/eslint.config.js deleted file mode 100644 index ad1a90dfa..000000000 --- a/packages/create/templates/hello/eslint.config.js +++ /dev/null @@ -1,28 +0,0 @@ -const { FlatCompat } = require("@eslint/eslintrc"); -const dbosIncEslintPlugin = require("@dbos-inc/eslint-plugin"); -const typescriptEslint = require("typescript-eslint"); -const typescriptEslintParser = require("@typescript-eslint/parser"); -const globals = require("globals"); -const js = require("@eslint/js"); - -const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended -}); - -module.exports = typescriptEslint.config({ - extends: compat.extends("plugin:@dbos-inc/dbosRecommendedConfig"), - plugins: { "@dbos-inc": dbosIncEslintPlugin }, - - languageOptions: { - parser: typescriptEslintParser, - parserOptions: { project: "./tsconfig.json" }, - globals: { ...globals.node, ...globals.es6 } - }, - - rules: { - "no-secrets/no-secrets": ["error", { "tolerance": 5 }] - }, - - ignores: ["**/*.test.ts"] -}); diff --git a/packages/create/templates/hello/gitignore.template b/packages/create/templates/hello/gitignore.template deleted file mode 100644 index 9297009be..000000000 --- a/packages/create/templates/hello/gitignore.template +++ /dev/null @@ -1,144 +0,0 @@ -# DBOS-specific -.dbos - -# Logs -**/.DS_Store -**/prisma/migrations/* - -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* -dbos_deploy/ - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# VSCode settings -.vscode/settings.json - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* - -# Editor temp/recovery files -*.swp -*.swo diff --git a/packages/create/templates/hello/jest.config.js b/packages/create/templates/hello/jest.config.js deleted file mode 100644 index 26529263a..000000000 --- a/packages/create/templates/hello/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('ts-jest').JestConfigWithTsJest} */ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - testRegex: '((\\.|/)(test|spec))\\.ts?$', - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], - modulePaths: ["./"], -}; diff --git a/packages/create/templates/hello/knexfile.js b/packages/create/templates/hello/knexfile.js deleted file mode 100644 index c315c00ad..000000000 --- a/packages/create/templates/hello/knexfile.js +++ /dev/null @@ -1,20 +0,0 @@ -const { parseConfigFile } = require('@dbos-inc/dbos-sdk'); - -const [dbosConfig, ] = parseConfigFile(); - -const config = { - client: 'pg', - connection: { - host: dbosConfig.poolConfig.host, - port: dbosConfig.poolConfig.port, - user: dbosConfig.poolConfig.user, - password: dbosConfig.poolConfig.password, - database: dbosConfig.poolConfig.database, - ssl: dbosConfig.poolConfig.ssl, - }, - migrations: { - directory: './migrations' - } -}; - -module.exports = config; diff --git a/packages/create/templates/hello/migrations/20240212161006_create_dbos_hello_tables.js b/packages/create/templates/hello/migrations/20240212161006_create_dbos_hello_tables.js deleted file mode 100644 index 2a1117be2..000000000 --- a/packages/create/templates/hello/migrations/20240212161006_create_dbos_hello_tables.js +++ /dev/null @@ -1,18 +0,0 @@ -const { Knex } = require("knex"); - -exports.up = async function(knex) { - await knex.schema.createTable('dbos_hello', table => { - table.text('name').primary(); - table.integer('greet_count').defaultTo(0); - }); - - return knex.schema.createTable('dbos_greetings', table => { - table.text('greeting_name'); - table.text('greeting_note_content'); - }); -}; - -exports.down = async function(knex) { - await knex.schema.dropTable('dbos_greetings'); - return knex.schema.dropTable('dbos_hello'); -}; diff --git a/packages/create/templates/hello/nodemon.json b/packages/create/templates/hello/nodemon.json deleted file mode 100644 index 43fb9eab5..000000000 --- a/packages/create/templates/hello/nodemon.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "watch": ["src/","migrations/"], - "ext": "ts,json", - "ignore": ["src/**/*.test.ts"], - "exec": "npm run build && npx dbos migrate && npm run start" -} - diff --git a/packages/create/templates/hello/package.json b/packages/create/templates/hello/package.json deleted file mode 100644 index 4ba2ac4b0..000000000 --- a/packages/create/templates/hello/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "dbos-hello-v2", - "version": "0.0.1", - "scripts": { - "build": "tsc", - "test": "npx dbos rollback && npx dbos migrate && jest", - "dev": "nodemon", - "start": "npx dbos start" - }, - "devDependencies": { - "@types/jest": "^29.5.12", - "@types/supertest": "^2.0.16", - "jest": "^29.7.0", - "nodemon": "^3.1.0", - "supertest": "^7.0.0", - "ts-jest": "^29.1.2", - "typescript": "^5.4.5" - }, - "dependencies": { - "@dbos-inc/dbos-sdk": "file:../../../..", - "knex": "3.1.0" - } -} diff --git a/packages/create/templates/hello/src/operations.test.ts b/packages/create/templates/hello/src/operations.test.ts deleted file mode 100644 index d57e060ea..000000000 --- a/packages/create/templates/hello/src/operations.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { DBOS } from "@dbos-inc/dbos-sdk"; -import { Hello, dbos_hello } from "./operations"; -import request from "supertest"; - -describe("operations-test", () => { - beforeAll(async () => { - await DBOS.launch(); - await DBOS.launchAppHTTPServer(); - }); - - afterAll(async () => { - await DBOS.shutdown(); - }); - - /** - * Test the transaction. - */ - test("test-transaction", async () => { - const res = await Hello.helloTransaction("dbos"); - expect(res).toMatch("Hello, dbos! You have been greeted"); - - // Check the greet count. - const rows = await DBOS.executor.queryUserDB("SELECT * FROM dbos_hello WHERE name=$1", ["dbos"]) as dbos_hello[]; - expect(rows[0].greet_count).toBe(1); - }); - - /** - * Test the HTTP endpoint. - */ - test("test-endpoint", async () => { - const res = await request(DBOS.getHTTPHandlersCallback()).get( - "/greeting/dbos" - ); - expect(res.statusCode).toBe(200); - expect(res.text).toMatch("Hello, dbos! You have been greeted"); - }); -}); diff --git a/packages/create/templates/hello/src/operations.ts b/packages/create/templates/hello/src/operations.ts deleted file mode 100644 index dab6c568d..000000000 --- a/packages/create/templates/hello/src/operations.ts +++ /dev/null @@ -1,64 +0,0 @@ -// Welcome to DBOS! - -// This is a sample "Hello" app built with DBOS. -// It greets visitors and keeps track of how many times each visitor has been greeted. - -// First, let's import DBOS -import { DBOS } from "@dbos-inc/dbos-sdk"; - -// Then, let's declare a type representing the "dbos_hello" database table -export interface dbos_hello { - name: string; - greet_count: number; -} - -// Now let's define a class with some static functions. -// DBOS uses TypeScript decorators to automatically make your functions reliable, so they need to be static. -export class Hello { - // Serve this function from HTTP GET requests at the /greeting endpoint with 'user' as a path parameter - @DBOS.getApi("/greeting/:user") - @DBOS.transaction() // Run this function as a database transaction - static async helloTransaction(user: string) { - // Retrieve and increment the number of times this user has been greeted. - const query = "INSERT INTO dbos_hello (name, greet_count) VALUES (?, 1) ON CONFLICT (name) DO UPDATE SET greet_count = dbos_hello.greet_count + 1 RETURNING greet_count;"; - const { rows } = (await DBOS.knexClient.raw(query, [user])) as { rows: dbos_hello[] }; - const greet_count = rows[0].greet_count; - const greeting = `Hello, ${user}! You have been greeted ${greet_count} times.`; - return Hello.makeHTML(greeting); - } - - // Serve a quick readme for the app at the / endpoint - @DBOS.getApi("/") - static async readme() { - const message = Hello.makeHTML( - `Visit the route /greeting/{name} to be greeted!
- For example, visit /greeting/Mike
- The counter increments with each page visit.` - ); - return Promise.resolve(message); - } - - // A helper function to create HTML pages with some styling - static makeHTML(message: string) { - const page = ` - - - - DBOS Template App - - - -

Welcome to DBOS!

-

` + message + `

-

- To learn how to run this app yourself, visit our - Quickstart. -

- Then, to learn how to build crashproof apps, continue to our - Programming Guide.
-

- - `; - return page; - } -} diff --git a/packages/create/templates/hello/start_postgres_docker.js b/packages/create/templates/hello/start_postgres_docker.js deleted file mode 100755 index 7660446a6..000000000 --- a/packages/create/templates/hello/start_postgres_docker.js +++ /dev/null @@ -1,40 +0,0 @@ -const { execSync } = require('child_process'); - -// Default PostgreSQL port -let port = '5432'; - -// Set the host PostgreSQL port with the -p/--port flag. -process.argv.forEach((val, index) => { - if (val === '-p' || val === '--port') { - if (process.argv[index + 1]) { - port = process.argv[index + 1]; - } - } -}); - -if (!process.env.PGPASSWORD) { - console.error("Error: PGPASSWORD is not set."); - process.exit(1); -} - -try { - execSync(`docker run --rm --name=dbos-db --env=POSTGRES_PASSWORD="${process.env.PGPASSWORD}" --env=PGDATA=/var/lib/postgresql/data --volume=/var/lib/postgresql/data -p ${port}:5432 -d sibedge/postgres-plv8`); - console.log("Waiting for PostgreSQL to start..."); - - let attempts = 30; - const checkDatabase = setInterval(() => { - try { - execSync('docker exec dbos-db psql -U postgres -c "SELECT 1;"', { stdio: 'ignore' }); - console.log("PostgreSQL started!"); - clearInterval(checkDatabase); - console.log("Database started successfully!"); - } catch (error) { - if (--attempts === 0) { - clearInterval(checkDatabase); - console.error("Failed to start PostgreSQL."); - } - } - }, 1000); -} catch (error) { - console.error("Error starting PostgreSQL in Docker:", error.message); -} diff --git a/packages/create/templates/hello/tsconfig.json b/packages/create/templates/hello/tsconfig.json deleted file mode 100644 index 5488d6185..000000000 --- a/packages/create/templates/hello/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -/* Visit https://aka.ms/tsconfig to read more about this file */ -{ - "compilerOptions": { - "target": "esnext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - "module": "Node16", /* Specify what module code is generated. */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - "declarationMap": true, /* Create sourcemaps for d.ts files. */ - "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ - "newLine": "lf", /* Set the newline character for emitting files. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - "strict": true, /* Enable all strict type-checking options. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - }, - "include": [ /* Specifies an array of filenames or patterns to include in the program. */ - "src" - ], - "exclude": [ - "**/*.test.ts", - "dist" - ] -} diff --git a/packages/dbos-openapi/TypeParser.ts b/packages/dbos-openapi/TypeParser.ts deleted file mode 100644 index c5c59dd0f..000000000 --- a/packages/dbos-openapi/TypeParser.ts +++ /dev/null @@ -1,198 +0,0 @@ -import ts from 'typescript'; -import { DiagnosticsCollector, diagResult } from './tsDiagUtil'; -import path from 'node:path'; -import fs from 'node:fs/promises'; - -export interface ClassInfo { - readonly node: ts.ClassDeclaration; - readonly name?: string; - readonly decorators: readonly DecoratorInfo[]; - readonly methods: readonly MethodInfo[]; -} - -export interface MethodInfo { - readonly node: ts.MethodDeclaration; - readonly name: string; - readonly decorators: readonly DecoratorInfo[]; - readonly parameters: readonly ParameterInfo[]; -} - -export interface ParameterInfo { - readonly node: ts.ParameterDeclaration; - readonly name: string; - readonly decorators: readonly DecoratorInfo[]; - readonly required: boolean; -} - -export interface DecoratorInfo { - node: ts.Decorator; - args: readonly ts.Expression[]; - name?: string; - module?: string; -} - -function isStaticMethod(node: ts.MethodDeclaration): boolean { - const mods = node.modifiers ?? []; - return mods.some(m => m.kind === ts.SyntaxKind.StaticKeyword); -} - -export class TypeParser { - readonly #program: ts.Program; - readonly #checker: ts.TypeChecker; - readonly #diags = new DiagnosticsCollector(); - get diags() { return this.#diags.diags; } - - constructor(program: ts.Program) { - this.#program = program; - this.#checker = program.getTypeChecker(); - } - - parse(): readonly ClassInfo[] | undefined { - const classes = new Array(); - for (const file of this.#program.getSourceFiles()) { - if (file.isDeclarationFile) continue; - for (const stmt of file.statements) { - if (ts.isClassDeclaration(stmt)) { - - const staticMethods = stmt.members - .filter(ts.isMethodDeclaration) - // Only static methods are supported now, so filter out instance methods by default - .filter(isStaticMethod) - .map(m => this.#getMethod(m)); - - classes.push({ - node: stmt, - // a class may not have a name if it's the default export - name: stmt.name?.getText(), - decorators: this.#getDecorators(stmt), - methods: staticMethods, - }); - } - } - } - - if (classes.length === 0) { - this.#diags.warn(`no classes found in ${JSON.stringify(this.#program.getRootFileNames())}`); - } - - return diagResult(classes, this.diags); - } - - #getMethod(node: ts.MethodDeclaration): MethodInfo { - const name = node.name.getText(); - const decorators = this.#getDecorators(node); - const parameters = node.parameters.map(p => this.#getParameter(p)); - return { node, name, decorators, parameters }; - } - - #getParameter(node: ts.ParameterDeclaration): ParameterInfo { - const decorators = this.#getDecorators(node); - const name = node.name.getText(); - const required = !node.questionToken && !node.initializer; - return { node, name, decorators, required }; - } - - #getImportSpecifierFromPAE(pae: ts.PropertyAccessExpression, checker: ts.TypeChecker): { name: string; module: string; } | undefined - { - const node = pae.expression; - const symbol = checker.getSymbolAtLocation(node); - const decls = symbol?.getDeclarations() ?? []; - for (const decl of decls) { - if (ts.isNamespaceImport(decl)) { - const name = pae.name; - const module = decl.parent.parent.moduleSpecifier as ts.StringLiteral; - return {name: name.getText(), module: module.text}; - } - if (ts.isImportClause(decl)) { - const name = pae.name; - const module = decl.parent.moduleSpecifier as ts.StringLiteral; - return { name: name.getText(), module: module.text }; - } - } - return undefined; - } - - #getDecoratorIdentifier(node: ts.Decorator): { name: string | undefined; module: string | undefined; args: readonly ts.Expression[]; } | undefined { - if (ts.isCallExpression(node.expression)) { - if (ts.isPropertyAccessExpression(node.expression.expression)) { - const pae: ts.PropertyAccessExpression = node.expression.expression; - // Let's imagine a module is the expression - const { name, module } = this.#getImportSpecifierFromPAE(pae, this.#checker) ?? {}; - return { name, module, args: node.expression.arguments }; - } - if (ts.isIdentifier(node.expression.expression)) { - const { name, module} = this.#getImportSpecifier(node.expression.expression, this.#checker) ?? {}; - return { name, module, args: node.expression.arguments }; - } - this.#diags.raise(`Unexpected decorator CallExpression.expression type: ${ts.SyntaxKind[node.expression.expression.kind]}`, node); - } - - if (ts.isIdentifier(node.expression)) { - const {name, module} = this.#getImportSpecifier(node.expression, this.#checker) ?? {}; - return { name, module, args: [] }; - } - this.#diags.raise(`Unexpected decorator expression type: ${ts.SyntaxKind[node.expression.kind]}`, node); - } - - #getImportSpecifier(node: ts.Node, checker: ts.TypeChecker): { name: string; module: string; } | undefined { - const symbol = checker.getSymbolAtLocation(node); - const decls = symbol?.getDeclarations() ?? []; - for (const decl of decls) { - if (ts.isImportSpecifier(decl)) { - // decl.name is the name for this type used in the local module. - // If the type name was overridden in the local module, the original type name is stored in decl.propertyName. - // Otherwise, decl.propertyName is undefined. - const name = (decl.propertyName ?? decl.name).getText(); - - // comment in TS AST declaration indicates moduleSpecifier *must* be a string literal - // "If [ImportDeclaration.moduleSpecifier] is not a StringLiteral it will be a grammar error." - const module = decl.parent.parent.parent.moduleSpecifier as ts.StringLiteral; - - return { name, module: module.text }; - } - } - return undefined; - } - - #getDecorators(node: ts.HasDecorators): DecoratorInfo[] { - - return (ts.getDecorators(node) ?? []) - .map(node => { - const decoratorIdentifier = this.#getDecoratorIdentifier(node); - if (!decoratorIdentifier) return undefined; - const { name, module, args } = decoratorIdentifier; - return { node, name, module, args } as DecoratorInfo; - }) - .filter((d): d is DecoratorInfo => !!d); - } -} - -export async function findPackageInfo(entrypoints: string[]): Promise<{ name: string, version: string }> { - for (const entrypoint of entrypoints) { - let dirname = path.dirname(entrypoint); - while (dirname !== '/') { - try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const packageJson = JSON.parse(await fs.readFile(path.join(dirname, 'package.json'), { encoding: 'utf-8' })); - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - const name = packageJson.name as string ?? "unknown"; - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - const version = packageJson.version as string | undefined; - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - const isPrivate = packageJson.private as boolean | undefined ?? false; - - return { - name, - version: version - ? version - : isPrivate ? "private" : "unknown" - }; - } catch (error) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any - if ((error as any).code !== 'ENOENT') throw error; - } - dirname = path.dirname(dirname); - } - } - return { name: "unknown", version: "unknown" }; -} diff --git a/packages/dbos-openapi/cli.ts b/packages/dbos-openapi/cli.ts deleted file mode 100644 index a46fa4fd9..000000000 --- a/packages/dbos-openapi/cli.ts +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env node - -import { Command } from 'commander'; -import { generateOpenApi } from "./openApi"; -import YAML from 'yaml'; -import fs from 'node:fs/promises'; -import path from 'node:path'; - -const program = new Command(); - -//////////////////////// -/* OpenAPI */ -//////////////////////// - -program - .command("generate") - .argument('', 'Specify entrypoints file path') - .action(async (entrypoints: string[]) => { - for (const entrypoint of entrypoints) { - const openapi = await generateOpenApi(entrypoint); - if (openapi) { - const filename = path.join(path.dirname(entrypoint), "openapi.yaml"); - const yaml = `# OpenApi specification generated for application\n\n` + YAML.stringify(openapi, { aliasDuplicateObjects: false }); - await fs.writeFile(filename, yaml, { encoding: 'utf-8' }); - } - } - }); - -program.parse(process.argv); - -// If no arguments provided, display help by default -if (!process.argv.slice(2).length) { - program.outputHelp(); -} diff --git a/packages/dbos-openapi/jest.config.js b/packages/dbos-openapi/jest.config.js deleted file mode 100644 index b3c1c9187..000000000 --- a/packages/dbos-openapi/jest.config.js +++ /dev/null @@ -1,14 +0,0 @@ -/** @type {import('ts-jest').JestConfigWithTsJest} */ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - testRegex: '((\\.|/)(test|spec))\\.ts?$', - testPathIgnorePatterns: ['examples/*'], - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], - modulePaths: ["./"], - collectCoverageFrom: [ - '*.ts', - ], - setupFiles: [ - ], -}; diff --git a/packages/dbos-openapi/openApi.ts b/packages/dbos-openapi/openApi.ts deleted file mode 100644 index 8b35c58a6..000000000 --- a/packages/dbos-openapi/openApi.ts +++ /dev/null @@ -1,675 +0,0 @@ -import ts from 'typescript'; -import { - DecoratorInfo, - MethodInfo, - TypeParser, - ClassInfo, - ParameterInfo, - findPackageInfo, -} from './TypeParser'; - -import { ArgSources } from "../../src/httpServer/handlerTypes"; -import { APITypes } from "../../src/httpServer/handlerTypes"; -import { exhaustiveCheckGuard } from '../../src/utils'; - -import { - createParser, - createFormatter, - SchemaGenerator, - SubNodeParser, - BaseType, - Context, - ReferenceType, - Schema, - PrimitiveType, - SubTypeFormatter, - Definition, - Config, -} from 'ts-json-schema-generator'; - -import { OpenAPIV3 as OpenApi3 } from 'openapi-types'; -import { diagResult, logDiagnostics, DiagnosticsCollector } from './tsDiagUtil'; - -function isValid(value: T | undefined): value is T { return value !== undefined; } - -interface HttpEndpointInfo { verb: APITypes; path: string; } - -// ts-json-schema-generator does not support BigInt, so OpenApiGenerator needs a custom type, formatter and parser for it -class BigIntType extends PrimitiveType { - getId(): string { return "integer"; } -} - -class BigIntKeywordParser implements SubNodeParser { - supportsNode(node: ts.Node): boolean { - return node.kind === ts.SyntaxKind.BigIntKeyword; - } - createType(_node: ts.Node, _context: Context, _reference?: ReferenceType | undefined): BaseType { - return new BigIntType(); - } -} - -class BigIntTypeFormatter implements SubTypeFormatter { - public supportsType(type: BigIntType): boolean { - return type instanceof BigIntType; - } - public getDefinition(_type: BigIntType): Definition { - // Note, JSON Schema integer type is not constrained to a specific size, so it is a valid type for BigInt - return { type: "integer", description: "bigint" }; - } - public getChildren(_type: BigIntType): BaseType[] { - return []; - } -} - -// type guard functions for ts.Type/TypeNode -function isNodeWithTypeArguments(node: ts.TypeNode): node is ts.NodeWithTypeArguments { - return "typeArguments" in node; -} - -function isObjectType(node: ts.Type): node is ts.ObjectType { - return !!(node.flags & ts.TypeFlags.Object); -} - -function isTypeReference(node: ts.Type): node is ts.TypeReference { - return isObjectType(node) && !!(node.objectFlags & ts.ObjectFlags.Reference); -} - -// workflow UUID header parameter info which is added to every generated endpoint -const workflowUuidParamName = "dbosWorkflowUUID"; -const workflowUuidRef: OpenApi3.ReferenceObject = { $ref: `#/components/parameters/${workflowUuidParamName}` } -const workflowUuidParam: readonly [string, OpenApi3.ParameterObject] = [workflowUuidParamName, { - name: 'dbos-idempotency-key', - in: 'header', - required: false, - description: "Caller specified [workflow idempotency key](https://docs.dbos.dev/tutorials/idempotency-tutorial#setting-idempotency-keys)", - schema: { type: 'string' }, -}] as const; - -export class OpenApiGenerator { - readonly #checker: ts.TypeChecker; - readonly #schemaGenerator: SchemaGenerator; - readonly #schemaMap: Map = new Map(); - readonly #securitySchemeMap: Map = new Map(); - readonly #diags = new DiagnosticsCollector(); - get diags() { return this.#diags.diags; } - - constructor(readonly program: ts.Program) { - this.#checker = program.getTypeChecker(); - const config: Config = { - discriminatorType: 'open-api', - encodeRefs: false - }; - const parser = createParser(program, config, aug => aug.addNodeParser(new BigIntKeywordParser())); - const formatter = createFormatter(config, (fmt) => fmt.addTypeFormatter(new BigIntTypeFormatter())); - this.#schemaGenerator = new SchemaGenerator(program, parser, formatter, {}); - } - - generate(classes: readonly ClassInfo[], title: string, version: string): OpenApi3.Document | undefined { - const paths = new Array<[string, OpenApi3.PathItemObject]>(); - classes.forEach((cls, index) => { - // if the class name is not specified, manufacture a name using the class index as the prefix - // JS class identifiers cannot start with a digit but OpenApi security scheme names can - const securitySchemeName = cls.name ? `${cls.name}Auth` : `${index}ClassAuth`; - const securitySchemeDecorator = this.getDBOSDecorator(cls, 'OpenApiSecurityScheme'); - const securityScheme = this.parseSecurityScheme(securitySchemeDecorator?.args[0]); - const defaultRoles = this.parseStringLiteralArray(this.getDBOSDecorator(cls, 'DefaultRequiredRole')?.args[0]) ?? []; - - if (securityScheme) { - this.#securitySchemeMap.set(securitySchemeName, securityScheme); - } - - for (const method of cls.methods) { - const http = this.getHttpInfo(method); - if (!http) continue; - const path = this.generatePath(method, http, defaultRoles, securityScheme ? securitySchemeName : undefined); - if (path) paths.push(path); - } - }) - - const openApi: OpenApi3.Document = { - openapi: "3.0.3", // https://spec.openapis.org/oas/v3.0.3 - info: { title, version }, - paths: Object.fromEntries(paths), - components: { - parameters: Object.fromEntries([workflowUuidParam]), - schemas: Object.fromEntries(this.#schemaMap), - securitySchemes: Object.fromEntries(this.#securitySchemeMap) - } - } - return diagResult(openApi, this.diags); - } - - generatePath(method: MethodInfo, { verb, path } : HttpEndpointInfo, defaultRoles: readonly string[], securityScheme: string | undefined): [string, OpenApi3.PathItemObject] | undefined { - const sourcedParams = method.parameters - // The first parameter of a handle method must be an DBOSContext, which is not exposed via the API - .slice(1) - .map(p => [p, this.getParamSource(p, verb)] as [ParameterInfo, ArgSources]); - - const parameters: Array = this.generateParameters(sourcedParams); - // add optional parameter for workflow UUID header - parameters.push(workflowUuidRef); - - const requestBody = this.generateRequestBody(sourcedParams); - const response = this.generateResponse(method); - if (!response) return; - - // if there is a security scheme specified, create a security requirement for it - // unless the method has no required roles - const security = new Array(); - if (securityScheme) { - const roles = this.parseStringLiteralArray(this.getDBOSDecorator(method, 'RequiredRole')?.args[0]) ?? defaultRoles; - if (roles.length > 0) { - security.push({ [securityScheme]: [] }); - } - } - - const operation: OpenApi3.OperationObject = { - operationId: method.name, - responses: Object.fromEntries([response]), - parameters, - requestBody, - security: security.length > 0 ? security : undefined, - } - - // validate all path parameters have matching parameters with URL ArgSource - const pathParams = path.split('/') - .filter(p => p.startsWith(':')) - .map(p => p.substring(1)); - - for (const pathParam of pathParams) { - const param = sourcedParams.find(([parameter, _]) => parameter.name === pathParam); - if (!param) { - this.#diags.raise(`Missing path parameter ${pathParam} for ${method.name}`, method.node); - return; - } - if (param[1] !== ArgSources.URL) { - this.#diags.raise(`Path parameter ${pathParam} must be a URL parameter: ${method.name}`, param[0].node); - return; - } - } - - // OpenAPI indicates path parameters with curly braces, but DBOS uses colons - const $path = path.split('/') - .map(p => p.startsWith(':') ? `{${p.substring(1)}}` : p) - .join('/'); - - switch (verb) { - case APITypes.GET: return [$path, { get: operation }]; - case APITypes.POST: return [$path, { post: operation }]; - case APITypes.PUT: return [$path, { put: operation }]; - case APITypes.PATCH: return [$path, { patch: operation }]; - case APITypes.DELETE: return [$path, { delete: operation }]; - default: exhaustiveCheckGuard(verb) - } - } - - getMethodReturnType(node: ts.MethodDeclaration): ts.Type | undefined { - // if the method has a declared return type, try to use that - if (node.type) { - if (!isNodeWithTypeArguments(node.type)) { - this.#diags.warn(`Unexpected type node kind ${ts.SyntaxKind[node.type.kind]}`, node.type); - } else { - // return type must be a Promise, so take the first type argument - const typeArg = node.type.typeArguments?.[0]; - if (typeArg) return this.#checker.getTypeFromTypeNode(typeArg); - - const printer = ts.createPrinter(); - const text = printer.printNode(ts.EmitHint.Unspecified, node.type, node.getSourceFile()); - this.#diags.warn(`Unexpected return type without at least one type argument ${text}`, node.type); - } - } - - // if the return type is inferred or the Promise type argument could not be determined, - // infer the return type via the type checker - const signature = this.#checker.getSignatureFromDeclaration(node); - const returnType = signature?.getReturnType(); - if (!returnType || !isTypeReference(returnType)) return undefined; - // again, return type must be a Promise, so take the first type argument - return returnType.typeArguments?.[0]; - } - - generateResponse(method: MethodInfo): [string, OpenApi3.ResponseObject] | undefined { - - const returnType = this.getMethodReturnType(method.node); - if (!returnType) { - this.#diags.raise(`Could not determine return type of ${method.name}`, method.node); - return; - } - - if (returnType.flags & ts.TypeFlags.VoidLike) { - return ["204", { description: "No Content" }]; - } - - const typeNode = this.#checker.typeToTypeNode(returnType, undefined, undefined); - if (!typeNode) { - this.#diags.warn(`Could not determine return type node of ${method.name}`, method.node); - } - - return ["200", { - description: "Ok", - content: { - "application/json": { - schema: this.generateSchema(typeNode) - } - } - }]; - } - - generateParameters(sourcedParams: [ParameterInfo, ArgSources][]): OpenApi3.ParameterObject[] { - if (sourcedParams.length === 0) return []; - return sourcedParams - // QUERY and URL parameters are specified in the Operation.parameters field - .filter(([_, source]) => source === ArgSources.QUERY || source === ArgSources.URL) - .map(([parameter, argSource]) => this.generateParameter(parameter, argSource)) - .filter(isValid); - } - - generateParameter(parameter: ParameterInfo, argSource: ArgSources): OpenApi3.ParameterObject | undefined { - if (argSource === ArgSources.BODY) { - this.#diags.raise(`BODY parameters must be specified in the Operation.requestBody field: ${parameter.name}`, parameter.node); - return; - } - if (argSource === ArgSources.URL && !parameter.required) { - this.#diags.raise(`URL parameters must be required: ${parameter.name}`, parameter.node); - return; - } - - const schema = this.generateSchema(parameter.node); - const name = this.getParamName(parameter); - const location = this.getLocation(parameter, argSource); - if (!name || !location) return; - - return { - name, - in: location, - required: parameter.required, - schema - }; - - } - - getLocation(parameter: ParameterInfo, argSource: ArgSources): "query" | "path" | undefined { - switch (argSource) { - case ArgSources.DEFAULT: - case ArgSources.BODY: - break; - case ArgSources.QUERY: return 'query'; - case ArgSources.URL: return 'path'; - default: { - const _: never = argSource; - break; - } - } - this.#diags.raise(`Unsupported Parameter ArgSource: ${argSource}`, parameter.node); - } - - generateRequestBody(sourcedParams: [ParameterInfo, ArgSources][]): OpenApi3.RequestBodyObject | undefined { - // BODY parameters are specified in the Operation.requestBody field - const parameters = sourcedParams - .filter(([_, source]) => source === ArgSources.BODY) - .map(([parameter, _]) => [this.getParamName(parameter), parameter] as [name: string, ParameterInfo]); - - if (parameters.length === 0) return undefined; - - const properties = parameters.map(([name, parameter]) => [name, this.generateSchema(parameter.node)] as [string, OpenApi3.SchemaObject]); - const schema: OpenApi3.SchemaObject = { - type: 'object', - properties: Object.fromEntries(properties), - required: parameters.filter(([_, parameter]) => parameter.required).map(([name, _]) => name), - } - - return { - required: true, - content: { - "application/json": { schema } - } - } - } - - generateSchema(node?: ts.Node): OpenApi3.SchemaObject | OpenApi3.ReferenceObject | undefined { - if (!node) return { description: "Undefined Node" }; - - const schema = this.#schemaGenerator.createSchemaFromNodes([node]); - if (!schema.$ref) return { description: "No $ref" }; - - const slashIndex = schema.$ref.lastIndexOf('/'); - if (slashIndex === -1) return { description: `Invalid $ref ${schema.$ref}` }; - const name = schema.$ref.substring(slashIndex + 1); - - const defs = new Map(Object.entries(schema.definitions ?? {})); - if (defs.size === 0) return { description: "No definitions" }; - const def = defs.get(name); - - if (!def) return { description: `No definition ${name}` }; - if (typeof def === 'boolean') return { description: `Definition ${name} is a boolean` }; - - if (defs.size > 1) { - defs.delete(name); - for (const [$name, $def] of defs) { - if (typeof $def === 'boolean') continue; - const $schema = this.mapSchema($def); - if ($schema) this.#schemaMap.set($name, $schema); - } - } - - return this.mapSchema(def); - } - - getDBOSDecorator(decorated: MethodInfo | ParameterInfo | ClassInfo, name: string): DecoratorInfo | undefined { - const filtered = decorated.decorators.filter(d => d.module === '@dbos-inc/dbos-sdk' && d.name === name); - if (filtered.length === 0) return undefined; - if (filtered.length > 1) { - this.#diags.raise(`Multiple ${JSON.stringify(name)} decorators found on ${decorated.name ?? ""}`, decorated.node); - } - return filtered[0]; - } - - getHttpInfo(method: MethodInfo): HttpEndpointInfo | undefined { - const httpDecoratorVerbs: Record = { - 'GetApi': APITypes.GET, - 'PostApi': APITypes.POST, - 'PutApi': APITypes.PUT, - 'PatchApi': APITypes.PATCH, - 'DeleteApi': APITypes.DELETE, - }; - const httpDecorators = Object.entries(httpDecoratorVerbs).map(([name, verb]) => { - return { - verb, - decorator: this.getDBOSDecorator(method, name)} - }).filter(d => !!d.decorator); - - if (!httpDecorators.length) return undefined; - - if (httpDecorators.length > 1) { - this.#diags.raise(`Method ${method.name} has more than one Api decorator`); - return; - } - const apiDecorator = httpDecorators[0]; - const verb = apiDecorator.verb; - const arg = apiDecorator.decorator!.args[0]; - if (!arg) { - this.#diags.raise(`Missing path argument for ${verb}Api decorator`, method.node); - return; - } - if (!ts.isStringLiteral(arg)) { - this.#diags.raise(`Unexpected path argument type: ${ts.SyntaxKind[arg.kind]}`, method.node); - return; - } - return { verb, path: arg.text }; - } - - getParamSource(parameter: ParameterInfo, verb: APITypes): ArgSources.BODY | ArgSources.QUERY | ArgSources.URL | undefined { - const argSource = this.getDBOSDecorator(parameter, 'ArgSource'); - if (!argSource) return getDefaultArgSource(verb); - - if (!ts.isPropertyAccessExpression(argSource.args[0])) { - this.#diags.raise(`Unexpected ArgSource argument type: ${ts.SyntaxKind[argSource.args[0].kind]}`, parameter.node); - return; - } - - const name = argSource.args[0].name.text; - const argSrc = name as ArgSources; - switch (argSrc) { - case ArgSources.DEFAULT: return getDefaultArgSource(verb); - case ArgSources.BODY: return ArgSources.BODY; - case ArgSources.QUERY: return ArgSources.QUERY; - case ArgSources.URL: return ArgSources.URL; - default: { - const _: never = argSrc; - this.#diags.raise(`Unexpected ArgSource argument: ${name}`, parameter.node); - return; - } - } - - function getDefaultArgSource(verb: APITypes): ArgSources.BODY | ArgSources.QUERY | undefined { - switch (verb) { - case APITypes.GET: return ArgSources.QUERY; - case APITypes.POST: return ArgSources.BODY; - case APITypes.PUT: return ArgSources.BODY; - case APITypes.PATCH: return ArgSources.BODY; - case APITypes.DELETE: return ArgSources.QUERY; - default: exhaustiveCheckGuard(verb) - } - } - } - - getParamName(parameter: ParameterInfo): string | undefined { - const argName = this.getDBOSDecorator(parameter, 'ArgName'); - if (!argName) return parameter.name; - - const nameParam = argName.args[0]; - if (nameParam && ts.isStringLiteral(nameParam)) return nameParam.text; - - this.#diags.raise(`Unexpected ArgName argument type: ${ts.SyntaxKind[nameParam.kind]}`, parameter.node); - } - - mapSchema(schema: Schema): OpenApi3.SchemaObject | OpenApi3.ReferenceObject | undefined { - - if (Array.isArray(schema.type)) { - this.#diags.raise(`OpenApi 3.0.x doesn't support type arrays: ${JSON.stringify(schema.type)}`); - return; - } - - if (schema.$ref) { - const $ref = schema.$ref.replace("#/definitions/", "#/components/schemas/"); - return { $ref } - } - - const [maximum, exclusiveMaximum] = getMaxes(); - const [minimum, exclusiveMinimum] = getMins(); - - const base: OpenApi3.BaseSchemaObject = { - title: schema.title, - multipleOf: schema.multipleOf, - maximum, - exclusiveMaximum, - minimum, - exclusiveMinimum, - maxLength: schema.maxLength, - minLength: schema.minLength, - pattern: schema.pattern, - maxItems: schema.maxItems, - minItems: schema.minItems, - uniqueItems: schema.uniqueItems, - maxProperties: schema.maxProperties, - minProperties: schema.minProperties, - required: schema.required, - enum: schema.enum, - description: schema.description, - format: schema.format, - default: schema.default, - - // OpenApi 3.0.x doesn't support boolean schema types, so filter those out when mapping these fields - allOf: schema.allOf?.filter(isSchema).map(s => this.mapSchema(s)).filter(isValid), - oneOf: schema.oneOf?.filter(isSchema).map(s => this.mapSchema(s)).filter(isValid), - anyOf: schema.anyOf?.filter(isSchema).map(s => this.mapSchema(s)).filter(isValid), - not: schema.not - ? isSchema(schema.not) ? this.mapSchema(schema.not) : undefined - : undefined, - properties: schema.properties - ? Object.fromEntries( - Object.entries(schema.properties) - .filter((entry): entry is [string, Schema] => isSchema(entry[1])) - .map(([name, prop]) => [name, this.mapSchema(prop)]) - .filter((entry): entry is [string, OpenApi3.SchemaObject | OpenApi3.ReferenceObject] => isValid(entry[1]))) - : undefined, - additionalProperties: typeof schema.additionalProperties === 'object' - ? this.mapSchema(schema.additionalProperties) - : schema.additionalProperties, - } - - if (schema.type === 'array') { - if (schema.items === undefined) { - this.#diags.raise(`Array schema has no items`); - return; - } - if (Array.isArray(schema.items)) { - this.#diags.raise(`OpenApi 3.0.x doesn't support array items arrays: ${JSON.stringify(schema.items)}`); - return; - } - if (typeof schema.items === 'boolean') { - this.#diags.raise(`OpenApi 3.0.x doesn't support array items booleans: ${JSON.stringify(schema.items)}`); - return; - } - - return { - type: schema.type, - items: this.mapSchema(schema.items), - ...base, - } - } - - return { - // OpenApi 3.0.x doesn't support null type value, so map null type to undefined and add nullable: true property - type: schema.type === 'null' ? undefined : schema.type, - ...base, - nullable: schema.type === 'null' ? true : undefined, - } - - function isSchema(schema: Schema | boolean): schema is Schema { - return typeof schema === "object"; - } - - // Convert JSON Schema Draft 7 (used by ts-json-schema-generator) max/min and exclusive max/min values to JSON Schema Draft 5 (used by OpenAPI). - // In Draft 5, exclusive max/min values are booleans - // In Draft 7, exclusive max/min values are numbers - - function getMaxes(): [maximum: number | undefined, exclusiveMaximum: boolean | undefined] { - const { maximum: max, exclusiveMaximum: xMax } = schema; - - if (max) { - if (xMax) { - return (xMax < max) ? [xMax, true] : [max, false]; - } else { - return [max, false]; - } - } else { - return xMax ? [xMax, true] : [undefined, undefined]; - } - } - - function getMins(): [minimum: number | undefined, exclusiveMinimum: boolean | undefined] { - const { minimum: min, exclusiveMinimum: xMin } = schema; - - if (min) { - if (xMin) { - return (xMin > min) ? [xMin, true] : [min, false]; - } else { - return [min, false]; - } - - } else { - return xMin ? [xMin, true] : [undefined, undefined]; - } - } - } - - parseStringLiteralArray(node: ts.Expression | undefined): string[] | undefined { - if (!node) return undefined; - if (!ts.isArrayLiteralExpression(node)) { - this.#diags.raise(`Expected Array Literal node, received: ${ts.SyntaxKind[node.kind]}`, node); - return; - } - - const values = new Array(); - for (const exp of node.elements) { - if (ts.isStringLiteral(exp)) { - values.push(exp.getText()); - } else { - this.#diags.raise(`Expected String Literal node, received: ${ts.SyntaxKind[exp.kind]}`, exp); - } - } - return values; - } - - parseSecurityScheme(arg?: ts.Expression): SecurityScheme | undefined { - if (!arg) return undefined; - if (!ts.isObjectLiteralExpression(arg)) { - this.#diags.raise(`Expected Object Literal node, received: ${ts.SyntaxKind[arg.kind]}`, arg); - return undefined; - } - - const map = new Map(); - for (const prop of arg.properties) { - if (!ts.isPropertyAssignment(prop)) { - this.#diags.raise(`Expected Property Assignment node, received: ${ts.SyntaxKind[prop.kind]}`, prop); - continue; - } - - if (!ts.isStringLiteral(prop.initializer)) { - this.#diags.raise(`Expected String Literal node, received: ${ts.SyntaxKind[prop.initializer.kind]}`, prop.initializer); - continue; - } - - map.set(prop.name.getText(), prop.initializer.text); - } - - const type = map.get("type"); - const description = map.get("description"); - if (!type) { - this.#diags.raise(`missing type property`, arg); - return; - } - - if (type === 'http') { - const scheme = map.get("scheme"); - if (!scheme) { - this.#diags.raise(`missing scheme property`, arg); - return; - } - const bearerFormat = map.get("bearerFormat"); - - return { type, scheme, bearerFormat, description }; - } - - if (type === 'apiKey') { - const name = map.get("name"); - if (!name) { - this.#diags.raise(`missing name property`, arg); - return; - } - const $in = map.get("id"); - if (!$in) { - this.#diags.raise(`missing in property`, arg); - return; - } - - return { type, name, in: $in, description }; - } - - if (type === "openIdConnect") { - const openIdConnectUrl = map.get("openIdConnectUrl"); - if (!openIdConnectUrl) { - this.#diags.raise(`missing openIdConnectUrl property`, arg); - return; - } - return { type, openIdConnectUrl, description }; - } - - - if (type === 'oauth2') { - this.#diags.raise(`OAuth2 Security Scheme not supported`, arg); - } else { - this.#diags.raise(`unrecognized Security Scheme type ${type}`, arg); - } - return; - } -} - -type SecurityScheme = Exclude; - -export async function generateOpenApi(entrypoint: string): Promise { - - const program = ts.createProgram([entrypoint], {}); - - const parser = new TypeParser(program); - const classes = parser.parse(); - logDiagnostics(parser.diags); - if (!classes || classes.length === 0) return undefined; - - const { name, version } = await findPackageInfo([entrypoint]); - const generator = new OpenApiGenerator(program); - const openapi = generator.generate(classes, name, version); - logDiagnostics(generator.diags); - return openapi; -} diff --git a/packages/dbos-openapi/package.json b/packages/dbos-openapi/package.json deleted file mode 100644 index b698796aa..000000000 --- a/packages/dbos-openapi/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "@dbos-inc/dbos-openapi", - "version": "0.0.0-placeholder", - "description": "Tool for integrating DBOS functions via OpenAPI", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/dbos-inc/dbos-transact", - "directory": "packages/openapi-gen" - }, - "main": "cli.ts", - "homepage": "https://docs.dbos.dev/", - "scripts": { - "build": "tsc --project tsconfig.json", - "test": "npm run build && jest --coverage --collectCoverageFrom='src/**/*' --detectOpenHandles" - }, - "bin": { - "dbos-openapi": "./dist/packages/dbos-openapi/cli.js" - }, - "devDependencies": { - "@types/jest": "^29.5.3", - "@types/node": "^20.6.3", - "@types/supertest": "^2.0.12", - "jest": "^29.6.1", - "supertest": "^7.0.0", - "ts-jest": "^29.1.1" - }, - "dependencies": { - "commander": "^11.0.0", - "openapi-types": "^12.1.3", - "ts-json-schema-generator": "^1.5.1", - "typescript": "5.4.5", - "yaml": "^2.3.4" - } -} diff --git a/packages/dbos-openapi/tests/makeProgram.ts b/packages/dbos-openapi/tests/makeProgram.ts deleted file mode 100644 index 88b7f8896..000000000 --- a/packages/dbos-openapi/tests/makeProgram.ts +++ /dev/null @@ -1,65 +0,0 @@ -import ts from "typescript"; -import path from "node:path"; -import fs from "node:fs"; - -const sdkRepoRoot = path.join(__dirname, "../../.."); -const dbosSDKModule = "node_modules/@dbos-inc/dbos-sdk/"; - -function readFile(fileName: string) { - if (fileName.startsWith(dbosSDKModule)) { - const $path = path.join(sdkRepoRoot, fileName.slice(dbosSDKModule.length)); - return fs.existsSync($path) ? fs.readFileSync($path, "utf-8") : undefined; - } - if (fileName.startsWith("node_modules/@dbos-inc/")) { - return undefined; - } - if (fileName.startsWith("node_modules/")) { - const $path = path.join(sdkRepoRoot, fileName); - return fs.existsSync($path) ? fs.readFileSync($path, "utf-8") : undefined; - } - return undefined; -} - -function readDirectory(directoryName: string) { - if (directoryName.startsWith(dbosSDKModule)) { - const $path = path.join(sdkRepoRoot, directoryName.slice(dbosSDKModule.length)); - return fs.readdirSync($path, { withFileTypes: true }).filter(f => f.isDirectory()).map(f => f.name); - } - if (directoryName.startsWith("node_modules/")) { - const $path = path.join(sdkRepoRoot, directoryName); - return fs.readdirSync($path, { withFileTypes: true }).filter(f => f.isDirectory()).map(f => f.name); - } - - return undefined; -} - -/* - * Helper function to generate a TS program object from a string for testing TS Compiler related features - */ -export function makeTestTypescriptProgram(source: string): ts.Program { - const inputFileName = "operation.ts"; - const sourceFile = ts.createSourceFile(inputFileName, source, ts.ScriptTarget.ESNext); - const compilerHost: ts.CompilerHost = { - getSourceFile: (fileName) => { - return fileName === inputFileName ? sourceFile : undefined; - }, - writeFile: () => { }, - getDefaultLibFileName: () => "lib.d.ts", - useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames, - getCanonicalFileName: fileName => fileName, - getCurrentDirectory: () => "", - getNewLine: () => ts.sys.newLine, - fileExists: (fileName) => (fileName === inputFileName) - ? true - : readFile(fileName) !== undefined, - readFile, - directoryExists: (directoryName) => { - if (directoryName === "node_modules") return true; - if (directoryName === "node_modules/@dbos-inc") return true; - if (directoryName === "node_modules/@dbos-inc/dbos-sdk") return true; - return readDirectory(directoryName) !== undefined; - }, - getDirectories: (path) => readDirectory(path) ?? [] - }; - return ts.createProgram([inputFileName], {}, compilerHost); -} diff --git a/packages/dbos-openapi/tests/openapi.test.ts b/packages/dbos-openapi/tests/openapi.test.ts deleted file mode 100644 index 2ec7929e6..000000000 --- a/packages/dbos-openapi/tests/openapi.test.ts +++ /dev/null @@ -1,393 +0,0 @@ -import ts from "typescript"; -import { TypeParser } from "../TypeParser"; -import { OpenApiGenerator } from "../openApi"; -import path from "node:path"; -import { makeTestTypescriptProgram } from "./makeProgram"; - -const printer = ts.createPrinter(); - -const entrypoint = path.join(__dirname, "../../create/templates/hello-contexts/src/operations.ts"); -const program = ts.createProgram([entrypoint], {}); - -describe("TypeParser", () => { - it("examples/hello", () => { - const parser = new TypeParser(program); - const classes = parser.parse(); - expect(parser.diags.length).toBe(0); - expect(classes).toBeDefined(); - expect(classes!.length).toBe(1); - - const [$class] = classes!; - expect($class.name).toBe("Hello"); - expect($class.decorators.length).toBe(0); - expect($class.methods.length).toBe(3); - - const method = $class.methods[0].name === "helloTransaction" ? $class.methods[0] : - $class.methods[1].name === "helloTransaction" ? $class.methods[1] : - $class.methods[2]; - expect(method.name).toBe("helloTransaction"); - expect(method.decorators.length).toBe(2); - - const [dec1, dec2] = method.decorators; - expect(dec1.name).toBe("GetApi"); - expect(dec1.args.length).toBe(1); - const [arg1] = dec1.args; - expect(arg1.kind).toBe(ts.SyntaxKind.StringLiteral); - expect((arg1 as ts.StringLiteral).text).toBe("/greeting/:user"); - - expect(dec2.name).toBe("Transaction"); - expect(dec2.args.length).toBe(0); - - expect(method.parameters.length).toBe(2); - const [param1, param2] = method.parameters; - expect(param1.name).toBe("ctxt"); - expect(param1.decorators.length).toBe(0); - - expect(param2.name).toBe("user"); - expect(param2.decorators.length).toBe(1); - const [dec3] = param2.decorators; - expect(dec3.name).toBe("ArgSource"); - - const dec3param = printer.printNode(ts.EmitHint.Unspecified, dec3.args[0], dec3.args[0].getSourceFile()); - expect(dec3param).toBe("ArgSources.URL"); - }); -}); - -describe("OpenApiGenerator", () => { - - it("examples/hello", () => { - const expected = { - openapi: "3.0.3", - info: { - title: "dbos-hello", - version: "0.0.1" - }, - paths: { - "/greeting/{user}": { - get: { - operationId: "helloTransaction", - responses: { - "200": { - description: "Ok", - content: { - "application/json": { - schema: { - type: "string" - } - } - } - } - }, - parameters: [ - { - name: "user", - in: "path", - required: true, - schema: { - type: "string" - } - }, - { - "$ref": "#/components/parameters/dbosWorkflowUUID" - } - ] - } - } - }, - components: { - schemas: {}, - parameters: { - dbosWorkflowUUID: { - name: "dbos-idempotency-key", - in: "header", - required: false, - schema: { - type: "string" - } - } - } - } - }; - - const parser = new TypeParser(program); - const classes = parser.parse(); - expect(parser.diags.length).toBe(0); - expect(classes).toBeDefined(); - expect(classes!.length).toBe(1); - - const generator = new OpenApiGenerator(program); - const openApi = generator.generate(classes!, "dbos-hello", "0.0.1"); - expect(generator.diags.length).toBe(0); - - expect(openApi).toBeDefined(); - expect(openApi).toMatchObject(expected); - }); - - it("OpenApiSecurityScheme RequiredRole", () => { - const source = /*javascript*/` - import { TransactionContext, Transaction, ArgSource, ArgSources, OpenApiSecurityScheme, RequiredRole } from '@dbos-inc/dbos-sdk'; - import * as dbos from '@dbos-inc/dbos-sdk'; - import dbossdk from '@dbos-inc/dbos-sdk'; - - @OpenApiSecurityScheme({ type: 'http', scheme: 'bearer' }) - export class Hello { - @dbos.GetApi('/greeting/:user') - @RequiredRole(['user']) - static async helloTransaction(ctxt: HandlerContext, @dbossdk.ArgSource(ArgSources.URL) user: string): Promise { - return ""; - } - } - `; - - const expected = { - openapi: "3.0.3", - info: { - title: "dbos-hello", - version: "0.0.1" - }, - paths: { - "/greeting/{user}": { - get: { - operationId: "helloTransaction", - responses: { - 200: { - description: "Ok", - content: { - "application/json": { - schema: { - type: "string" - } - } - } - } - }, - parameters: [ - { - name: "user", - in: "path", - required: true, - schema: { - type: "string" - } - }, - { - $ref: "#/components/parameters/dbosWorkflowUUID" - } - ], - security: [ - { - HelloAuth: [] - } - ] - } - } - }, - components: { - parameters: { - dbosWorkflowUUID: { - name: "dbos-idempotency-key", - in: "header", - required: false, - description: "Caller specified [workflow idempotency key](https://docs.dbos.dev/tutorials/idempotency-tutorial#setting-idempotency-keys)", - schema: { - type: "string" - } - } - }, - schemas: {}, - securitySchemes: { - HelloAuth: { - type: "http", - scheme: "bearer" - } - } - } - }; - - const program = makeTestTypescriptProgram(source); - const parser = new TypeParser(program); - const classes = parser.parse(); - expect(parser.diags.length).toBe(0); - const generator = new OpenApiGenerator(program); - const openApi = generator.generate(classes!, "dbos-hello", "0.0.1"); - expect(generator.diags.length).toBe(0); - expect(openApi).toBeDefined(); - expect(openApi).toMatchObject(expected); - }); - - it("OpenApiSecurityScheme DefaultRequiredRole", () => { - const source = /*javascript*/` - import { TransactionContext, Transaction, GetApi, ArgSource, ArgSources, DefaultRequiredRole, OpenApiSecurityScheme } from '@dbos-inc/dbos-sdk' - - @OpenApiSecurityScheme({ type: 'http', scheme: 'bearer' }) - @DefaultRequiredRole(['user']) - export class Hello { - @GetApi('/greeting/:user') - static async helloTransaction(ctxt: HandlerContext, @ArgSource(ArgSources.URL) user: string): Promise { - return ""; - } - } - `; - - const expected = { - openapi: "3.0.3", - info: { - title: "dbos-hello", - version: "0.0.1" - }, - paths: { - "/greeting/{user}": { - get: { - operationId: "helloTransaction", - responses: { - 200: { - description: "Ok", - content: { - "application/json": { - schema: { - type: "string" - } - } - } - } - }, - parameters: [ - { - name: "user", - in: "path", - required: true, - schema: { - type: "string" - } - }, - { - $ref: "#/components/parameters/dbosWorkflowUUID" - } - ], - security: [ - { - HelloAuth: [] - } - ] - } - } - }, - components: { - parameters: { - dbosWorkflowUUID: { - name: "dbos-idempotency-key", - in: "header", - required: false, - description: "Caller specified [workflow idempotency key](https://docs.dbos.dev/tutorials/idempotency-tutorial#setting-idempotency-keys)", - schema: { - type: "string" - } - } - }, - schemas: {}, - securitySchemes: { - HelloAuth: { - type: "http", - scheme: "bearer" - } - } - } - }; - - const program = makeTestTypescriptProgram(source); - const parser = new TypeParser(program); - const classes = parser.parse(); - expect(parser.diags.length).toBe(0); - const generator = new OpenApiGenerator(program); - const openApi = generator.generate(classes!, "dbos-hello", "0.0.1"); - expect(generator.diags.length).toBe(0); - expect(openApi).toBeDefined(); - expect(openApi).toMatchObject(expected); - }); - - it("OpenApiSecurityScheme empty RequiredRole array", () => { - const source = /*javascript*/` - import { TransactionContext, Transaction, GetApi, ArgSource, ArgSources, OpenApiSecurityScheme, DefaultRequiredRole, RequiredRole } from '@dbos-inc/dbos-sdk' - - @DefaultRequiredRole(['user']) - @OpenApiSecurityScheme({ type: 'http', scheme: 'bearer' }) - export class Hello { - @GetApi('/greeting/:user') - @RequiredRole([]) - static async helloTransaction(ctxt: HandlerContext, @ArgSource(ArgSources.URL) user: string): Promise { - return ""; - } - } - `; - - const expected = { - openapi: "3.0.3", - info: { - title: "dbos-hello", - version: "0.0.1" - }, - paths: { - "/greeting/{user}": { - get: { - operationId: "helloTransaction", - responses: { - 200: { - description: "Ok", - content: { - "application/json": { - schema: { - type: "string" - } - } - } - } - }, - parameters: [ - { - name: "user", - in: "path", - required: true, - schema: { - type: "string" - } - }, - { - $ref: "#/components/parameters/dbosWorkflowUUID" - } - ] - } - } - }, - components: { - parameters: { - dbosWorkflowUUID: { - name: "dbos-idempotency-key", - in: "header", - required: false, - description: "Caller specified [workflow idempotency key](https://docs.dbos.dev/tutorials/idempotency-tutorial#setting-idempotency-keys)", - schema: { - type: "string" - } - } - }, - schemas: {}, - securitySchemes: { - HelloAuth: { - type: "http", - scheme: "bearer" - } - } - } - }; - - const program = makeTestTypescriptProgram(source); - const parser = new TypeParser(program); - const classes = parser.parse(); - expect(parser.diags.length).toBe(0); - const generator = new OpenApiGenerator(program); - const openApi = generator.generate(classes!, "dbos-hello", "0.0.1"); - expect(generator.diags.length).toBe(0); - expect(openApi).toBeDefined(); - expect(openApi).toMatchObject(expected); - }) -}); diff --git a/packages/dbos-openapi/tsDiagUtil.ts b/packages/dbos-openapi/tsDiagUtil.ts deleted file mode 100644 index d1d71c93d..000000000 --- a/packages/dbos-openapi/tsDiagUtil.ts +++ /dev/null @@ -1,56 +0,0 @@ -import * as ts from 'typescript'; - -const printer = ts.createPrinter(); - -export interface DiagnosticOptions { - code?: number; - node?: ts.Node; - category?: ts.DiagnosticCategory; -} - -export function createDiagnostic(messageText: string, options?: DiagnosticOptions): ts.Diagnostic { - const node = options?.node; - const category = options?.category ?? ts.DiagnosticCategory.Error; - const code = options?.code ?? 0; - - return { - category, - code, - file: node?.getSourceFile(), - length: node?.getWidth(), - messageText, - start: node?.getStart(), - source: node ? printer.printNode(ts.EmitHint.Unspecified, node, node.getSourceFile()) : undefined, - }; -} - -export function diagResult(value: T, diags: readonly ts.Diagnostic[]): T | undefined { - return diags.some(e => e.category === ts.DiagnosticCategory.Error) ? undefined : value; -} - -export function logDiagnostics(diags: readonly ts.Diagnostic[]): void { - if (diags.length > 0) { - const formatHost: ts.FormatDiagnosticsHost = { - getCurrentDirectory: () => ts.sys.getCurrentDirectory(), - getNewLine: () => ts.sys.newLine, - getCanonicalFileName: (fileName: string) => ts.sys.useCaseSensitiveFileNames - ? fileName : fileName.toLowerCase() - } - - const text = ts.formatDiagnosticsWithColorAndContext(diags, formatHost); - console.log(text); - } -} - -export class DiagnosticsCollector { - readonly #diags = new Array(); - get diags() { return this.#diags as readonly ts.Diagnostic[]; } - - raise(message: string, node?: ts.Node): void { - this.#diags.push(createDiagnostic(message, { node })); - } - - warn(message: string, node?: ts.Node): void { - this.#diags.push(createDiagnostic(message, { node, category: ts.DiagnosticCategory.Warning })); - } -} diff --git a/packages/dbos-openapi/tsconfig.json b/packages/dbos-openapi/tsconfig.json deleted file mode 100644 index 52dda4089..000000000 --- a/packages/dbos-openapi/tsconfig.json +++ /dev/null @@ -1,33 +0,0 @@ -/* Visit https://aka.ms/tsconfig to read more about this file */ -{ - "compilerOptions": { - "target": "esnext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - "module": "Node16", /* Specify what module code is generated. */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - "declarationMap": true, /* Create sourcemaps for d.ts files. */ - "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ - "newLine": "lf", /* Set the newline character for emitting files. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - "strict": true, /* Enable all strict type-checking options. */ - "skipLibCheck": true, /* Skip type checking all .d.ts files. */ - "paths": { - "../../src/telemetry/*": ["../../src/telemetry/*"], - "../../src/utils": ["../../src/utils"], - "../../src/user_database": ["../../src/user_database"], - "../../src/system_database": ["../../src/system_database"], - "../../src/dbos-runtime/*": ["../../src/dbos-runtime/*"], - "../../src/httpServer/*": ["../../src/httpServer/*"] - } - }, - "include": [ /* Specifies an array of filenames or patterns to include in the program. */ - ".", - "applications" - ], - "exclude": [ - "dist" - ] -} diff --git a/src/httpServer/middleware.ts b/src/httpServer/middleware.ts index e192d5c46..16a1df4b9 100644 --- a/src/httpServer/middleware.ts +++ b/src/httpServer/middleware.ts @@ -13,7 +13,6 @@ import { HTTPRequest } from "../context"; import { Span } from "@opentelemetry/sdk-trace-base"; import { W3CTraceContextPropagator } from "@opentelemetry/core"; import { trace, defaultTextMapGetter, ROOT_CONTEXT, SpanStatusCode } from "@opentelemetry/api"; -import { OpenAPIV3 as OpenApi3 } from "openapi-types"; import { v4 as uuidv4 } from "uuid"; import { DBOSJSON } from "../utils"; @@ -145,17 +144,9 @@ export function KoaGlobalMiddleware(...koaMiddleware: Koa.Middleware[]) { /* OPEN API DECORATORS */ ///////////////////////////////// -// Note, OAuth2 is not supported yet. -type SecurityScheme = Exclude; - -/** - * Declare an OpenApi Security Scheme (https://spec.openapis.org/oas/v3.0.3#security-scheme-object - * for the methods of a class. Note, this decorator is only used in OpenApi generation and does not - * affect runtime behavior of the app. - */ // eslint-disable-next-line @typescript-eslint/no-unused-vars -export function OpenApiSecurityScheme(securityScheme: SecurityScheme) { - return function (_ctor: T) {}; +export function OpenApiSecurityScheme(securityScheme: unknown) { + throw new Error("DBOS no longer supports OpenAPI generation as of DBOSv2.0.") } ///////////////////////////////// diff --git a/tests/dbos-runtime/runtime.test.ts b/tests/dbos-runtime/runtime.test.ts index 3bb20b094..e52da14b3 100644 --- a/tests/dbos-runtime/runtime.test.ts +++ b/tests/dbos-runtime/runtime.test.ts @@ -3,7 +3,6 @@ import { spawn, execSync, ChildProcess } from "child_process"; import { Writable } from "stream"; import { Client } from "pg"; import { generateDBOSTestConfig, setUpDBOSTestDb } from "../helpers"; -import fs from "fs"; import { HealthUrl } from "../../src/httpServer/server"; async function waitForMessageTest(command: ChildProcess, port: string, adminPort?: string) { @@ -66,15 +65,14 @@ async function dropHelloSystemDB() { database: "hello", }); await pgSystemClient.connect(); - await pgSystemClient.query(`DROP DATABASE IF EXISTS hello_dbos_sys;`); - await pgSystemClient.query(`DROP DATABASE IF EXISTS hello_typeorm_dbos_sys;`); - await pgSystemClient.query(`DROP DATABASE IF EXISTS hello_prisma_dbos_sys;`); - await pgSystemClient.query(`DROP DATABASE IF EXISTS hello_drizzle_dbos_sys;`); - await pgSystemClient.query(`DROP DATABASE IF EXISTS hello_express_dbos_sys;`); - await pgSystemClient.query(`DROP DATABASE IF EXISTS hello_typeorm;`); - await pgSystemClient.query(`DROP DATABASE IF EXISTS hello_prisma;`); - await pgSystemClient.query(`DROP DATABASE IF EXISTS hello_drizzle;`); - await pgSystemClient.query(`DROP DATABASE IF EXISTS hello_express;`); + await pgSystemClient.query(`DROP DATABASE IF EXISTS dbos_typeorm_dbos_sys;`); + await pgSystemClient.query(`DROP DATABASE IF EXISTS dbos_prisma_dbos_sys;`); + await pgSystemClient.query(`DROP DATABASE IF EXISTS dbos_drizzle_dbos_sys;`); + await pgSystemClient.query(`DROP DATABASE IF EXISTS dbos_knex_dbos_sys;`); + await pgSystemClient.query(`DROP DATABASE IF EXISTS dbos_typeorm;`); + await pgSystemClient.query(`DROP DATABASE IF EXISTS dbos_prisma;`); + await pgSystemClient.query(`DROP DATABASE IF EXISTS dbos_drizzle;`); + await pgSystemClient.query(`DROP DATABASE IF EXISTS dbos_knex;`); await pgSystemClient.end(); } @@ -87,54 +85,10 @@ function configureHelloExample() { execSync("npx dbos migrate", { env: process.env }); } -describe("runtime-entrypoint-tests", () => { +describe("runtime-tests-knex", () => { beforeAll(async () => { await dropHelloSystemDB(); - - process.chdir("packages/create/templates/hello-contexts"); - execSync("mv src/operations.ts src/entrypoint.ts"); - configureHelloExample(); - }); - - afterAll(() => { - execSync("mv src/entrypoint.ts src/operations.ts"); - process.chdir("../../../.."); - }); - - test("runtime-hello using entrypoint runtimeConfig", async () => { - const mockDBOSConfigYamlString = ` -database: - hostname: 'localhost' - port: 5432 - username: 'postgres' - password: \${PGPASSWORD} - connectionTimeoutMillis: 3000 - app_db_client: 'knex' -runtimeConfig: - entrypoints: - - dist/entrypoint.js -`; - const filePath = "dbos-config.yaml"; - fs.copyFileSync(filePath, `${filePath}.bak`); - fs.writeFileSync(filePath, mockDBOSConfigYamlString, "utf-8"); - - try { - const command = spawn("node_modules/@dbos-inc/dbos-sdk/dist/src/dbos-runtime/cli.js", ["start", "--port", "1234"], { - env: process.env, - }); - await waitForMessageTest(command, "1234"); - } finally { - fs.copyFileSync(`${filePath}.bak`, filePath); - fs.unlinkSync(`${filePath}.bak`); - } - }); -}); - -describe("runtime-tests", () => { - beforeAll(async () => { - await dropHelloSystemDB(); - - process.chdir("packages/create/templates/hello-contexts"); + process.chdir("packages/create/templates/dbos-knex"); configureHelloExample(); }); @@ -142,71 +96,23 @@ describe("runtime-tests", () => { process.chdir("../../../.."); }); - test("runtime-hello-jest", () => { - execSync("npm run test", { env: process.env }); // Make sure the hello example passes its own tests. + test("test hello-knex tests", () => { + execSync("npm run test", { env: process.env }); // Make sure hello-knex passes its own tests. execSync("npm run lint", { env: process.env }); // Pass linter rules. }); - // Attention! this test relies on example/hello/dbos-config.yaml not declaring a port! - test("runtime-hello using default runtime configuration", async () => { - const command = spawn("node_modules/@dbos-inc/dbos-sdk/dist/src/dbos-runtime/cli.js", ["start"], { + test("test hello-knex runtime", async () => { + const command = spawn("node", ["dist/main.js"], { env: process.env, }); await waitForMessageTest(command, "3000"); }); - - test("runtime hello with port provided as CLI parameter", async () => { - const command = spawn("node_modules/@dbos-inc/dbos-sdk/dist/src/dbos-runtime/cli.js", ["start", "--port", "1234"], { - env: process.env, - }); - await waitForMessageTest(command, "1234"); - }); - - test("runtime hello with appDir provided as CLI parameter", async () => { - process.chdir("../../../.."); - try { - const command = spawn("dist/src/dbos-runtime/cli.js", ["start", "--appDir", "packages/create/templates/hello-contexts"], { - env: process.env, - }); - await waitForMessageTest(command, "3000"); - } finally { - process.chdir("packages/create/templates/hello-contexts"); - } - }); - - test("runtime hello with ports provided in configuration file", async () => { - const mockDBOSConfigYamlString = ` -database: - hostname: 'localhost' - port: 5432 - username: 'postgres' - password: \${PGPASSWORD} - connectionTimeoutMillis: 3000 - app_db_client: 'knex' -runtimeConfig: - port: 6666 - admin_port: 6789 -`; - const filePath = "dbos-config.yaml"; - fs.copyFileSync(filePath, `${filePath}.bak`); - fs.writeFileSync(filePath, mockDBOSConfigYamlString, "utf-8"); - - try { - const command = spawn("node_modules/@dbos-inc/dbos-sdk/dist/src/dbos-runtime/cli.js", ["start"], { - env: process.env, - }); - await waitForMessageTest(command, "6666", "6789"); - } finally { - fs.copyFileSync(`${filePath}.bak`, filePath); - fs.unlinkSync(`${filePath}.bak`); - } - }); }); describe("runtime-tests-typeorm", () => { beforeAll(async () => { await dropHelloSystemDB(); - process.chdir("packages/create/templates/hello-typeorm"); + process.chdir("packages/create/templates/dbos-typeorm"); configureHelloExample(); }); @@ -219,7 +125,6 @@ describe("runtime-tests-typeorm", () => { execSync("npm run lint", { env: process.env }); // Pass linter rules. }); - // Attention! this test relies on example/hello/dbos-config.yaml not declaring a port! test("test hello-typeorm runtime", async () => { const command = spawn("node_modules/@dbos-inc/dbos-sdk/dist/src/dbos-runtime/cli.js", ["start"], { env: process.env, @@ -231,7 +136,7 @@ describe("runtime-tests-typeorm", () => { describe("runtime-tests-prisma", () => { beforeAll(async () => { await dropHelloSystemDB(); - process.chdir("packages/create/templates/hello-prisma"); + process.chdir("packages/create/templates/dbos-prisma"); configureHelloExample(); }); @@ -244,7 +149,6 @@ describe("runtime-tests-prisma", () => { execSync("npm run lint", { env: process.env }); // Pass linter rules. }); - // Attention! this test relies on example/hello/dbos-config.yaml not declaring a port! test("test hello-prisma runtime", async () => { const command = spawn("node_modules/@dbos-inc/dbos-sdk/dist/src/dbos-runtime/cli.js", ["start"], { env: process.env, @@ -256,7 +160,7 @@ describe("runtime-tests-prisma", () => { describe("runtime-tests-drizzle", () => { beforeAll(async () => { await dropHelloSystemDB(); - process.chdir("packages/create/templates/hello-drizzle"); + process.chdir("packages/create/templates/dbos-drizzle"); configureHelloExample(); }); @@ -265,12 +169,10 @@ describe("runtime-tests-drizzle", () => { }); test("test hello-drizzle tests", () => { - execSync("npm run test", { env: process.env }); // Make sure hello-typeorm passes its own tests. - console.log("linting hello-drizzle"); - execSync("npm run lint", { env: process.env, stdio: 'inherit'}); // Pass linter rules. + execSync("npm run test", { env: process.env }); // Make sure hello-drizzle passes its own tests. + execSync("npm run lint", { env: process.env, stdio: 'inherit' }); // Pass linter rules. }); - // Attention! this test relies on example/hello/dbos-config.yaml not declaring a port! test("test hello-drizzle runtime", async () => { const command = spawn("node_modules/@dbos-inc/dbos-sdk/dist/src/dbos-runtime/cli.js", ["start"], { env: process.env, @@ -278,26 +180,3 @@ describe("runtime-tests-drizzle", () => { await waitForMessageTest(command, "3000"); }); }); - -describe("runtime-tests-express", () => { - beforeAll(async () => { - await dropHelloSystemDB(); - process.chdir("packages/create/templates/hello-express"); - configureHelloExample(); - }); - - afterAll(() => { - process.chdir("../../../.."); - }); - - test("test hello-express tests", () => { - execSync("npm run test", { env: process.env }); // Make sure hello-express passes its own tests. - }); - - test("test hello-express runtime", async () => { - const command = spawn("node_modules/@dbos-inc/dbos-sdk/dist/src/dbos-runtime/cli.js", ["start"], { - env: process.env, - }); - await waitForMessageTest(command, "3000"); - }); -});