@getstrata/starter 1.0.8 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1702,11 +1702,13 @@ function renderSiteModule(_layers) {
1702
1702
  import { withErrorHandling } from "@getstrata/core/http/response";
1703
1703
  import { getSql, pingDatabase } from "../../bootstrap/database.ts";
1704
1704
  import { plainText, renderPage } from "../../lib/view.ts";
1705
+ import { Note } from "../../models/Note.ts";
1705
1706
 
1706
1707
  // Proves the database answers and the schema is migrated. Point it at a table your app owns.
1707
1708
  async function schemaReady(): Promise<boolean> {
1708
1709
  try {
1709
- await getSql().unsafe("SELECT 1 FROM notes LIMIT 1");
1710
+ getSql();
1711
+ await Note.query().value("id");
1710
1712
  return true;
1711
1713
  } catch {
1712
1714
  return false;
@@ -2529,9 +2531,9 @@ function renderPackageJson(projectName, options = {}) {
2529
2531
  "@getstrata/cli": "workspace:*",
2530
2532
  "@getstrata/core": "workspace:*"
2531
2533
  } : {
2532
- "@getstrata/bootstrap": "^1.0.8",
2533
- "@getstrata/cli": "^1.0.8",
2534
- "@getstrata/core": "^1.0.8"
2534
+ "@getstrata/bootstrap": "^1.0.9",
2535
+ "@getstrata/cli": "^1.0.9",
2536
+ "@getstrata/core": "^1.0.9"
2535
2537
  };
2536
2538
  coreDeps.eta = "^4.6.0";
2537
2539
  if (options.layers?.database === "mysql") {
@@ -2666,7 +2668,7 @@ Create a module under \`src/modules/\` and return a route map. Modules are disco
2666
2668
  // src/modules/notes/index.ts
2667
2669
  import type { AppModule } from "@getstrata/bootstrap/contracts";
2668
2670
  import { jsonResponse } from "@getstrata/core/http/response";
2669
- import { getSql } from "../../bootstrap/database.ts";
2671
+ import { Note } from "../../models/Note.ts";
2670
2672
 
2671
2673
  const notesModule: AppModule = {
2672
2674
  name: "notes",
@@ -2674,10 +2676,8 @@ const notesModule: AppModule = {
2674
2676
  routes({ kernel }) {
2675
2677
  return {
2676
2678
  "/api/v1/notes": kernel.wrap("api", async () => {
2677
- const rows = await getSql().unsafe<{ id: number; body: string }>(
2678
- "SELECT id, body FROM notes ORDER BY id DESC",
2679
- );
2680
- return jsonResponse({ data: rows });
2679
+ const bodiesById = await Note.query().orderBy({ id: "desc" }).pluck("body", "id");
2680
+ return jsonResponse({ data: Object.fromEntries(bodiesById) });
2681
2681
  }),
2682
2682
  };
2683
2683
  },
@@ -2695,7 +2695,25 @@ Import from \`@getstrata/core/...\` subpaths rather than the package root, so si
2695
2695
  - [Databases](https://github.com/EyK-26/strata/blob/main/docs/DATABASE.md)
2696
2696
  `;
2697
2697
  }
2698
- function renderReadme(projectName, layers) {
2698
+ function renderReadmeIntro(projectName, options) {
2699
+ if (options.dogfood) {
2700
+ return `# ${projectName}
2701
+
2702
+ This in-repo app is Strata dogfood for internal end-to-end testing (migrate, seed, boot, OpenAPI, smoke). It is not a product. Start a product app with \`bunx create-strata\`.
2703
+ `;
2704
+ }
2705
+ if (options.inRepoExample) {
2706
+ return `# ${projectName}
2707
+
2708
+ This in-repo app is a generated sibling layer map. It is not a product and it is not CI dogfood. CI dogfood is \`apps/hiroapp\`. Start a product app with \`bunx create-strata\`.
2709
+ `;
2710
+ }
2711
+ return `# ${projectName}
2712
+
2713
+ Strata app generated by \`create-strata\`.
2714
+ `;
2715
+ }
2716
+ function renderReadme(projectName, layers, options = {}) {
2699
2717
  const docker = renderDockerCompose(projectName, layers);
2700
2718
  const next = [`cd ${projectName}`, "cp .env.example .env"];
2701
2719
  if (docker) {
@@ -2710,10 +2728,7 @@ function renderReadme(projectName, layers) {
2710
2728
  const dockerServices = selectedDockerServices(layers);
2711
2729
  const neededTools = neededDockerServices(layers);
2712
2730
  const dockerLabel = neededTools.length === 0 ? "not needed" : dockerServices.length > 0 ? dockerServices.join(", ") : "off (local installs)";
2713
- return `# ${projectName}
2714
-
2715
- Strata app generated by \`create-strata\`.
2716
-
2731
+ return `${renderReadmeIntro(projectName, options)}
2717
2732
  ## Layers
2718
2733
 
2719
2734
  | Layer | Choice |
@@ -3089,7 +3104,6 @@ function renderMigrateTs(layers) {
3089
3104
  const list = statements.map((sql) => ` \`${sql}\`,`).join(`
3090
3105
  `);
3091
3106
  const ph = layers.database === "postgres";
3092
- const notePlaceholder = ph ? "$1" : "?";
3093
3107
  const verifyOn = layers.extras.emailVerification && authNeedsUsers(layers.auth);
3094
3108
  const userColumns = verifyOn ? "name, email, password, is_admin, email_verified_at" : "name, email, password, is_admin";
3095
3109
  const userPlaceholders = verifyOn ? ph ? "$1, $2, $3, $4, $5), ($6, $7, $8, $9, $10" : "?, ?, ?, ?, ?), (?, ?, ?, ?, ?" : ph ? "$1, $2, $3, $4), ($5, $6, $7, $8" : "?, ?, ?, ?), (?, ?, ?, ?";
@@ -3121,21 +3135,19 @@ function renderMigrateTs(layers) {
3121
3135
  const hashImport = authNeedsUsers(layers.auth) ? `import { hashPassword } from "@getstrata/core/auth/password";
3122
3136
  ` : "";
3123
3137
  const seedBlock = `${seedTenant}${seedUsers}`;
3138
+ const bindSql = seedBlock.length > 0 ? ` const sql = getSql();
3139
+ ` : ` getSql();
3140
+ `;
3124
3141
  return `${hashImport}${ensureImport(layers)}import { closeDatabase, getSql } from "../bootstrap/database.ts";
3142
+ import { Note } from "../models/Note.ts";
3125
3143
 
3126
3144
  const migrations = [
3127
3145
  ${list}
3128
3146
  ];
3129
3147
 
3130
3148
  export async function seed() {
3131
- ${ensureCall(layers)} const sql = getSql();
3132
- const [{ count }] = await sql.unsafe<{ count: string | number }>(
3133
- "SELECT COUNT(*) AS count FROM notes",
3134
- );
3135
- if (Number(count) === 0) {
3136
- await sql.unsafe("INSERT INTO notes (body) VALUES (${notePlaceholder})", [
3137
- "Welcome to Strata!",
3138
- ]);
3149
+ ${ensureCall(layers)}${bindSql} if ((await Note.query().value("id")) === null) {
3150
+ await Note.create({ body: "Welcome to Strata!" });
3139
3151
  }${seedBlock}
3140
3152
  }
3141
3153
 
@@ -3160,6 +3172,43 @@ if (import.meta.main) {
3160
3172
  }
3161
3173
  `;
3162
3174
  }
3175
+ function renderNoteModel() {
3176
+ return `import { BaseRepository } from "@getstrata/core/database/baseRepository";
3177
+ import { Model, registerModelRepository } from "@getstrata/core/database/model";
3178
+ import { defineTable } from "@getstrata/core/database/table";
3179
+
3180
+ interface NoteRecord {
3181
+ id: number;
3182
+ body: string;
3183
+ created_at: Date | string;
3184
+ }
3185
+
3186
+ const notesTable = defineTable<NoteRecord, "id">({
3187
+ name: "notes",
3188
+ primaryKey: "id",
3189
+ columns: ["id", "body", "created_at"],
3190
+ defaultOrderBy: { column: "id", direction: "ASC" },
3191
+ });
3192
+
3193
+ class NoteRepository extends BaseRepository<NoteRecord, "id"> {
3194
+ constructor() {
3195
+ super(notesTable);
3196
+ }
3197
+ }
3198
+
3199
+ class Note extends Model<NoteRecord, "id"> {
3200
+ static $fillable = ["body"] as const;
3201
+ // created_at uses the table default. Sending a JS Date from $timestamps
3202
+ // is rejected by SQLite bindings.
3203
+ static $timestamps = false;
3204
+ }
3205
+
3206
+ registerModelRepository(Note, new NoteRepository());
3207
+
3208
+ export type { NoteRecord };
3209
+ export { Note };
3210
+ `;
3211
+ }
3163
3212
  function dropTables(layers) {
3164
3213
  const ordered = [];
3165
3214
  if (authUsesToken(layers.auth)) {
@@ -4064,7 +4113,10 @@ function writeGeneratedFiles(options) {
4064
4113
  writeText(join2(targetDir, ".env.example"), renderEnvExample(projectName, layers));
4065
4114
  writeText(join2(targetDir, ".gitignore"), renderGitignore());
4066
4115
  writeText(join2(targetDir, "package.json"), renderPackageJson(projectName, { ...options, layers }));
4067
- writeText(join2(targetDir, "README.md"), renderReadme(projectName, layers));
4116
+ writeText(join2(targetDir, "README.md"), renderReadme(projectName, layers, {
4117
+ inRepoExample: options.inRepoExample,
4118
+ dogfood: options.dogfood
4119
+ }));
4068
4120
  if (layers.frontend === "api") {
4069
4121
  writeText(join2(targetDir, "docs/API.md"), renderApiDocs(projectName, layers));
4070
4122
  } else {
@@ -4095,6 +4147,7 @@ function writeGeneratedFiles(options) {
4095
4147
  writeText(join2(src, "bootstrap/providers/queue.ts"), renderQueueProvider());
4096
4148
  writeText(join2(src, "bootstrap/providers/index.ts"), renderProvidersIndex());
4097
4149
  writeText(join2(src, "bootstrap/providers/auth.ts"), renderAuthProvider(layers));
4150
+ writeText(join2(src, "models/Note.ts"), renderNoteModel());
4098
4151
  writeText(join2(src, "db/migrate.ts"), renderMigrateTs(layers));
4099
4152
  writeText(join2(src, "db/fresh.ts"), renderFreshTs(layers));
4100
4153
  writeText(join2(src, "db/seed.ts"), renderSeedTs());
@@ -11,9 +11,9 @@
11
11
  "check": "tsc --noEmit"
12
12
  },
13
13
  "dependencies": {
14
- "@getstrata/bootstrap": "^1.0.8",
15
- "@getstrata/cli": "^1.0.8",
16
- "@getstrata/core": "^1.0.8",
14
+ "@getstrata/bootstrap": "^1.0.9",
15
+ "@getstrata/cli": "^1.0.9",
16
+ "@getstrata/core": "^1.0.9",
17
17
  "eta": "^4.6.0"
18
18
  },
19
19
  "devDependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/starter",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Interactive create-strata wizard. Choose each layer; one database engine.",
5
5
  "type": "module",
6
6
  "license": "MIT",