create-bw-app 0.9.9 → 0.10.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-bw-app",
3
3
  "private": false,
4
- "version": "0.9.9",
4
+ "version": "0.10.1",
5
5
  "type": "module",
6
6
  "bin": "bin/create-bw-app.mjs",
7
7
  "files": [
package/src/constants.mjs CHANGED
@@ -67,12 +67,12 @@ export const MODULE_STARTER_FILES = {
67
67
 
68
68
  export const APP_DEPENDENCY_DEFAULTS = {
69
69
  "@brightweblabs/app-shell": "^0.3.0",
70
- "@brightweblabs/core-auth": "^0.3.1",
71
- "@brightweblabs/infra": "^0.2.1",
72
- "@brightweblabs/module-admin": "^0.3.0",
73
- "@brightweblabs/module-crm": "^0.3.0",
74
- "@brightweblabs/module-projects": "^0.2.2",
75
- "@brightweblabs/ui": "^0.3.0",
70
+ "@brightweblabs/core-auth": "^0.3.3",
71
+ "@brightweblabs/infra": "^0.3.0",
72
+ "@brightweblabs/module-admin": "^0.3.2",
73
+ "@brightweblabs/module-crm": "^0.3.3",
74
+ "@brightweblabs/module-projects": "^0.3.2",
75
+ "@brightweblabs/ui": "^0.3.1",
76
76
  "lucide-react": "^0.562.0",
77
77
  "next": "16.1.6",
78
78
  "react": "19.2.3",
@@ -110,7 +110,6 @@ export const DEFAULTS = {
110
110
  tagline: "A configurable Brightweb starter app for new client instances.",
111
111
  contactEmail: "hello@example.com",
112
112
  supportEmail: "support@example.com",
113
- primaryHex: "#1f7a45",
114
113
  };
115
114
 
116
115
  export const HELP_TEXT = `
package/src/generator.mjs CHANGED
@@ -270,7 +270,6 @@ function createDerivedBrandValues(slug) {
270
270
  tagline: DEFAULTS.tagline,
271
271
  contactEmail: DEFAULTS.contactEmail,
272
272
  supportEmail: DEFAULTS.supportEmail,
273
- primaryHex: DEFAULTS.primaryHex,
274
273
  };
275
274
  }
276
275
 
@@ -283,7 +282,6 @@ function createPlatformBrandConfigFile({ slug, brandValues }) {
283
282
  " tagline: string;",
284
283
  " contactEmail: string;",
285
284
  " supportEmail: string;",
286
- " primaryHex: string;",
287
285
  "};",
288
286
  "",
289
287
  "export const starterBrandConfig: StarterBrandConfig = {",
@@ -293,7 +291,6 @@ function createPlatformBrandConfigFile({ slug, brandValues }) {
293
291
  ` tagline: ${JSON.stringify(brandValues.tagline)},`,
294
292
  ` contactEmail: ${JSON.stringify(brandValues.contactEmail)},`,
295
293
  ` supportEmail: ${JSON.stringify(brandValues.supportEmail)},`,
296
- ` primaryHex: ${JSON.stringify(brandValues.primaryHex)},`,
297
294
  "};",
298
295
  "",
299
296
  ].join("\n");
@@ -328,7 +325,7 @@ export function createPlatformModulesConfigFile(selectedModules) {
328
325
  " {",
329
326
  ' key: "crm",',
330
327
  ' label: "CRM",',
331
- ' description: "Contacts, marketing audience, and CRM server/data layer.",',
328
+ ' description: "Contacts and CRM server/data layer, with marketing-adjacent operational data stored in Supabase.",',
332
329
  ` enabled: ${String(selected.has("crm"))},`,
333
330
  ' packageName: "@brightweblabs/module-crm",',
334
331
  ' playgroundHref: "/playground/crm",',
@@ -368,6 +365,12 @@ function createEnvFileContent() {
368
365
  "NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY=",
369
366
  "SUPABASE_SECRET_DEFAULT_KEY=",
370
367
  "RESEND_API_KEY=",
368
+ "RESEND_FROM_TRANSACTIONAL=",
369
+ "RESEND_FROM_MARKETING=",
370
+ "CONTACT_TO_EMAIL=",
371
+ "RESEND_WEBHOOK_SECRET=",
372
+ "MARKETING_WORKER_SECRET=",
373
+ "MARKETING_TEST_EMAIL=",
371
374
  "",
372
375
  ].join("\n");
373
376
  }
@@ -630,6 +633,7 @@ export function createAppContextFile({
630
633
  "AGENTS.md",
631
634
  "docs/ai/README.md",
632
635
  "README.md",
636
+ "app/globals.css",
633
637
  "config/brand.ts",
634
638
  "config/modules.ts",
635
639
  "config/client.ts",
@@ -10,6 +10,7 @@ This generated project is a BrightWeb platform starter. Use this file as the loc
10
10
  - `docs/ai/app-context.json`: machine-readable app summary for quick discovery.
11
11
  - `components/`: local app components used by starter routes and future product surfaces.
12
12
  - `config/brand.ts`: client identity, naming, and contact defaults.
13
+ - `app/globals.css`: global design tokens, theme mapping, and shared visual styling.
13
14
  - `config/modules.ts`: selected module set and runtime enablement.
14
15
  - `config/client.ts`: starter-facing derived state used by the home page and setup surfaces.
15
16
  - `.env.local`: runtime service values for local development.
@@ -17,6 +18,7 @@ This generated project is a BrightWeb platform starter. Use this file as the loc
17
18
  ## Working rules
18
19
 
19
20
  - Treat `/bootstrap`, `/preview/app-shell`, and `/playground/*` as starter validation surfaces. They are app-owned and can be removed after setup if links and references are cleaned up too.
21
+ - Keep identity/contact in `config/brand.ts`, and keep all color/theme tokens in `app/globals.css`.
20
22
  - Check `config/modules.ts` before assuming CRM, Projects, or Admin routes exist.
21
23
  - Prefer composing app-level routes and config before forking logic from `@brightweblabs/*` packages.
22
24
  - Keep edits local to this app unless the change is intentionally shared across multiple BrightWeb projects.
@@ -4,17 +4,32 @@
4
4
  color-scheme: light;
5
5
  --font-sans: "IBM Plex Sans", "Segoe UI", sans-serif;
6
6
  --font-display: Georgia, "Times New Roman", serif;
7
+ /* Raw brand tokens: customize these first per client. */
8
+ --brand-primary: #266946;
9
+ --brand-primary-rgb: 38, 105, 70;
10
+ --brand-secondary-rgb: 126, 170, 112;
11
+ --brand-highlight-rgb: 189, 140, 89;
12
+ --brand-positive-rgb: 113, 196, 139;
13
+ /* Semantic theme tokens consumed by the UI. */
7
14
  --background: #f3efe5;
8
15
  --foreground: #18241d;
9
16
  --muted-foreground: #5a665c;
10
17
  --border: rgba(24, 36, 29, 0.12);
11
18
  --panel: rgba(255, 255, 255, 0.82);
12
19
  --panel-strong: #ffffff;
13
- --accent: #266946;
14
- --accent-soft: rgba(38, 105, 70, 0.12);
20
+ --accent: var(--brand-primary);
21
+ --accent-soft: rgba(var(--brand-primary-rgb), 0.12);
15
22
  --danger: #b43f3f;
16
23
  --ink-soft: #eef1e8;
17
- --ring: rgba(38, 105, 70, 0.28);
24
+ --ring: rgba(var(--brand-primary-rgb), 0.28);
25
+ --surface-glow-cool: rgba(var(--brand-secondary-rgb), 0.18);
26
+ --surface-glow-warm: rgba(var(--brand-highlight-rgb), 0.12);
27
+ --accent-glow-strong: rgba(var(--brand-primary-rgb), 0.18);
28
+ --accent-glow-mid: rgba(var(--brand-primary-rgb), 0.14);
29
+ --accent-glow-soft: rgba(var(--brand-primary-rgb), 0.08);
30
+ --positive-glow-strong: rgba(var(--brand-positive-rgb), 0.5);
31
+ --positive-glow-soft: rgba(var(--brand-positive-rgb), 0.14);
32
+ --positive-glow-subtle: rgba(var(--brand-positive-rgb), 0.15);
18
33
 
19
34
  /* Compatibility aliases so both starters expose the same token surface. */
20
35
  --bg: var(--background);
@@ -32,8 +47,8 @@ body {
32
47
  margin: 0;
33
48
  padding: 0;
34
49
  background:
35
- radial-gradient(circle at 15% 10%, rgba(126, 170, 112, 0.18), transparent 22%),
36
- radial-gradient(circle at 85% 12%, rgba(189, 140, 89, 0.12), transparent 18%),
50
+ radial-gradient(circle at 15% 10%, var(--surface-glow-cool), transparent 22%),
51
+ radial-gradient(circle at 85% 12%, var(--surface-glow-warm), transparent 18%),
37
52
  linear-gradient(180deg, #faf8f1 0%, var(--bg) 48%, #e8e0cf 100%);
38
53
  color: var(--text);
39
54
  font-family: var(--font-sans), sans-serif;
@@ -97,7 +112,7 @@ input {
97
112
  overflow: hidden;
98
113
  background:
99
114
  linear-gradient(180deg, rgba(255, 255, 255, 0.86), rgba(247, 244, 235, 0.84)),
100
- radial-gradient(circle at top right, rgba(38, 105, 70, 0.18), transparent 45%);
115
+ radial-gradient(circle at top right, var(--accent-glow-strong), transparent 45%);
101
116
  }
102
117
 
103
118
  .starter-hero-card::after {
@@ -107,7 +122,7 @@ input {
107
122
  width: 150px;
108
123
  height: 150px;
109
124
  border-radius: 999px;
110
- background: rgba(38, 105, 70, 0.08);
125
+ background: var(--accent-glow-soft);
111
126
  }
112
127
 
113
128
  .starter-stat-grid {
@@ -185,7 +200,7 @@ input {
185
200
  .preview-glass-card {
186
201
  background:
187
202
  linear-gradient(180deg, rgba(255, 255, 255, 0.84), rgba(246, 244, 236, 0.76)),
188
- radial-gradient(circle at top right, rgba(38, 105, 70, 0.08), transparent 45%);
203
+ radial-gradient(circle at top right, var(--accent-glow-soft), transparent 45%);
189
204
  }
190
205
 
191
206
  .panel h2,
@@ -308,7 +323,7 @@ input {
308
323
  border-radius: 28px;
309
324
  background:
310
325
  linear-gradient(180deg, rgba(255, 255, 255, 0.88), rgba(241, 237, 226, 0.84)),
311
- radial-gradient(circle at top, rgba(38, 105, 70, 0.12), transparent 34%);
326
+ radial-gradient(circle at top, var(--accent-soft), transparent 34%);
312
327
  backdrop-filter: blur(16px);
313
328
  box-shadow: 0 22px 60px rgba(24, 36, 29, 0.1);
314
329
  }
@@ -328,7 +343,7 @@ input {
328
343
  border-radius: 28px;
329
344
  background:
330
345
  linear-gradient(180deg, rgba(255, 255, 255, 0.88), rgba(246, 243, 235, 0.86)),
331
- radial-gradient(circle at right top, rgba(38, 105, 70, 0.14), transparent 35%);
346
+ radial-gradient(circle at right top, var(--accent-glow-mid), transparent 35%);
332
347
  backdrop-filter: blur(16px);
333
348
  }
334
349
 
@@ -374,7 +389,7 @@ input {
374
389
  .preview-stage-panel {
375
390
  background:
376
391
  linear-gradient(180deg, rgba(23, 37, 29, 0.96), rgba(18, 30, 24, 0.95)),
377
- radial-gradient(circle at top left, rgba(113, 196, 139, 0.15), transparent 34%);
392
+ radial-gradient(circle at top left, var(--positive-glow-subtle), transparent 34%);
378
393
  color: #f4f1e8;
379
394
  border-color: rgba(255, 255, 255, 0.1);
380
395
  }
@@ -409,8 +424,8 @@ input {
409
424
 
410
425
  .nav-chip.active,
411
426
  .nav-chip:hover {
412
- border-color: rgba(113, 196, 139, 0.5);
413
- background: rgba(113, 196, 139, 0.14);
427
+ border-color: var(--positive-glow-strong);
428
+ background: var(--positive-glow-soft);
414
429
  }
415
430
 
416
431
  .preview-surface-grid {
@@ -510,7 +525,7 @@ input {
510
525
  }
511
526
 
512
527
  .status.ok {
513
- background: rgba(31, 122, 69, 0.14);
528
+ background: rgba(var(--brand-primary-rgb), 0.14);
514
529
  color: var(--accent);
515
530
  }
516
531
 
@@ -113,6 +113,7 @@ export default function HomePage() {
113
113
  <h2>Starter controls</h2>
114
114
  <ul className="list">
115
115
  <li>`config/brand.ts` for client identity and contact details.</li>
116
+ <li>`app/globals.css` for color and theme token management.</li>
116
117
  <li>`config/modules.ts` for enabled platform modules.</li>
117
118
  <li>`config/env.ts` for infra requirements and readiness checks.</li>
118
119
  <li>`.env.local` for per-client service credentials and local runtime overrides.</li>
@@ -63,8 +63,14 @@ export function getStarterBootstrapChecklist() {
63
63
  },
64
64
  {
65
65
  label: "Configure Resend for the client sender domain",
66
- done: Boolean(config.envStatus.find((item) => item.key === "RESEND_API_KEY")?.present),
67
- detail: "Use a sender/domain owned by this client instance.",
66
+ done: [
67
+ "RESEND_API_KEY",
68
+ "RESEND_FROM_TRANSACTIONAL",
69
+ "RESEND_FROM_MARKETING",
70
+ "CONTACT_TO_EMAIL",
71
+ "RESEND_WEBHOOK_SECRET",
72
+ ].every((key) => Boolean(config.envStatus.find((item) => item.key === key)?.present)),
73
+ detail: "Use client-owned sender identities and configure webhook signature verification.",
68
74
  },
69
75
  {
70
76
  label: "Create per-client environment variables",
@@ -5,7 +5,6 @@ export type StarterBrandConfig = {
5
5
  tagline: string;
6
6
  contactEmail: string;
7
7
  supportEmail: string;
8
- primaryHex: string;
9
8
  };
10
9
 
11
10
  export const starterBrandConfig: StarterBrandConfig = {
@@ -15,5 +14,4 @@ export const starterBrandConfig: StarterBrandConfig = {
15
14
  tagline: "A configurable Brightweb starter app for shipping new client instances without rebuilding the platform.",
16
15
  contactEmail: "hello@example.com",
17
16
  supportEmail: "support@example.com",
18
- primaryHex: "#1f7a45",
19
17
  };
@@ -39,8 +39,44 @@ export const starterEnvRequirements: StarterEnvRequirement[] = [
39
39
  {
40
40
  key: "RESEND_API_KEY",
41
41
  scope: "server",
42
- description: "Email delivery key for auth and marketing-related server flows.",
43
- requiredFor: ["core-auth"],
42
+ description: "Email delivery key for app-owned transactional and marketing server flows.",
43
+ requiredFor: ["admin"],
44
+ },
45
+ {
46
+ key: "RESEND_FROM_TRANSACTIONAL",
47
+ scope: "server",
48
+ description: "Default transactional sender used by app-owned invite/contact email flows.",
49
+ requiredFor: ["admin"],
50
+ },
51
+ {
52
+ key: "RESEND_FROM_MARKETING",
53
+ scope: "server",
54
+ description: "Default marketing sender used by campaign and workflow email flows.",
55
+ requiredFor: ["admin"],
56
+ },
57
+ {
58
+ key: "CONTACT_TO_EMAIL",
59
+ scope: "server",
60
+ description: "Destination inbox for app-owned contact notifications.",
61
+ requiredFor: ["admin"],
62
+ },
63
+ {
64
+ key: "RESEND_WEBHOOK_SECRET",
65
+ scope: "server",
66
+ description: "Webhook signing secret used to verify inbound Resend event payloads.",
67
+ requiredFor: ["admin"],
68
+ },
69
+ {
70
+ key: "MARKETING_WORKER_SECRET",
71
+ scope: "server",
72
+ description: "Shared secret required to trigger internal marketing worker endpoints.",
73
+ requiredFor: ["admin"],
74
+ },
75
+ {
76
+ key: "MARKETING_TEST_EMAIL",
77
+ scope: "server",
78
+ description: "Inbox used by admin marketing test send actions.",
79
+ requiredFor: ["admin"],
44
80
  },
45
81
  ];
46
82
 
@@ -23,7 +23,7 @@ export const starterModuleConfig: StarterModuleConfig[] = [
23
23
  {
24
24
  key: "crm",
25
25
  label: "CRM",
26
- description: "Contacts, marketing audience, and CRM server/data layer.",
26
+ description: "Contacts and CRM server/data layer, with marketing-adjacent operational data stored in Supabase.",
27
27
  enabled: true,
28
28
  packageName: "@brightweblabs/module-crm",
29
29
  playgroundHref: "/playground/crm",
@@ -20,11 +20,12 @@ This app is a normal Next.js App Router project with BrightWeb runtime wiring la
20
20
  - `docs/ai/examples.md`: common setup and customization workflows.
21
21
  - `README.md`: first-run setup steps.
22
22
  - `components/`: local app component layer for starter surfaces and future product UI.
23
- - `config/brand.ts`: client name, product name, support inboxes, and brand color.
23
+ - `config/brand.ts`: client identity, product naming, and contact inboxes.
24
24
  - `config/modules.ts`: module metadata and enablement flags for CRM, Projects, and Admin.
25
25
  - `config/client.ts`: aggregated state consumed by starter pages.
26
26
  - `config/bootstrap.ts`: bootstrap checklist content for `/bootstrap`.
27
27
  - `config/shell.ts`: app-shell registration and navigation wiring.
28
+ - `app/globals.css`: color tokens (raw brand + semantic mappings), typography, and global surface styling.
28
29
  - `app/page.tsx`: starter landing page for the generated app.
29
30
  - `app/bootstrap/page.tsx`: setup checklist surface.
30
31
  - `app/preview/app-shell/page.tsx`: shell preview validation route.
@@ -34,6 +35,7 @@ This app is a normal Next.js App Router project with BrightWeb runtime wiring la
34
35
  ## Editing strategy
35
36
 
36
37
  - Change client identity first in `config/brand.ts`.
38
+ - Change colors and theme tokens in `app/globals.css`.
37
39
  - Check module presence in `config/modules.ts` before editing or creating module-specific routes.
38
40
  - Add app-specific UI in `components/` before forking shared package code.
39
41
  - Use `config/shell.ts` when navigation or toolbar behavior needs to change.
@@ -8,6 +8,7 @@ Goal: get the generated starter running with real credentials.
8
8
 
9
9
  - Review `.env.local` and replace placeholder values.
10
10
  - Review `config/brand.ts` and confirm client identity.
11
+ - Review `app/globals.css` and confirm brand tokens map to the intended visual system.
11
12
  - Review `config/modules.ts` before touching module routes.
12
13
  - Run the local dev server for this app or workspace.
13
14
  - Validate `/`, `/bootstrap`, `/preview/app-shell`, and `/playground/auth`.
@@ -18,6 +19,7 @@ Goal: get the generated starter running with real credentials.
18
19
  Goal: update the starter to the real client name and support details.
19
20
 
20
21
  - Edit `config/brand.ts`.
22
+ - Edit `app/globals.css` when palette or theme token mapping needs to change.
21
23
  - Move route-specific presentation into `components/` when the home or preview surfaces need app-owned UI.
22
24
  - Check `config/client.ts` or `config/bootstrap.ts` if starter copy still references old defaults.
23
25
  - Validate the home page and `/preview/app-shell` after the change.
@@ -0,0 +1,13 @@
1
+ import "server-only";
2
+
3
+ export {
4
+ getContactDestination,
5
+ getMarketingSender,
6
+ getResendApiKey,
7
+ getResendWebhookSecret,
8
+ getTransactionalSender,
9
+ resendApiRequest,
10
+ verifyResendWebhookSignature,
11
+ ResendApiError,
12
+ ResendConfigError,
13
+ } from "@brightweblabs/infra/server";
@@ -11,12 +11,14 @@
11
11
  - `organization_invitations`
12
12
  - CRM/org helper functions like `is_org_admin()` and `set_crm_status(...)`
13
13
  - CRM/org RLS and triggers
14
+ - app-owned operational contact records, not external ESP topic identifiers
14
15
 
15
16
  ## Shared boundary
16
17
 
17
18
  Some marketing data is adjacent to CRM, but the Brightweb v1 split is:
18
19
 
19
20
  - CRM owns operational contact and organization management
21
+ - CRM does not currently define newsletter/product-update subscription tables or Resend Topic mappings
20
22
  - Marketing automation remains its own future module boundary
21
23
 
22
24
  ## Dependency
@@ -0,0 +1,22 @@
1
+ -- portal_read_indexes
2
+ -- target: crm
3
+ -- created_at: 2026-04-21T20:15:23.686Z
4
+
5
+ CREATE INDEX IF NOT EXISTS idx_crm_contacts_updated_at_desc
6
+ ON public.crm_contacts (updated_at DESC);
7
+
8
+ CREATE INDEX IF NOT EXISTS idx_crm_contacts_created_at_desc
9
+ ON public.crm_contacts (created_at DESC);
10
+
11
+ CREATE INDEX IF NOT EXISTS idx_crm_contacts_status
12
+ ON public.crm_contacts (status);
13
+
14
+ CREATE INDEX IF NOT EXISTS idx_crm_contacts_source
15
+ ON public.crm_contacts (source)
16
+ WHERE source IS NOT NULL;
17
+
18
+ CREATE INDEX IF NOT EXISTS idx_crm_status_log_changed_at_desc
19
+ ON public.crm_status_log (changed_at DESC);
20
+
21
+ CREATE INDEX IF NOT EXISTS idx_organizations_created_at_desc
22
+ ON public.organizations (created_at DESC);
@@ -0,0 +1,6 @@
1
+ -- portal_read_indexes
2
+ -- target: projects
3
+ -- created_at: 2026-04-21T20:15:28.609Z
4
+
5
+ CREATE INDEX IF NOT EXISTS idx_project_tasks_status
6
+ ON public.project_tasks (status);