create-projx 1.7.0 → 1.7.2

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 (76) hide show
  1. package/README.md +13 -35
  2. package/dist/{baseline-FHOZNS4D.js → baseline-ZPPJKHBN.js} +2 -2
  3. package/dist/{chunk-HAT7D4G2.js → chunk-FQPOK3QZ.js} +10 -3
  4. package/dist/{chunk-IMZKHDIL.js → chunk-XAYCVTHL.js} +15 -18
  5. package/dist/index.js +166 -385
  6. package/dist/{utils-BZGSJ7XZ.js → utils-MC7VKL2U.js} +1 -1
  7. package/package.json +2 -3
  8. package/src/templates/README.md.ejs +1 -1
  9. package/src/templates/ci.yml.ejs +14 -15
  10. package/src/templates/pre-commit.ejs +13 -1
  11. package/src/addons/orms/drizzle/express/src/app.ts +0 -81
  12. package/src/addons/orms/drizzle/express/src/modules/_base/auto-routes.ts +0 -278
  13. package/src/addons/orms/drizzle/express/src/modules/_base/index.ts +0 -20
  14. package/src/addons/orms/drizzle/express/src/server.ts +0 -32
  15. package/src/addons/orms/drizzle/express/tests/app.test.ts +0 -24
  16. package/src/addons/orms/drizzle/express/vitest.config.ts +0 -20
  17. package/src/addons/orms/drizzle/fastify/src/app.ts +0 -90
  18. package/src/addons/orms/drizzle/fastify/src/modules/_base/auto-routes.ts +0 -268
  19. package/src/addons/orms/drizzle/fastify/src/modules/_base/index.ts +0 -20
  20. package/src/addons/orms/drizzle/fastify/tests/modules/app.test.ts +0 -20
  21. package/src/addons/orms/drizzle/fastify/vitest.config.ts +0 -31
  22. package/src/addons/orms/drizzle/gen-entity/express-router.ts +0 -21
  23. package/src/addons/orms/drizzle/gen-entity/express-test.ts +0 -61
  24. package/src/addons/orms/drizzle/gen-entity/fastify-router.ts +0 -19
  25. package/src/addons/orms/drizzle/gen-entity/fastify-test.ts +0 -87
  26. package/src/addons/orms/drizzle/manifest.json +0 -52
  27. package/src/addons/orms/drizzle/shared/drizzle.config.ts +0 -12
  28. package/src/addons/orms/drizzle/shared/src/db/client.ts +0 -17
  29. package/src/addons/orms/drizzle/shared/src/db/schema.ts +0 -14
  30. package/src/addons/orms/drizzle/shared/src/modules/_base/query-engine.ts +0 -115
  31. package/src/addons/orms/drizzle/shared/src/modules/_base/registry.ts +0 -15
  32. package/src/addons/orms/sequelize/express/src/app.ts +0 -82
  33. package/src/addons/orms/sequelize/express/src/modules/_base/auto-routes.ts +0 -226
  34. package/src/addons/orms/sequelize/express/src/modules/_base/index.ts +0 -20
  35. package/src/addons/orms/sequelize/express/src/server.ts +0 -32
  36. package/src/addons/orms/sequelize/express/tests/app.test.ts +0 -24
  37. package/src/addons/orms/sequelize/express/vitest.config.ts +0 -20
  38. package/src/addons/orms/sequelize/fastify/src/app.ts +0 -83
  39. package/src/addons/orms/sequelize/fastify/src/modules/_base/auto-routes.ts +0 -216
  40. package/src/addons/orms/sequelize/fastify/src/modules/_base/index.ts +0 -20
  41. package/src/addons/orms/sequelize/fastify/tests/modules/app.test.ts +0 -20
  42. package/src/addons/orms/sequelize/fastify/vitest.config.ts +0 -31
  43. package/src/addons/orms/sequelize/gen-entity/express-router.ts +0 -17
  44. package/src/addons/orms/sequelize/gen-entity/express-test.ts +0 -65
  45. package/src/addons/orms/sequelize/gen-entity/fastify-router.ts +0 -19
  46. package/src/addons/orms/sequelize/gen-entity/fastify-test.ts +0 -89
  47. package/src/addons/orms/sequelize/gen-entity/model.ts +0 -21
  48. package/src/addons/orms/sequelize/manifest.json +0 -53
  49. package/src/addons/orms/sequelize/shared/scripts/db-sync.ts +0 -14
  50. package/src/addons/orms/sequelize/shared/src/db/client.ts +0 -19
  51. package/src/addons/orms/sequelize/shared/src/models/index.ts +0 -9
  52. package/src/addons/orms/sequelize/shared/src/modules/_base/query-engine.ts +0 -101
  53. package/src/addons/orms/sequelize/shared/src/modules/_base/registry.ts +0 -15
  54. package/src/addons/orms/typeorm/express/src/app.ts +0 -82
  55. package/src/addons/orms/typeorm/express/src/modules/_base/auto-routes.ts +0 -249
  56. package/src/addons/orms/typeorm/express/src/modules/_base/index.ts +0 -19
  57. package/src/addons/orms/typeorm/express/src/server.ts +0 -43
  58. package/src/addons/orms/typeorm/express/tests/app.test.ts +0 -24
  59. package/src/addons/orms/typeorm/express/vitest.config.ts +0 -20
  60. package/src/addons/orms/typeorm/fastify/src/app.ts +0 -86
  61. package/src/addons/orms/typeorm/fastify/src/modules/_base/auto-routes.ts +0 -239
  62. package/src/addons/orms/typeorm/fastify/src/modules/_base/index.ts +0 -19
  63. package/src/addons/orms/typeorm/fastify/tests/modules/app.test.ts +0 -20
  64. package/src/addons/orms/typeorm/fastify/vitest.config.ts +0 -31
  65. package/src/addons/orms/typeorm/gen-entity/entity.ts +0 -21
  66. package/src/addons/orms/typeorm/gen-entity/express-router.ts +0 -17
  67. package/src/addons/orms/typeorm/gen-entity/express-test.ts +0 -66
  68. package/src/addons/orms/typeorm/gen-entity/fastify-router.ts +0 -19
  69. package/src/addons/orms/typeorm/gen-entity/fastify-test.ts +0 -89
  70. package/src/addons/orms/typeorm/manifest.json +0 -53
  71. package/src/addons/orms/typeorm/shared/scripts/db-sync.ts +0 -14
  72. package/src/addons/orms/typeorm/shared/src/db/data-source.ts +0 -21
  73. package/src/addons/orms/typeorm/shared/src/entities/index.ts +0 -8
  74. package/src/addons/orms/typeorm/shared/src/modules/_base/query-engine.ts +0 -94
  75. package/src/addons/orms/typeorm/shared/src/modules/_base/registry.ts +0 -15
  76. package/src/addons/orms/typeorm/shared/tsconfig.json +0 -16
package/README.md CHANGED
@@ -85,15 +85,15 @@ If this saves you even one hour, it's already paid for itself. (It's free.)
85
85
 
86
86
  ## What you get
87
87
 
88
- | Component | Stack | What it gives you |
89
- | ---------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
90
- | `fastapi` | Python, SQLAlchemy, Alembic | Auto-entity CRUD, JWT auth, migrations, OpenAPI docs |
91
- | `fastify` | Node.js, Prisma / Drizzle / Sequelize / TypeORM, TypeBox | Auto-entity CRUD, JWT auth, typed schemas, OpenAPI docs (auth currently Prisma-only) |
92
- | `express` | Express 5, TypeScript, Prisma / Drizzle / Sequelize / TypeORM | Auto-entity CRUD, validation, security middleware, health checks |
93
- | `frontend` | React 19, TypeScript, Vite | Auto-entity UI from `/_meta`, design tokens, light/dark mode |
94
- | `mobile` | Flutter, Riverpod, GoRouter | Auto-entity screens, offline-first with Isar, biometric auth |
95
- | `e2e` | Playwright | Page object model, auth fixtures, accessibility scans |
96
- | `infra` | Terraform, AWS | EKS, RDS, VPC, ALB, CodePipeline, multi-environment |
88
+ | Component | Stack | What it gives you |
89
+ | ---------- | ------------------------------------------------------------- | -------------------------------------------------------------------------- |
90
+ | `fastapi` | Python, SQLAlchemy, Alembic | Auto-entity CRUD, JWT auth, migrations, OpenAPI docs |
91
+ | `fastify` | Node.js, Prisma / Drizzle / Sequelize / TypeORM, TypeBox | Auto-entity CRUD, JWT auth, typed schemas, OpenAPI docs |
92
+ | `express` | Express 5, TypeScript, Prisma / Drizzle / Sequelize / TypeORM | Auto-entity CRUD, JWT auth, validation, security middleware, health checks |
93
+ | `frontend` | React 19, TypeScript, Vite | Auth, theming, design tokens, light/dark mode |
94
+ | `mobile` | Flutter, Riverpod, GoRouter | Auth, biometric, theming, GoRouter shell |
95
+ | `e2e` | Playwright | Page object model, auth fixtures, accessibility scans |
96
+ | `infra` | Terraform, AWS | EKS, RDS, VPC, ALB, CodePipeline, multi-environment |
97
97
 
98
98
  Plus, in every project: Docker Compose for dev + prod, GitHub Actions CI per component (path-filtered), pre-commit hooks, secret detection, VS Code settings, and 80% test coverage enforced.
99
99
 
@@ -242,7 +242,6 @@ npx create-projx unpin <patterns...>
242
242
  npx create-projx pin --list
243
243
  npx create-projx doctor [--fix]
244
244
  npx create-projx gen entity <name> [--ai | --backend]
245
- npx create-projx sync [--url <url>]
246
245
 
247
246
  --components <list> Comma-separated: fastapi,fastify,express,frontend,mobile,e2e,infra
248
247
  --name <dir> Custom directory for `add <type>` (multi-instance)
@@ -315,7 +314,6 @@ Override with `--ai` (fastapi) or `--backend` (fastify).
315
314
  | Primary backend (fastapi) | `src/entities/<name>/_model.py` + `tests/test_<name>_entity.py` — model + 11 CRUD/auth tests |
316
315
  | Primary backend (fastify) | `src/modules/<name>/schemas.ts` + `index.ts` + Prisma model + `tests/modules/<name>.test.ts` |
317
316
  | `frontend` | `src/types/<name>.ts` — TypeScript interface + Create/Update variants |
318
- | `mobile` | `lib/entities/<name>/model.dart` — Dart class with fromJson/toJson/copyWith |
319
317
 
320
318
  **Tests included**: every `gen entity` writes a working integration test file alongside the model — 11 tests for FastAPI (extending `BaseEntityApiTest`), 11 tests for Fastify (via `describeCrudEntity`). Both run against a real database (Postgres). New entities ship green from day one — no scrambling to bolt on tests at go-live.
321
319
 
@@ -336,26 +334,6 @@ All four ORMs scaffold equivalent runtime behavior: `_base/auto-routes.ts` wires
336
334
 
337
335
  ORM-specific scaffolding lives in [cli/src/addons/orms/](cli/src/addons/orms/) — each ORM is a self-contained folder with a `manifest.json` (deps, file removals, scripts), `shared/` files, per-framework overlays, and `gen-entity/` templates. Adding a new ORM means adding a new folder there; no CLI core changes.
338
336
 
339
- ### Sync Types
340
-
341
- Regenerate all frontend/mobile types from a running backend:
342
-
343
- ```bash
344
- npx create-projx sync # auto-detects URL
345
- npx create-projx sync --url http://localhost:8000/api/v1/_meta # explicit URL
346
- ```
347
-
348
- Fetches `/_meta` from your backend, generates typed interfaces for every entity. Run after any backend change — new field, renamed column, new entity.
349
-
350
- The generic `api.ts` client accepts type parameters:
351
-
352
- ```tsx
353
- import type { Invoice } from "../types/invoice";
354
-
355
- const { data } = await api.list<Invoice>("/invoices"); // data: Invoice[]
356
- const item = await api.get<Invoice>("/invoices", id); // item: Invoice
357
- ```
358
-
359
337
  ## Rename Component Directories
360
338
 
361
339
  Rename `fastapi/` to `backend/`? Just rename the folder — the `.projx-component` marker file moves with it. The `update` command auto-discovers where each component lives by scanning for these markers. No config changes needed.
@@ -373,7 +351,7 @@ CI, `scripts/setup.sh`, pre-commit hooks, and docker-compose are all regenerated
373
351
  my-app/
374
352
  ├── fastapi/ # Auto-entity CRUD backend
375
353
  │ └── .projx-component # Identifies this as the fastapi component
376
- ├── frontend/ # Auto-entity UI from /_meta
354
+ ├── frontend/ # React + Vite shell
377
355
  │ └── .projx-component
378
356
  ├── e2e/ # Playwright E2E tests
379
357
  │ └── .projx-component
@@ -393,11 +371,11 @@ The core idea: define a data model, get everything else for free.
393
371
 
394
372
  **Backend** — Drop a model file. The registry auto-discovers it and generates CRUD routes, schemas, pagination, filtering, sorting, search, FK expansion, and OpenAPI docs.
395
373
 
396
- **Field privacy** — Sensitive columns (`password_hash`, `secret`, `api_key`, `mfa_secret`, etc.) are automatically stripped from API responses and `/_meta` via a built-in baseline. Add project-specific hidden fields per entity (`__hidden_fields__` in FastAPI, `hiddenFields` in Fastify). Mark entire entities as `__private__` / `private: true` to hide them from the API entirely — no routes registered, not listed in `/_meta`. The `/_meta` endpoint requires authentication on both backends.
374
+ **Field privacy** — Sensitive columns (`password_hash`, `secret`, `api_key`, `mfa_secret`, etc.) are automatically stripped from API responses via a built-in baseline. Add project-specific hidden fields per entity (`__hidden_fields__` in FastAPI, `hiddenFields` in Fastify). Mark entire entities as `__private__` / `private: true` to hide them from the API entirely — no routes registered.
397
375
 
398
- **Frontend** — Fetches metadata from `GET /api/v1/_meta`, renders table + form UI automatically. Customize with overrides.
376
+ **Frontend** — Ships a React shell with auth, theming, and design tokens. Build your own pages using the generated types from `gen entity`.
399
377
 
400
- **Mobile** — Same metadata endpoint, generates list/detail/form screens. Offline-first with local DB and sync queue.
378
+ **Mobile** — Ships a Flutter shell with auth, biometric, and GoRouter scaffolding. Build screens using the generated Dart models from `gen entity`.
401
379
 
402
380
  ## Encrypted Service Config
403
381
 
@@ -8,8 +8,8 @@ import {
8
8
  matchesSkip,
9
9
  saveBaselineRef,
10
10
  writeTemplateToDir
11
- } from "./chunk-IMZKHDIL.js";
12
- import "./chunk-HAT7D4G2.js";
11
+ } from "./chunk-XAYCVTHL.js";
12
+ import "./chunk-FQPOK3QZ.js";
13
13
  export {
14
14
  BASELINE_REF,
15
15
  applyTemplate,
@@ -223,6 +223,7 @@ async function copyStaticFiles(repoDir, dest) {
223
223
  }
224
224
  const staticScripts = [
225
225
  "ci-local.sh",
226
+ "check-bundle-size.sh",
226
227
  "setup-docker.sh",
227
228
  "setup-ssl.sh",
228
229
  "style-check.py"
@@ -343,6 +344,7 @@ var DEFAULT_ROOT_SKIP_PATTERNS = [
343
344
  ".githooks/pre-commit",
344
345
  ".github/workflows/ci.yml",
345
346
  "scripts/ci-local.sh",
347
+ "scripts/check-bundle-size.sh",
346
348
  "scripts/setup.sh",
347
349
  "scripts/setup-docker.sh",
348
350
  "scripts/setup-ssl.sh"
@@ -389,15 +391,20 @@ async function discoverComponentsFromMarkers(cwd) {
389
391
  }
390
392
  function render(template, vars) {
391
393
  const lines = template.split("\n");
392
- return renderLines(lines, vars).replace(/\n{3,}/g, "\n\n");
394
+ return renderLines(lines, vars).replace(/\n{4,}/g, "\n\n\n");
393
395
  }
394
396
  function evalExpr(expr, vars) {
395
397
  const components = vars.components;
396
398
  const projectName = vars.projectName;
397
- const pmName = vars.pm?.name ?? "npm";
399
+ if (vars.pm !== void 0 && typeof vars.pm !== "object") {
400
+ throw new Error(
401
+ `render: vars.pm must be a PmCommands object (got ${typeof vars.pm}). Templates use pm.exec, pm.name etc.; passing the package-manager name as a bare string silently breaks those references.`
402
+ );
403
+ }
404
+ const pm = typeof vars.pm === "object" && vars.pm !== null ? vars.pm : { name: "npm" };
398
405
  const orm = vars.orm ?? "prisma";
399
406
  const argNames = ["components", "projectName", "pm", "orm"];
400
- const argValues = [components, projectName, pmName, orm];
407
+ const argValues = [components, projectName, pm, orm];
401
408
  for (const [k, v] of Object.entries(vars)) {
402
409
  if (["components", "projectName", "pm", "orm"].includes(k)) continue;
403
410
  if (!/^[a-zA-Z_$][\w$]*$/.test(k)) continue;
@@ -13,7 +13,7 @@ import {
13
13
  toSnake,
14
14
  upsertComponentMarker,
15
15
  writeProjxConfig
16
- } from "./chunk-HAT7D4G2.js";
16
+ } from "./chunk-FQPOK3QZ.js";
17
17
 
18
18
  // src/baseline.ts
19
19
  import { existsSync, writeFileSync, unlinkSync } from "fs";
@@ -29,7 +29,6 @@ import {
29
29
  } from "fs/promises";
30
30
  import { execSync } from "child_process";
31
31
  import { join as join2, dirname } from "path";
32
- import { fileURLToPath } from "url";
33
32
  import { tmpdir } from "os";
34
33
 
35
34
  // src/generators/index.ts
@@ -127,7 +126,7 @@ function generateVscodeSettings(vars) {
127
126
  // src/baseline.ts
128
127
  var BASELINE_REF = "refs/projx/baseline";
129
128
  async function migrateComponentMarkers(cwd, components, componentPaths, applyDefaults) {
130
- const { readComponentMarker: readComponentMarker2, writeComponentMarker } = await import("./utils-BZGSJ7XZ.js");
129
+ const { readComponentMarker: readComponentMarker2, writeComponentMarker } = await import("./utils-MC7VKL2U.js");
131
130
  for (const component of components) {
132
131
  const dir = componentPaths[component];
133
132
  const markerDir = join2(cwd, dir);
@@ -505,7 +504,7 @@ async function writeOneInstance(inst, opts) {
505
504
  [type]: targetDir
506
505
  };
507
506
  await renderEjsInDir(outDir, { ...vars, paths: instancePaths });
508
- await applyOrmProviderToInstance(outDir, type, vars);
507
+ await applyOrmProviderToInstance(repoDir, outDir, type, vars);
509
508
  await upsertComponentMarker(
510
509
  join2(dest, targetDir),
511
510
  type,
@@ -519,18 +518,14 @@ async function writeOneInstance(inst, opts) {
519
518
  vars.nameOverrides
520
519
  );
521
520
  }
522
- async function applyOrmProviderToInstance(dir, component, vars) {
521
+ async function applyOrmProviderToInstance(repoDir, dir, component, vars) {
523
522
  const orm = typeof vars.orm === "string" ? vars.orm : void 0;
524
523
  if (!orm || orm === "prisma") return;
525
524
  if (component !== "fastify" && component !== "express") return;
526
- await applyOrmAddon(orm, component, dir, vars);
525
+ await applyOrmAddon(repoDir, orm, component, dir, vars);
527
526
  }
528
- function sharedAddonDir() {
529
- const thisFile = fileURLToPath(import.meta.url);
530
- return join2(thisFile, "../../src/addons");
531
- }
532
- async function loadOrmManifest(orm) {
533
- const path = join2(sharedAddonDir(), "orms", orm, "manifest.json");
527
+ async function loadOrmManifest(repoDir, orm) {
528
+ const path = join2(repoDir, "addons", "orms", orm, "manifest.json");
534
529
  if (!existsSync(path)) {
535
530
  throw new Error(
536
531
  `ORM "${orm}" is not yet supported. No manifest found at ${path}.`
@@ -566,8 +561,8 @@ function applyPackageOverrides(pkg, overrides) {
566
561
  Object.assign(devDependencies, overrides.addDevDependencies ?? {});
567
562
  pkg.devDependencies = devDependencies;
568
563
  }
569
- async function applyOrmAddon(orm, framework, dir, vars) {
570
- const manifest = await loadOrmManifest(orm);
564
+ async function applyOrmAddon(repoDir, orm, framework, dir, vars) {
565
+ const manifest = await loadOrmManifest(repoDir, orm);
571
566
  if (!manifest.frameworks.includes(framework)) {
572
567
  throw new Error(
573
568
  `ORM "${orm}" does not support framework "${framework}". Supported: ${manifest.frameworks.join(", ")}`
@@ -577,10 +572,12 @@ async function applyOrmAddon(orm, framework, dir, vars) {
577
572
  await rm(join2(dir, relPath), { recursive: true, force: true });
578
573
  }
579
574
  const pkgPath = join2(dir, "package.json");
580
- const pkg = await readJsonObject(pkgPath);
581
- applyPackageOverrides(pkg, manifest.packageOverrides);
582
- await writeJsonObject(pkgPath, pkg);
583
- const addonRoot = join2(sharedAddonDir(), "orms", orm);
575
+ if (existsSync(pkgPath)) {
576
+ const pkg = await readJsonObject(pkgPath);
577
+ applyPackageOverrides(pkg, manifest.packageOverrides);
578
+ await writeJsonObject(pkgPath, pkg);
579
+ }
580
+ const addonRoot = join2(repoDir, "addons", "orms", orm);
584
581
  const sharedSrc = join2(addonRoot, "shared");
585
582
  const frameworkSrc = join2(addonRoot, framework);
586
583
  if (existsSync(sharedSrc)) {