create-strata 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +54 -0
  2. package/dist/cli.js +4222 -0
  3. package/dist/templates/.env.example +22 -0
  4. package/dist/templates/docker-compose.yml +14 -0
  5. package/dist/templates/overlays/api/docs/API.md +49 -0
  6. package/dist/templates/overlays/server-htmx/public/assets/app.css +89 -0
  7. package/dist/templates/overlays/server-htmx/resources/views/errors/error.eta +14 -0
  8. package/dist/templates/overlays/server-htmx/resources/views/errors/forbidden.eta +4 -0
  9. package/dist/templates/overlays/server-htmx/resources/views/errors/not-found.eta +4 -0
  10. package/dist/templates/overlays/server-htmx/resources/views/layouts/app.eta +34 -0
  11. package/dist/templates/overlays/server-htmx/resources/views/organizations/_table.eta +23 -0
  12. package/dist/templates/overlays/server-htmx/resources/views/organizations/index.eta +37 -0
  13. package/dist/templates/overlays/server-htmx/resources/views/pages/home.eta +4 -0
  14. package/dist/templates/overlays/server-htmx/resources/views/partials/_flash.eta +3 -0
  15. package/dist/templates/overlays/spa-react/frontend/build.ts +17 -0
  16. package/dist/templates/overlays/spa-react/frontend/bun-env.d.ts +4 -0
  17. package/dist/templates/overlays/spa-react/frontend/bun.lock +51 -0
  18. package/dist/templates/overlays/spa-react/frontend/dev-server.ts +66 -0
  19. package/dist/templates/overlays/spa-react/frontend/index.html +12 -0
  20. package/dist/templates/overlays/spa-react/frontend/package.json +21 -0
  21. package/dist/templates/overlays/spa-react/frontend/src/App.tsx +59 -0
  22. package/dist/templates/overlays/spa-react/frontend/src/api/client.ts +86 -0
  23. package/dist/templates/overlays/spa-react/frontend/src/app.css +98 -0
  24. package/dist/templates/overlays/spa-react/frontend/src/auth/AuthContext.tsx +103 -0
  25. package/dist/templates/overlays/spa-react/frontend/src/auth/tokenStorage.ts +15 -0
  26. package/dist/templates/overlays/spa-react/frontend/src/main.tsx +22 -0
  27. package/dist/templates/overlays/spa-react/frontend/src/pages/HomePage.tsx +72 -0
  28. package/dist/templates/overlays/spa-react/frontend/src/pages/LoginPage.tsx +70 -0
  29. package/dist/templates/overlays/spa-react/frontend/src/pages/NotFoundPage.tsx +12 -0
  30. package/dist/templates/overlays/spa-react/frontend/tsconfig.json +17 -0
  31. package/dist/templates/package.json +22 -0
  32. package/dist/templates/public/assets/site.css +29 -0
  33. package/dist/templates/src/bootstrap/config.ts +29 -0
  34. package/dist/templates/src/bootstrap/createApp.ts +94 -0
  35. package/dist/templates/src/bootstrap/database.ts +30 -0
  36. package/dist/templates/src/bootstrap/preload.ts +5 -0
  37. package/dist/templates/src/bootstrap/providers/auth.ts +41 -0
  38. package/dist/templates/src/bootstrap/providers/cache.ts +28 -0
  39. package/dist/templates/src/bootstrap/providers/config.ts +27 -0
  40. package/dist/templates/src/bootstrap/providers/index.ts +14 -0
  41. package/dist/templates/src/bootstrap/providers/storage.ts +11 -0
  42. package/dist/templates/src/bootstrap/server.ts +25 -0
  43. package/dist/templates/src/db/fresh.ts +19 -0
  44. package/dist/templates/src/db/migrate.ts +35 -0
  45. package/dist/templates/src/lib/view.ts +21 -0
  46. package/dist/templates/src/modules/site/index.ts +29 -0
  47. package/dist/templates/src/routes.ts +6 -0
  48. package/dist/templates/strata.config.ts +7 -0
  49. package/dist/templates/tsconfig.json +14 -0
  50. package/dist/templates/views/home.eta +5 -0
  51. package/dist/templates/views/layouts/app.eta +18 -0
  52. package/package.json +29 -0
@@ -0,0 +1,27 @@
1
+ import {
2
+ APP_PORT_CONFIG_KEY,
3
+ CORE_CONFIG_TOKEN,
4
+ DATABASE_URL_CONFIG_KEY,
5
+ REDIS_URL_CONFIG_KEY,
6
+ } from "@getstrata/bootstrap/config";
7
+ import type { ServiceProvider } from "@getstrata/core/contracts/di";
8
+ import { loadConfig } from "../config.ts";
9
+
10
+ const configProvider: ServiceProvider = {
11
+ name: "starter.config",
12
+ register({ container, config }) {
13
+ const appConfig = loadConfig();
14
+
15
+ container.set(CORE_CONFIG_TOKEN, config);
16
+ config.set(DATABASE_URL_CONFIG_KEY, appConfig.databaseUrl);
17
+ config.set(APP_PORT_CONFIG_KEY, appConfig.port);
18
+ config.set(REDIS_URL_CONFIG_KEY, process.env.REDIS_URL ?? "");
19
+ config.set("app.url", appConfig.appUrl);
20
+ config.set("cache.driver", "array");
21
+ config.set("cache.ttlMs", 3_600_000);
22
+ config.set("cache.maxEntries", 100);
23
+ config.set("queue.driver", process.env.QUEUE_DRIVER ?? "sync");
24
+ },
25
+ };
26
+
27
+ export default configProvider;
@@ -0,0 +1,14 @@
1
+ import type { ServiceProvider } from "@getstrata/core/contracts/di";
2
+ import authProvider from "./auth.ts";
3
+ import cacheProvider from "./cache.ts";
4
+ import configProvider from "./config.ts";
5
+ import storageProvider from "./storage.ts";
6
+
7
+ const starterProviders: ServiceProvider[] = [
8
+ configProvider,
9
+ cacheProvider,
10
+ storageProvider,
11
+ authProvider,
12
+ ];
13
+
14
+ export { starterProviders };
@@ -0,0 +1,11 @@
1
+ import type { ServiceProvider } from "@getstrata/core/contracts/di";
2
+ import { createStorageDriver, StorageManager } from "@getstrata/core/storage/storage";
3
+
4
+ const storageProvider: ServiceProvider = {
5
+ name: "starter.storage",
6
+ register({ dependencies }) {
7
+ Reflect.set(dependencies, "storage", new StorageManager(createStorageDriver()));
8
+ },
9
+ };
10
+
11
+ export default storageProvider;
@@ -0,0 +1,25 @@
1
+ import "./preload.ts";
2
+ import { ensureModulesLoaded } from "@getstrata/bootstrap/discoverModules";
3
+ import { bootstrapApp, createAppServer } from "./createApp.ts";
4
+ import { closeDatabase, pingDatabase } from "./database.ts";
5
+
6
+ await ensureModulesLoaded();
7
+
8
+ const { routes, config } = await bootstrapApp();
9
+
10
+ const server = createAppServer(routes, config.port);
11
+
12
+ console.log(`Listening on http://localhost:${server.port} (APP_URL ${config.appUrl})`);
13
+
14
+ if (!(await pingDatabase())) {
15
+ console.warn("Warning: database ping failed.");
16
+ }
17
+
18
+ async function shutdown() {
19
+ await closeDatabase();
20
+ server.stop();
21
+ process.exit(0);
22
+ }
23
+
24
+ process.on("SIGINT", shutdown);
25
+ process.on("SIGTERM", shutdown);
@@ -0,0 +1,19 @@
1
+ import { getSql } from "../bootstrap/database.ts";
2
+ import { migrate, seed } from "./migrate.ts";
3
+
4
+ const tables = ["notes"];
5
+
6
+ export async function fresh() {
7
+ const sql = getSql();
8
+ for (const table of tables) {
9
+ await sql.unsafe(`DROP TABLE IF EXISTS ${table} CASCADE`);
10
+ }
11
+ await migrate();
12
+ await seed();
13
+ }
14
+
15
+ if (import.meta.main) {
16
+ await fresh();
17
+ console.log("Database reset, migrated, and seeded.");
18
+ process.exit(0);
19
+ }
@@ -0,0 +1,35 @@
1
+ import { getSql } from "../bootstrap/database.ts";
2
+
3
+ const migrations = [
4
+ `CREATE TABLE IF NOT EXISTS notes (
5
+ id SERIAL PRIMARY KEY,
6
+ body TEXT NOT NULL,
7
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
8
+ )`,
9
+ ];
10
+
11
+ export async function migrate() {
12
+ const sql = getSql();
13
+ for (const statement of migrations) {
14
+ await sql.unsafe(statement);
15
+ }
16
+ }
17
+
18
+ export async function seed() {
19
+ const sql = getSql();
20
+ const [{ count }] = await sql<{ count: string }[]>`
21
+ SELECT COUNT(*)::text AS count FROM notes
22
+ `;
23
+ if (Number(count) > 0) return;
24
+
25
+ await sql`
26
+ INSERT INTO notes (body) VALUES ('Welcome to Strata!')
27
+ `;
28
+ }
29
+
30
+ if (import.meta.main) {
31
+ await migrate();
32
+ await seed();
33
+ console.log("Database migrated and seeded.");
34
+ process.exit(0);
35
+ }
@@ -0,0 +1,21 @@
1
+ import { join } from "node:path";
2
+ import { EtaViewEngine, htmlResponse } from "@getstrata/core/view";
3
+
4
+ const engine = new EtaViewEngine(join(import.meta.dir, "../../views"));
5
+
6
+ export interface LayoutData {
7
+ title: string;
8
+ description?: string;
9
+ }
10
+
11
+ export async function renderPage(
12
+ template: string,
13
+ data: Record<string, unknown> & { layout: LayoutData },
14
+ ): Promise<Response> {
15
+ const html = await engine.render(template, data);
16
+ return htmlResponse(html);
17
+ }
18
+
19
+ export function plainText(body: string, status = 200): Response {
20
+ return new Response(body, { status, headers: { "content-type": "text/plain; charset=utf-8" } });
21
+ }
@@ -0,0 +1,29 @@
1
+ import type { AppModule } from "@getstrata/bootstrap/contracts";
2
+ import { withErrorHandling } from "@getstrata/core/http/response";
3
+ import { pingDatabase } from "../../bootstrap/database.ts";
4
+ import { plainText, renderPage } from "../../lib/view.ts";
5
+
6
+ const siteModule: AppModule = {
7
+ name: "site",
8
+ order: 1,
9
+ webRoutes({ kernel }) {
10
+ return {
11
+ "/": kernel.wrapWeb(async () =>
12
+ renderPage("home.eta", {
13
+ layout: {
14
+ title: "Home",
15
+ description: "A new Strata application",
16
+ },
17
+ }),
18
+ ),
19
+ "/health": kernel.wrapWeb(
20
+ withErrorHandling(async () => {
21
+ const dbOk = await pingDatabase();
22
+ return plainText(dbOk ? "ok" : "degraded", dbOk ? 200 : 503);
23
+ }),
24
+ ),
25
+ };
26
+ },
27
+ };
28
+
29
+ export default siteModule;
@@ -0,0 +1,6 @@
1
+ import { buildWebModuleRoutes } from "@getstrata/bootstrap/buildWebModuleRoutes";
2
+ import type { AppDependencies, AppRouteMap } from "@getstrata/bootstrap/contracts";
3
+
4
+ export function buildRoutes(dependencies: AppDependencies): AppRouteMap {
5
+ return buildWebModuleRoutes(dependencies);
6
+ }
@@ -0,0 +1,7 @@
1
+ export default {
2
+ preload: "./src/bootstrap/preload.ts",
3
+ server: "./src/bootstrap/server.ts",
4
+ modulesDirectory: "./src/modules",
5
+ migrate: "./src/db/migrate.ts",
6
+ fresh: "./src/db/fresh.ts",
7
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "skipLibCheck": true,
8
+ "noEmit": true,
9
+ "allowImportingTsExtensions": true,
10
+ "types": ["bun"],
11
+ "lib": ["ES2022", "DOM"]
12
+ },
13
+ "include": ["src/**/*.ts"]
14
+ }
@@ -0,0 +1,5 @@
1
+ <section class="section">
2
+ <h1>Welcome to Strata</h1>
3
+ <p>Your app <strong>{{PROJECT_NAME}}</strong> is running.</p>
4
+ <p>Edit <code>src/routes.ts</code> and <code>views/home.eta</code> to get started.</p>
5
+ </section>
@@ -0,0 +1,18 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title><%= it.layout.title %> · Strata</title>
7
+ <% if (it.layout.description) { %>
8
+ <meta name="description" content="<%= it.layout.description %>" />
9
+ <% } %>
10
+ <link rel="stylesheet" href="/assets/site.css" />
11
+ </head>
12
+ <body>
13
+ <header class="site-header">
14
+ <a class="brand" href="/">Strata</a>
15
+ </header>
16
+ <main><%~ it.body %></main>
17
+ </body>
18
+ </html>
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "create-strata",
3
+ "version": "1.0.1",
4
+ "description": "Create a new Strata app. Interactive wizard; choose each layer; one database engine.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/EyK-26/strata.git",
10
+ "directory": "packages/create-strata"
11
+ },
12
+ "bin": {
13
+ "create-strata": "./dist/cli.js"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "bun build ../strata-starter/cli.ts --outdir dist --target bun && rm -rf dist/templates && mkdir -p dist/templates/overlays && cp -r ../strata-starter/templates/. dist/templates && cp -r ../../templates/scaffold/. dist/templates/overlays",
21
+ "prepublishOnly": "bun run build"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "engines": {
27
+ "bun": ">=1.4.0"
28
+ }
29
+ }