create-bw-app 0.27.2 → 0.27.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -40,7 +40,7 @@ bw admin create --email owner@example.com
40
40
  - `bw add <moduleKey>` resolves requirements, installs thin package mounts and module wiring, and appends migrations.
41
41
  - `bw upgrade [moduleKey]` includes the existing managed update flow plus forward-only module migrations. For a staged database rollout, `bw upgrade <moduleKey> --through-migration <filename>` still updates packages and managed files but appends and records that module only through the named migration.
42
42
  - Destructive migrations are held at the preceding safe boundary. After compatible code is deployed and affected data is backed up, apply a held migration with an explicit module-scoped `bw upgrade <moduleKey> --include-destructive-migrations`. A destructive `--through-migration` target also requires that opt-in.
43
- - `bw doctor` checks package, config, scaffold, environment-name, migration, configured function-region, and deployed function-region consistency. Pass `--deployment-url` to inspect the deployed `x-vercel-id`; add `--report` to stamp the result in the app manifest.
43
+ - `bw doctor` checks package, config, scaffold, environment-name, migration, configured function-region, and deployed function-region consistency. App-owned scaffold files are preserved on upgrade; doctor warns when their recorded template baseline is outdated and their contents differ from the current template. Use `bw diff`, reconcile relevant changes, and verify behavior before recording the reviewed baseline with `bw scaffold manage <path>` followed by `bw scaffold own <path>` (neither replaces file contents). Live database objects, applied migrations, and authenticated permissions are explicitly **NOT VERIFIED** by doctor; a successful local result does not certify deployed behavior. Pass `--deployment-url` to inspect the deployed `x-vercel-id`; add `--report` to stamp the result in the app manifest.
44
44
  - `bw admin create --email <email>` creates a passwordless Supabase Auth user, transactionally ensures its profile and `admin` assignment, then sends the Core Auth `/reset-password` flow. It always refuses when the project already has an admin (use the in-app admin role controls to add administrators) and never promotes an existing Auth user.
45
45
  - All mutating commands support `--dry-run`.
46
46
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-bw-app",
3
3
  "private": false,
4
- "version": "0.27.2",
4
+ "version": "0.27.3",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "create-bw-app": "bin/create-bw-app.mjs",
package/src/constants.mjs CHANGED
@@ -116,6 +116,7 @@ export const MODULE_STARTER_FILES = {
116
116
  "app/(shell)/projetos/[projectId]/page.tsx",
117
117
  "app/(shell)/projetos/[projectId]/quadro/page.tsx",
118
118
  "app/(shell)/projetos/[projectId]/tarefas/page.tsx",
119
+ "app/(shell)/account/perfil/page.tsx",
119
120
  "app/(shell)/account/projetos/page.tsx",
120
121
  "app/(shell)/account/projetos/loading.tsx",
121
122
  "app/(shell)/account/projetos/[projectId]/page.tsx",
package/src/doctor.mjs CHANGED
@@ -3,11 +3,11 @@ import path from "node:path";
3
3
  import { stdout as output } from "node:process";
4
4
  import { APP_DEPENDENCY_DEFAULTS, BRIGHTWEB_PACKAGE_NAMES } from "./constants.mjs";
5
5
  import { cursorMigrationStatus, exactMigrationCompatibilityStatus } from "./migrations.mjs";
6
- import { findWorkspaceRoot, loadModuleCatalog, MODULE_PACKAGES, readAppManifest, readConfiguredModuleFlags, satisfiesVersion, validateAppManifest, writeAppManifest } from "./app-manifest.mjs";
6
+ import { findWorkspaceRoot, hashFile, loadModuleCatalog, MODULE_PACKAGES, readAppManifest, readConfiguredModuleFlags, satisfiesVersion, validateAppManifest, writeAppManifest } from "./app-manifest.mjs";
7
7
  import { loadAppEnvironment, readFirstEnvironmentValue } from "./env.mjs";
8
8
  import { pathExists, readJsonIfPresent } from "./generator.mjs";
9
9
  import { nearestVercelRegion, normalizeSupabaseRegion } from "./regions.mjs";
10
- import { scaffoldDrift } from "./scaffold.mjs";
10
+ import { canonicalScaffoldHash, scaffoldDrift } from "./scaffold.mjs";
11
11
 
12
12
  const HELP = `Usage: bw doctor [options]\n\nOptions:\n --target-dir <path> App directory (defaults to cwd)\n --workspace-root <path> BrightWeb workspace root\n --deployment-url <url> Deployed app URL (defaults to PUBLIC_APP_URL/NEXT_PUBLIC_APP_URL)\n --supabase-region <id> Current Supabase project region override\n --strict Treat warnings as failures\n --report Stamp lastDoctor in the app manifest\n --help Show this help`;
13
13
  const RUNTIME_PACKAGE_NAMES = ["react", "react-dom", "next"];
@@ -378,6 +378,18 @@ export async function doctorBrightwebApp(argvOptions = {}, runtimeOptions = {})
378
378
  add(topologyProblems.length ? "FAIL" : "PASS", "topology", topologyProblems.join("; ") || "Module requirements are satisfied.");
379
379
 
380
380
  const scaffold = await scaffoldDrift(targetDir, appManifest.scaffoldFiles);
381
+ const ownedTemplateUpdates = [];
382
+ for (const entry of scaffold.entries) {
383
+ if (entry.intent !== "owned" || entry.status === "missing") continue;
384
+ const templateHash = await canonicalScaffoldHash({ relativePath: entry.relativePath, manifest: appManifest, targetDir, workspaceRoot });
385
+ // Ownership preserves the recorded baseline during upgrades; it does not prove
386
+ // that later starter fixes have been reconciled into the app's custom file.
387
+ if (templateHash && templateHash !== appManifest.scaffoldFiles[entry.relativePath].hash
388
+ && templateHash !== await hashFile(path.join(targetDir, entry.relativePath))) {
389
+ ownedTemplateUpdates.push(entry.relativePath);
390
+ }
391
+ }
392
+ if (ownedTemplateUpdates.length) add("WARN", "scaffold-owned-template-updates", `Current templates changed for preserved app-owned files: ${ownedTemplateUpdates.join(", ")}. Compatibility is NOT VERIFIED. Run bw diff, reconcile relevant fixes and verify behavior. After review, use bw scaffold manage followed by bw scaffold own for each custom file to record the current template baseline; these commands do not replace file contents.`);
381
393
  const scaffoldGroups = {
382
394
  current: scaffold.entries.filter((entry) => entry.status === "current" && entry.intent !== "skipped"),
383
395
  owned: scaffold.entries.filter((entry) => entry.intent === "owned" && entry.status === "drifted"),
@@ -391,7 +403,7 @@ export async function doctorBrightwebApp(argvOptions = {}, runtimeOptions = {})
391
403
  if (scaffoldGroups.undecidedDrift.length) add("WARN", "scaffold-undecided-drift", `Unacknowledged drift: ${scaffoldGroups.undecidedDrift.map((entry) => entry.relativePath).join(", ")} (use bw scaffold own or bw diff).`);
392
404
  if (scaffoldGroups.undecidedMissing.length) add("WARN", "scaffold-undecided-missing", `Unacknowledged missing files: ${scaffoldGroups.undecidedMissing.map((entry) => entry.relativePath).join(", ")} (use bw scaffold skip after review).`);
393
405
  if (scaffoldGroups.mismatched.length) add("FAIL", "scaffold-intent-mismatch", `Recorded scaffold intent no longer matches reality: ${scaffoldGroups.mismatched.map((entry) => `${entry.relativePath} (${entry.intent}, ${entry.status})`).join(", ")}.`);
394
- const scaffoldStatus = scaffoldGroups.mismatched.length ? "FAIL" : scaffoldGroups.undecidedDrift.length || scaffoldGroups.undecidedMissing.length ? "WARN" : "PASS";
406
+ const scaffoldStatus = scaffoldGroups.mismatched.length ? "FAIL" : ownedTemplateUpdates.length || scaffoldGroups.undecidedDrift.length || scaffoldGroups.undecidedMissing.length ? "WARN" : "PASS";
395
407
  add(scaffoldStatus, "scaffold", `${scaffoldGroups.current.length} current, ${scaffoldGroups.owned.length} owned, ${scaffoldGroups.skipped.length} skipped, ${scaffoldGroups.undecidedDrift.length} undecided-drift, ${scaffoldGroups.undecidedMissing.length} undecided-missing, ${scaffoldGroups.mismatched.length} intent-mismatch.`);
396
408
  add("INFO", "owned-surfaces", `Owned surfaces: ${(appManifest.ownedSurfaces || []).join(", ") || "none"}.`);
397
409
 
@@ -456,7 +468,7 @@ export async function doctorBrightwebApp(argvOptions = {}, runtimeOptions = {})
456
468
  add("INFO", `migration-legacy-equivalent-${key}`, `${key}: ${status.legacyEquivalent.length} immutable historical migration file${status.legacyEquivalent.length === 1 ? " matches" : "s match"} a reviewed SQL-equivalent legacy hash.`);
457
469
  }
458
470
  }
459
- add("WARN", "db-objects", "SKIP live database checks are not available yet.");
471
+ add("WARN", "db-objects", "NOT VERIFIED: live database objects, applied migrations, and authenticated row-level permissions. Local migration provenance does not establish deployed behavior; apply pending migrations and run authenticated admin/client permission checks against the target database.");
460
472
  return finish(checks, argvOptions, appManifest, targetDir);
461
473
  }
462
474
 
@@ -0,0 +1,9 @@
1
+ -- The admin directory joins role assignments to profiles using the caller's
2
+ -- authenticated session. Grant the corresponding profile reads through RLS.
3
+ -- Staff and clients retain their existing profile visibility and write rules.
4
+ DROP POLICY IF EXISTS "Admins can view profiles" ON public.profiles;
5
+ CREATE POLICY "Admins can view profiles"
6
+ ON public.profiles
7
+ FOR SELECT
8
+ TO authenticated
9
+ USING (public.is_admin());