minutework 0.1.33 → 0.1.35

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/EXTERNAL_ALPHA.md CHANGED
@@ -120,14 +120,16 @@ If you run `minutework init` **without** `--starter` in an interactive terminal,
120
120
  The generated `tenant-app/.env.example` defaults to:
121
121
 
122
122
  ```dotenv
123
- MW_CONTENT_API_TOKEN=
124
- MW_PUBLIC_SITE_PROPERTY_KEY=main-site
125
- MW_PUBLIC_SITE_ENV=preview
123
+ NEXT_PUBLIC_MW_APP_ID=tenant.app
124
+ MW_TEMPLATE_APP_NAME=Tenant App
125
+ MW_PUBLIC_BASE_URL=
126
126
  ```
127
127
 
128
- `MW_CONTENT_API_TOKEN` stays server-only. `MW_PUBLIC_SITE_ENV=preview` targets
129
- preview or draft-safe reads, while `MW_PUBLIC_SITE_ENV=live` is publication-safe
130
- and intended for published snapshot delivery.
128
+ `tenant-app` browser auth and manifest calls use same-origin `/_mw` routes
129
+ through `@minutework/web-auth`. Do not add platform content tokens or
130
+ public-site property variables to the SDK-based tenant app; public content
131
+ should come from hosted published releases/snapshots or gateway-approved
132
+ public-content routes over the MinuteWork substrate.
131
133
 
132
134
  If the workspace cannot compile hosted preview release metadata for the linked property, deploy fails closed.
133
135
 
package/README.md CHANGED
@@ -122,7 +122,7 @@ Combined **tenant-app + sidecar** workspaces keep sidecar setup explicit. Root `
122
122
 
123
123
  For direct third-party web hosts such as Vercel, deploy `tenant-app` only. In Vercel, point the project Root Directory at `tenant-app`; the optional sidecar remains a separate Poetry-managed surface.
124
124
 
125
- `link` provisions or resolves the default published-site property for the active tenant and stores that property key in repo-local state. The generated `tenant-app/.env.example` includes the standard site env contract: `MW_CONTENT_API_TOKEN`, `MW_PUBLIC_SITE_PROPERTY_KEY`, and `MW_PUBLIC_SITE_ENV`. It defaults to `MW_PUBLIC_SITE_PROPERTY_KEY=main-site` and `MW_PUBLIC_SITE_ENV=preview`.
125
+ `link` provisions or resolves the default published-site property for the active tenant and stores that property key in repo-local state. The SDK-based `tenant-app/.env.example` contains only the app metadata contract used by the template: `NEXT_PUBLIC_MW_APP_ID`, `MW_TEMPLATE_APP_NAME`, and optional `MW_PUBLIC_BASE_URL`. Browser auth and manifest calls use same-origin `/_mw` routes through `@minutework/web-auth`; do not add platform content tokens or public-site property vars to the tenant app.
126
126
 
127
127
  `deploy --preview` always revalidates and recompiles before submit, prints a local-vs-remote diff, requires confirmation unless `--yes` is passed, polls typed receipts until a terminal state, and persists the last known preview deploy state under `.minutework/deploy/preview/status.json`. This alpha is preview-first; live public delivery remains follow-on.
128
128
 
@@ -6,10 +6,13 @@ smallest surface that fits the request.
6
6
 
7
7
  `tenant-app` is the combined web starter: public site routes at the root plus a
8
8
  private authenticated workspace under `/app`.
9
+ It uses `@minutework/web-auth` through a thin `src/mw/` substrate and
10
+ same-origin `/_mw` routes; do not add per-app auth/gateway BFF routes or
11
+ browser-stored tokens.
9
12
 
10
13
  `mobile` is the standalone Expo/React Native client starter. It talks directly
11
14
  to the platform with native device-flow auth and bearer tokens; it is not a
12
- `tenant-app` BFF cookie client and not a `sidecar`. It ships through the
15
+ `tenant-app` web SDK cookie client and not a `sidecar`. It ships through the
13
16
  developer's own EAS/App Store/Play Store pipeline, not `minutework deploy`.
14
17
 
15
18
  ## Refresh Managed Guidance
@@ -189,13 +192,14 @@ a concrete backend responsibility such as:
189
192
  - `mw.core.site` already includes `config`, `page`, `nav`, `form`, and
190
193
  `submission` schemas before Builder starts authoring.
191
194
  - Author public content against runtime/CMS records and published-web flows, not
192
- against a fixed in-repo marketing template.
193
- - Use `MW_PUBLIC_CONTENT_SOURCE` to select the public content adapter mode.
194
- - `MW_CONTENT_API_TOKEN`, `MW_PUBLIC_SITE_PROPERTY_KEY`, and
195
- `MW_PUBLIC_SITE_ENV` are required only for `MW_PUBLIC_CONTENT_SOURCE=minutework_cms`.
196
- - `MW_STATIC_PUBLIC_CONTENT_PATH` is used for `MW_PUBLIC_CONTENT_SOURCE=static_json`.
197
- - `MW_PUBLIC_SITE_ENV=preview` is for preview or draft-safe reads.
198
- - `MW_PUBLIC_SITE_ENV=live` is for publication-safe delivery.
195
+ against a fixed in-repo marketing template or a server-only content-token
196
+ adapter in `tenant-app`.
197
+ - The SDK-based `tenant-app` public metadata env contract is
198
+ `NEXT_PUBLIC_MW_APP_ID`, `MW_TEMPLATE_APP_NAME`, and optional
199
+ `MW_PUBLIC_BASE_URL`; do not add legacy `MW_CONTENT_API_TOKEN`,
200
+ `MW_PUBLIC_CONTENT_SOURCE`, `MW_PUBLIC_SITE_PROPERTY_KEY`,
201
+ `MW_PUBLIC_SITE_ENV`, or `MW_STATIC_PUBLIC_CONTENT_PATH` variables unless a
202
+ compatibility task explicitly targets that old adapter.
199
203
  - Anonymous live delivery should prefer published snapshots instead of direct
200
204
  runtime reads on every request.
201
205
  - `runtime_local_sidecar` is an opt-in public-site serving exception, not the
@@ -21,6 +21,16 @@ An `app pack` is the shipped product unit.
21
21
  per-app `src/app/api/auth/*`, `src/app/api/gateway/*`, platform-session BFF
22
22
  layers, platform credential login screens, operator-console links, or
23
23
  runtime-command demos.
24
+ - If a generated workspace reports `@minutework/web-auth` as missing, treat it
25
+ as dependency installation, package publishing, or package sync work. Do not
26
+ hand-roll auth/data routes or browser tokens as a workaround.
27
+ - The template may render a local `/login` page that calls SDK hooks. The SDK
28
+ also exposes hosted UI helpers for same-origin `/_mw/login`,
29
+ `/_mw/signup`, and `/_mw/verify-email`; both choices remain SDK-only.
30
+ - Client-side `/app` session checks are UX gating only. Real authorization is
31
+ enforced server-side by platform `/_mw` routes and runtime dispatch checks:
32
+ active customer membership, email verification, app publication, and
33
+ `webCustomerExposed` / `web_customer_exposed` manifest declarations.
24
34
  - Browser calls from `tenant-app` should use `mw.query(...)` and
25
35
  `mw.action(..., { idempotencyKey })`; actions require a non-empty
26
36
  idempotency key. The runtime surface must be declared on the manifest with
@@ -26,6 +26,15 @@ the monorepo or a live tenant runtime.
26
26
  `src/mw/` substrate and `@minutework/web-auth`. Product UI may call that
27
27
  substrate, but it must not recreate platform-session BFF routes, platform
28
28
  credential login, or browser-exposed platform/runtime tokens.
29
+ - If `@minutework/web-auth` is not installed or resolvable in a generated
30
+ workspace, fix dependency installation, package publishing, or package sync.
31
+ Do not replace the SDK with local auth/gateway routes.
32
+ - A local `/login` page may call SDK hooks, while SDK hosted UI helpers point
33
+ to same-origin `/_mw/login`, `/_mw/signup`, and `/_mw/verify-email`; both
34
+ are valid only when they stay on the SDK/`/_mw` contract.
35
+ - Client-side app guards are UX only. Platform `/_mw` routes and runtime
36
+ dispatch enforce active customer membership, email verification, app
37
+ publication, and manifest exposure server-side.
29
38
  - `tenant-app` runtime data access goes through `mw.query(...)` and
30
39
  `mw.action(..., { idempotencyKey })` against manifest-declared
31
40
  `webCustomerExposed` / `web_customer_exposed` customer surfaces.
@@ -1,9 +1,3 @@
1
- MW_PLATFORM_BASE_URL=http://127.0.0.1:8000
2
- MW_PUBLIC_CONTENT_SOURCE=none
3
- MW_CONTENT_API_TOKEN=
4
- MW_TEMPLATE_APP_NAME=MinuteWork Combined Starter
1
+ NEXT_PUBLIC_MW_APP_ID=tenant.app
2
+ MW_TEMPLATE_APP_NAME=Tenant App
5
3
  MW_PUBLIC_BASE_URL=
6
- MW_PUBLIC_SITE_PROPERTY_KEY=
7
- MW_PUBLIC_SITE_ENV=preview
8
- MW_STATIC_PUBLIC_CONTENT_PATH=content/public-site.json
9
- MW_ENABLE_RUNTIME_COMMAND_EXAMPLE=false
@@ -22,6 +22,20 @@ This template uses the `tenant_web_auth_sdk` profile:
22
22
  Only `src/mw/` is MinuteWork substrate. Keep product UI and product logic in
23
23
  `src/app`, `src/features`, and developer-owned modules.
24
24
 
25
+ The local `/login` route is product UI that calls SDK hooks. The SDK also
26
+ provides hosted UI helpers for same-origin `/_mw/login`, `/_mw/signup`, and
27
+ `/_mw/verify-email` URLs; both paths stay on the same SDK contract.
28
+
29
+ Client-side `/app` session checks are only UX gating. Authorization is enforced
30
+ by the platform `/_mw` routes and runtime dispatch: active customer membership,
31
+ email verification, app publication, and manifest `web_customer_exposed`
32
+ declarations are checked server-side.
33
+
34
+ If a generated workspace reports `@minutework/web-auth` as missing, treat that
35
+ as a dependency installation or package publishing/sync issue. Do not recreate
36
+ platform BFF routes, browser bearer tokens, or platform credential login as a
37
+ workaround.
38
+
25
39
  ## Default route shape
26
40
 
27
41
  - public routes at `/`, `/pricing`, `/docs`, and `/blog`
@@ -29,6 +43,10 @@ Only `src/mw/` is MinuteWork substrate. Keep product UI and product logic in
29
43
  - authenticated workspace at `/app`
30
44
  - SDK manifest demo at `/app/demo`
31
45
 
46
+ All `/app/*` routes are wrapped by `src/app/app/layout.tsx`, which renders the
47
+ shared authenticated layout guard. Individual `/app` pages should assume an
48
+ authenticated tenant-customer session instead of reimplementing auth checks.
49
+
32
50
  The template intentionally does not include `src/lib/platform/*`,
33
51
  `src/app/api/auth/*`, `src/app/api/gateway/*`, an operator-console link, a
34
52
  runtime-command demo, or a server-only public-content token adapter.
@@ -5,11 +5,5 @@ export const metadata = {
5
5
  };
6
6
 
7
7
  export default function DemoPage() {
8
- return (
9
- <main className="min-h-screen bg-background text-foreground">
10
- <div className="mx-auto flex min-h-screen max-w-5xl flex-col gap-6 px-6 py-8">
11
- <ManifestDemo />
12
- </div>
13
- </main>
14
- );
8
+ return <ManifestDemo />;
15
9
  }
@@ -1,6 +1,8 @@
1
1
  import type { Metadata } from "next";
2
2
  import type { ReactNode } from "react";
3
3
 
4
+ import { AuthenticatedAppLayoutShell } from "@/features/shell/components/authenticated-app-layout-shell";
5
+
4
6
  export const metadata: Metadata = {
5
7
  robots: {
6
8
  index: false,
@@ -13,5 +15,11 @@ export default function AuthenticatedAppLayout({
13
15
  }: {
14
16
  children: ReactNode;
15
17
  }) {
16
- return children;
18
+ return (
19
+ <AuthenticatedAppLayoutShell
20
+ appName={process.env.MW_TEMPLATE_APP_NAME || "Tenant App"}
21
+ >
22
+ {children}
23
+ </AuthenticatedAppLayoutShell>
24
+ );
17
25
  }
@@ -0,0 +1,181 @@
1
+ "use client";
2
+
3
+ import type { ReactNode } from "react";
4
+ import { startTransition } from "react";
5
+ import Link from "next/link";
6
+ import { useRouter } from "next/navigation";
7
+ import { LayoutDashboard, LogOut, PlaySquare } from "lucide-react";
8
+ import type {
9
+ TenantCustomerMembership,
10
+ TenantCustomerSession,
11
+ } from "@minutework/web-auth";
12
+ import {
13
+ useMinuteWorkAuth,
14
+ useMinuteWorkSession,
15
+ } from "@minutework/web-auth/react";
16
+
17
+ import { PanelFrame } from "@/design-system/patterns/panel-frame";
18
+ import { ThemeModeToggle } from "@/design-system/patterns/theme-mode-toggle";
19
+ import { Button } from "@/design-system/primitives/button";
20
+ import { appRoutes } from "@/lib/app-routes";
21
+
22
+ type AuthenticatedTenantCustomerSession = TenantCustomerSession & {
23
+ customer_membership: TenantCustomerMembership;
24
+ };
25
+
26
+ export function useAuthenticatedTenantCustomerSession(): AuthenticatedTenantCustomerSession {
27
+ const { session } = useMinuteWorkSession();
28
+
29
+ if (!session?.customer_membership) {
30
+ throw new Error(
31
+ "useAuthenticatedTenantCustomerSession must be rendered below AuthenticatedAppLayoutShell.",
32
+ );
33
+ }
34
+
35
+ return session as AuthenticatedTenantCustomerSession;
36
+ }
37
+
38
+ export function AuthenticatedAppLayoutShell({
39
+ appName,
40
+ children,
41
+ }: {
42
+ appName: string;
43
+ children: ReactNode;
44
+ }) {
45
+ const router = useRouter();
46
+ const { logout } = useMinuteWorkAuth();
47
+ const { session, loading, authenticated, emailVerificationRequired } =
48
+ useMinuteWorkSession();
49
+
50
+ function redirectToLogin() {
51
+ startTransition(() => {
52
+ router.replace(appRoutes.login);
53
+ router.refresh();
54
+ });
55
+ }
56
+
57
+ async function handleLogout() {
58
+ await logout().catch(() => undefined);
59
+ redirectToLogin();
60
+ }
61
+
62
+ if (loading) {
63
+ return (
64
+ <main className="min-h-screen bg-background text-foreground">
65
+ <div className="mx-auto flex min-h-screen max-w-5xl items-center px-6 py-10">
66
+ <PanelFrame tone="floating" radius="xl" padding="lg" className="w-full">
67
+ <p className="text-sm text-muted-foreground">Loading session</p>
68
+ </PanelFrame>
69
+ </div>
70
+ </main>
71
+ );
72
+ }
73
+
74
+ if (
75
+ emailVerificationRequired ||
76
+ session?.email_verification_required ||
77
+ session?.customer_membership?.status === "pending_verification"
78
+ ) {
79
+ return (
80
+ <main className="min-h-screen bg-background text-foreground">
81
+ <div className="mx-auto flex min-h-screen max-w-5xl items-center px-6 py-10">
82
+ <PanelFrame
83
+ tone="floating"
84
+ radius="xl"
85
+ padding="lg"
86
+ className="w-full space-y-4"
87
+ >
88
+ <div className="space-y-2">
89
+ <h1 className="text-3xl font-semibold tracking-tight">
90
+ Email verification required
91
+ </h1>
92
+ <p className="text-sm leading-7 text-muted-foreground">
93
+ Finish verification from your email before opening the workspace.
94
+ </p>
95
+ </div>
96
+ <Button onClick={redirectToLogin} className="w-fit">
97
+ Open login
98
+ </Button>
99
+ </PanelFrame>
100
+ </div>
101
+ </main>
102
+ );
103
+ }
104
+
105
+ if (!authenticated || !session?.customer_membership) {
106
+ return (
107
+ <main className="min-h-screen bg-background text-foreground">
108
+ <div className="mx-auto flex min-h-screen max-w-5xl items-center px-6 py-10">
109
+ <PanelFrame
110
+ tone="floating"
111
+ radius="xl"
112
+ padding="lg"
113
+ className="w-full space-y-4"
114
+ >
115
+ <div className="space-y-2">
116
+ <h1 className="text-3xl font-semibold tracking-tight">
117
+ Log in to continue
118
+ </h1>
119
+ <p className="text-sm leading-7 text-muted-foreground">
120
+ This area is available to verified customers.
121
+ </p>
122
+ </div>
123
+ <Button onClick={redirectToLogin} className="w-fit">
124
+ Open login
125
+ </Button>
126
+ </PanelFrame>
127
+ </div>
128
+ </main>
129
+ );
130
+ }
131
+
132
+ return (
133
+ <main className="min-h-screen bg-background text-foreground">
134
+ <div className="mx-auto flex min-h-screen max-w-7xl flex-col gap-6 px-6 py-8">
135
+ <header className="flex flex-col gap-6 xl:flex-row xl:items-start xl:justify-between">
136
+ <div className="space-y-2">
137
+ <p className="text-sm font-semibold uppercase tracking-widest text-muted-foreground">
138
+ {session.tenant.name}
139
+ </p>
140
+ <h1 className="max-w-3xl text-4xl font-semibold tracking-tight text-balance sm:text-5xl">
141
+ {appName}
142
+ </h1>
143
+ <p className="max-w-3xl text-base leading-7 text-muted-foreground sm:text-lg">
144
+ Signed in as {session.user?.email || session.user?.username}.
145
+ </p>
146
+ </div>
147
+
148
+ <div className="flex flex-col gap-3 sm:flex-row sm:items-start">
149
+ <ThemeModeToggle className="w-full sm:w-72" />
150
+ <Button
151
+ type="button"
152
+ variant="outline"
153
+ className="gap-2"
154
+ onClick={handleLogout}
155
+ >
156
+ <LogOut className="size-4" />
157
+ Log out
158
+ </Button>
159
+ </div>
160
+ </header>
161
+
162
+ <nav className="flex flex-wrap gap-2">
163
+ <Button asChild variant="default">
164
+ <Link href={appRoutes.appHome}>
165
+ <LayoutDashboard className="size-4" />
166
+ Dashboard
167
+ </Link>
168
+ </Button>
169
+ <Button asChild variant="outline">
170
+ <Link href={appRoutes.demo}>
171
+ <PlaySquare className="size-4" />
172
+ Demo
173
+ </Link>
174
+ </Button>
175
+ </nav>
176
+
177
+ {children}
178
+ </div>
179
+ </main>
180
+ );
181
+ }
@@ -1,120 +1,10 @@
1
1
  "use client";
2
2
 
3
- import { startTransition } from "react";
4
- import Link from "next/link";
5
- import { useRouter } from "next/navigation";
6
- import { LayoutDashboard, LogOut, PlaySquare } from "lucide-react";
7
-
8
- import { PanelFrame } from "@/design-system/patterns/panel-frame";
9
- import { ThemeModeToggle } from "@/design-system/patterns/theme-mode-toggle";
10
- import { Button } from "@/design-system/primitives/button";
11
3
  import { TenantDashboard } from "@/features/dashboard/components/tenant-dashboard";
12
- import { appRoutes } from "@/lib/app-routes";
13
- import { useMinuteWorkAuth, useMinuteWorkSession } from "@minutework/web-auth/react";
4
+ import { useAuthenticatedTenantCustomerSession } from "@/features/shell/components/authenticated-app-layout-shell";
14
5
 
15
6
  export function PrivateAppShell({ appName }: { appName: string }) {
16
- const router = useRouter();
17
- const { logout } = useMinuteWorkAuth();
18
- const { session, loading, authenticated, emailVerificationRequired } =
19
- useMinuteWorkSession();
20
-
21
- function redirectToLogin() {
22
- startTransition(() => {
23
- router.replace(appRoutes.login);
24
- router.refresh();
25
- });
26
- }
27
-
28
- async function handleLogout() {
29
- await logout().catch(() => undefined);
30
- redirectToLogin();
31
- }
32
-
33
- if (loading) {
34
- return (
35
- <main className="min-h-screen bg-background text-foreground">
36
- <div className="mx-auto flex min-h-screen max-w-5xl items-center px-6 py-10">
37
- <PanelFrame tone="floating" radius="xl" padding="lg" className="w-full">
38
- <p className="text-sm text-muted-foreground">Loading session</p>
39
- </PanelFrame>
40
- </div>
41
- </main>
42
- );
43
- }
44
-
45
- if (!authenticated || !session?.customer_membership) {
46
- return (
47
- <main className="min-h-screen bg-background text-foreground">
48
- <div className="mx-auto flex min-h-screen max-w-5xl items-center px-6 py-10">
49
- <PanelFrame tone="floating" radius="xl" padding="lg" className="w-full space-y-4">
50
- <div className="space-y-2">
51
- <h1 className="text-3xl font-semibold tracking-tight">
52
- {emailVerificationRequired
53
- ? "Email verification required"
54
- : "Log in to continue"}
55
- </h1>
56
- <p className="text-sm leading-7 text-muted-foreground">
57
- {emailVerificationRequired
58
- ? "Finish verification from your email before opening the workspace."
59
- : "This area is available to verified customers."}
60
- </p>
61
- </div>
62
- <Button onClick={redirectToLogin} className="w-fit">
63
- Open login
64
- </Button>
65
- </PanelFrame>
66
- </div>
67
- </main>
68
- );
69
- }
70
-
71
- return (
72
- <main className="min-h-screen bg-background text-foreground">
73
- <div className="mx-auto flex min-h-screen max-w-7xl flex-col gap-6 px-6 py-8">
74
- <header className="flex flex-col gap-6 xl:flex-row xl:items-start xl:justify-between">
75
- <div className="space-y-2">
76
- <p className="text-sm font-semibold uppercase tracking-widest text-muted-foreground">
77
- {session.tenant.name}
78
- </p>
79
- <h1 className="max-w-3xl text-4xl font-semibold tracking-tight text-balance sm:text-5xl">
80
- {appName}
81
- </h1>
82
- <p className="max-w-3xl text-base leading-7 text-muted-foreground sm:text-lg">
83
- Signed in as {session.user?.email || session.user?.username}.
84
- </p>
85
- </div>
86
-
87
- <div className="flex flex-col gap-3 sm:flex-row sm:items-start">
88
- <ThemeModeToggle className="w-full sm:w-72" />
89
- <Button
90
- type="button"
91
- variant="outline"
92
- className="gap-2"
93
- onClick={handleLogout}
94
- >
95
- <LogOut className="size-4" />
96
- Log out
97
- </Button>
98
- </div>
99
- </header>
100
-
101
- <nav className="flex flex-wrap gap-2">
102
- <Button asChild variant="default">
103
- <Link href={appRoutes.appHome}>
104
- <LayoutDashboard className="size-4" />
105
- Dashboard
106
- </Link>
107
- </Button>
108
- <Button asChild variant="outline">
109
- <Link href={appRoutes.demo}>
110
- <PlaySquare className="size-4" />
111
- Demo
112
- </Link>
113
- </Button>
114
- </nav>
7
+ const session = useAuthenticatedTenantCustomerSession();
115
8
 
116
- <TenantDashboard appName={appName} session={session} />
117
- </div>
118
- </main>
119
- );
9
+ return <TenantDashboard appName={appName} session={session} />;
120
10
  }
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
 
3
- import { createIdempotencyKey } from "@/mw/client";
3
+ import { createIdempotencyKey, mwAppId } from "@/mw/client";
4
4
  import { createTemplateMockMinuteWorkClient } from "@/mw/mock";
5
5
 
6
6
  describe("MinuteWork SDK mock substrate", () => {
@@ -17,5 +17,6 @@ describe("MinuteWork SDK mock substrate", () => {
17
17
 
18
18
  expect(queryResult.count).toBe(1);
19
19
  expect(actionResult.created).toBe(true);
20
+ expect(client.appId).toBe(mwAppId);
20
21
  });
21
22
  });
@@ -4,9 +4,11 @@
4
4
 
5
5
  import { createMockMinuteWorkClient } from "@minutework/web-auth/mock";
6
6
 
7
+ import { mwAppId } from "@/mw/client";
8
+
7
9
  export function createTemplateMockMinuteWorkClient() {
8
10
  return createMockMinuteWorkClient({
9
- appId: "tenant.app",
11
+ appId: mwAppId,
10
12
  verified: true,
11
13
  queryHandlers: {
12
14
  "demo.list": () => ({
@@ -1,4 +1,4 @@
1
- import { existsSync } from "node:fs";
1
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
 
@@ -22,6 +22,38 @@ const requiredPaths = [
22
22
  "src/mw/provider.tsx",
23
23
  "src/mw/mock.ts",
24
24
  ];
25
+ const appRoutesRoot = path.join(templateRoot, "src", "app", "app");
26
+ const appLayoutPath = path.join(appRoutesRoot, "layout.tsx");
27
+ const appLayoutSource = readFileSync(appLayoutPath, "utf8");
28
+ const appRoutePagePaths = collectNamedFiles(appRoutesRoot, new Set(["page.tsx"]));
29
+ const appRouteHandlerPaths = collectNamedFiles(appRoutesRoot, new Set(["route.ts", "route.tsx"]));
30
+ const guardedSurfaceSourcePaths = [
31
+ ...collectSourceFiles(appRoutesRoot),
32
+ ...collectSourceFiles(path.join(templateRoot, "src", "features")),
33
+ ];
34
+ const allowedGuardPatternFiles = new Map([
35
+ [
36
+ "src/app/app/layout.tsx",
37
+ new Set(["AuthenticatedAppLayoutShell"]),
38
+ ],
39
+ [
40
+ "src/features/auth/components/login-screen.tsx",
41
+ new Set(["useMinuteWorkAuth"]),
42
+ ],
43
+ [
44
+ "src/features/shell/components/authenticated-app-layout-shell.tsx",
45
+ new Set([
46
+ "AuthenticatedAppLayoutShell",
47
+ "useMinuteWorkAuth",
48
+ "useMinuteWorkSession",
49
+ ]),
50
+ ],
51
+ ]);
52
+ const forbiddenGuardPatterns = [
53
+ "useMinuteWorkSession",
54
+ "useMinuteWorkAuth",
55
+ "AuthenticatedAppLayoutShell",
56
+ ];
25
57
 
26
58
  const missingPaths = requiredPaths.filter(
27
59
  (relativePath) => !existsSync(path.join(templateRoot, relativePath)),
@@ -35,4 +67,91 @@ if (missingPaths.length > 0) {
35
67
  );
36
68
  }
37
69
 
70
+ if (!appLayoutSource.includes("AuthenticatedAppLayoutShell")) {
71
+ throw new Error("src/app/app/layout.tsx must wrap all /app routes in AuthenticatedAppLayoutShell.");
72
+ }
73
+
74
+ if (!appLayoutSource.includes("{children}")) {
75
+ throw new Error("src/app/app/layout.tsx must pass children through the authenticated layout guard.");
76
+ }
77
+
78
+ const appRouteRelativePaths = appRoutePagePaths.map((absolutePath) =>
79
+ path.relative(templateRoot, absolutePath),
80
+ );
81
+
82
+ if (!appRouteRelativePaths.includes(path.join("src", "app", "app", "demo", "page.tsx"))) {
83
+ throw new Error("Route contract must include /app/demo so the guarded layout cannot be bypassed.");
84
+ }
85
+
86
+ if (appRouteHandlerPaths.length > 0) {
87
+ throw new Error(
88
+ `Route handlers under /app bypass the authenticated layout guard.\n${appRouteHandlerPaths
89
+ .map((absolutePath) => `- ${path.relative(templateRoot, absolutePath)}`)
90
+ .join("\n")}`,
91
+ );
92
+ }
93
+
94
+ for (const sourcePath of guardedSurfaceSourcePaths) {
95
+ const source = readFileSync(sourcePath, "utf8");
96
+ const relativePath = normalizePath(path.relative(templateRoot, sourcePath));
97
+ const allowedPatterns = allowedGuardPatternFiles.get(relativePath) ?? new Set();
98
+ const matchedPattern = forbiddenGuardPatterns.find(
99
+ (pattern) => source.includes(pattern) && !allowedPatterns.has(pattern),
100
+ );
101
+
102
+ if (matchedPattern) {
103
+ throw new Error(
104
+ `${relativePath} must not own guarded-surface auth wiring (${matchedPattern}); keep raw SDK auth/session hooks in src/features/shell/components/authenticated-app-layout-shell.tsx or the public login screen.`,
105
+ );
106
+ }
107
+ }
108
+
38
109
  console.log("combined route contract is valid");
110
+
111
+ function collectNamedFiles(directory, names) {
112
+ const entries = readdirSync(directory, { withFileTypes: true });
113
+ const files = [];
114
+
115
+ for (const entry of entries) {
116
+ const absolutePath = path.join(directory, entry.name);
117
+ if (entry.isDirectory()) {
118
+ files.push(...collectNamedFiles(absolutePath, names));
119
+ continue;
120
+ }
121
+ if (!entry.isFile() || !names.has(entry.name)) {
122
+ continue;
123
+ }
124
+ if (!statSync(absolutePath).isFile()) {
125
+ continue;
126
+ }
127
+ files.push(absolutePath);
128
+ }
129
+
130
+ return files;
131
+ }
132
+
133
+ function collectSourceFiles(directory) {
134
+ const entries = readdirSync(directory, { withFileTypes: true });
135
+ const files = [];
136
+
137
+ for (const entry of entries) {
138
+ const absolutePath = path.join(directory, entry.name);
139
+ if (entry.isDirectory()) {
140
+ files.push(...collectSourceFiles(absolutePath));
141
+ continue;
142
+ }
143
+ if (!entry.isFile() || !/\.(tsx|ts)$/.test(entry.name)) {
144
+ continue;
145
+ }
146
+ if (!statSync(absolutePath).isFile()) {
147
+ continue;
148
+ }
149
+ files.push(absolutePath);
150
+ }
151
+
152
+ return files;
153
+ }
154
+
155
+ function normalizePath(value) {
156
+ return value.split(path.sep).join("/");
157
+ }
@@ -1,5 +1,4 @@
1
1
  import { spawn } from "node:child_process";
2
- import { createServer } from "node:http";
3
2
 
4
3
  const [command, ...args] = process.argv.slice(2);
5
4
  const storybookHeapMb = process.env.MW_STORYBOOK_NODE_HEAP_MB ?? "768";
@@ -9,123 +8,38 @@ if (!command) {
9
8
  process.exit(1);
10
9
  }
11
10
 
12
- const emptySnapshot = {
13
- site: {
14
- siteName: "MinuteWork Combined Starter",
15
- siteDescription: "Validation fixture snapshot for the combined starter.",
16
- organizationName: "MinuteWork",
17
- footerBlurb: "Validation fixture content.",
18
- primaryCta: {
19
- label: "Sign In",
20
- href: "/login",
21
- },
22
- secondaryCta: {
23
- label: "Docs",
24
- href: "/docs",
25
- },
26
- primaryNavigation: [
27
- { label: "Home", href: "/" },
28
- { label: "Pricing", href: "/pricing" },
29
- { label: "Docs", href: "/docs" },
30
- { label: "Blog", href: "/blog" },
31
- ],
32
- collections: {
33
- docs: {
34
- eyebrow: "Docs",
35
- title: "Documentation",
36
- description: "Validation fixture docs collection.",
37
- },
38
- blog: {
39
- eyebrow: "Blog",
40
- title: "Updates",
41
- description: "Validation fixture blog collection.",
42
- },
43
- },
44
- },
45
- marketingPages: [],
46
- docs: [],
47
- blog: [],
11
+ const childEnv = {
12
+ ...process.env,
13
+ NEXT_PUBLIC_MW_APP_ID: process.env.NEXT_PUBLIC_MW_APP_ID ?? "tenant.app",
14
+ MW_TEMPLATE_APP_NAME: process.env.MW_TEMPLATE_APP_NAME ?? "Tenant App",
15
+ MW_PUBLIC_BASE_URL: process.env.MW_PUBLIC_BASE_URL ?? "https://public.example.com",
48
16
  };
49
17
 
50
- const server = createServer((request, response) => {
51
- const requestUrl = new URL(request.url ?? "/", "http://127.0.0.1");
52
- if (
53
- request.method !== "GET" ||
54
- requestUrl.pathname !== "/api/v1/developer/public-site/snapshots/main-site/"
55
- ) {
56
- response.writeHead(404, { "content-type": "application/json" });
57
- response.end(JSON.stringify({ detail: "Not found" }));
58
- return;
59
- }
60
-
61
- const environment = requestUrl.searchParams.get("environment") ?? "preview";
62
- const sourceBoundary = environment === "live" ? "runtime_live" : "runtime_preview";
18
+ if (
19
+ command === "storybook" &&
20
+ !/\bmax-old-space-size=/.test(childEnv.NODE_OPTIONS ?? "")
21
+ ) {
22
+ childEnv.NODE_OPTIONS = [childEnv.NODE_OPTIONS, `--max-old-space-size=${storybookHeapMb}`]
23
+ .filter(Boolean)
24
+ .join(" ");
25
+ }
63
26
 
64
- response.writeHead(200, { "content-type": "application/json" });
65
- response.end(
66
- JSON.stringify({
67
- environment,
68
- source_boundary: sourceBoundary,
69
- snapshot: emptySnapshot,
70
- }),
71
- );
27
+ const child = spawn(command, args, {
28
+ env: childEnv,
29
+ stdio: "inherit",
72
30
  });
73
31
 
74
- try {
75
- const baseUrl = await new Promise((resolve, reject) => {
76
- server.once("error", reject);
77
- server.listen(0, "127.0.0.1", () => {
78
- const address = server.address();
79
- if (!address || typeof address === "string") {
80
- reject(new Error("Unable to resolve validation fixture server address."));
81
- return;
82
- }
83
- resolve(`http://127.0.0.1:${address.port}`);
84
- });
85
- });
86
-
87
- const exitCode = await new Promise((resolve, reject) => {
88
- const childEnv = {
89
- ...process.env,
90
- MW_CONTENT_API_TOKEN: "validate-content-token",
91
- MW_PLATFORM_BASE_URL: baseUrl,
92
- MW_PUBLIC_CONTENT_SOURCE: "minutework_cms",
93
- MW_PUBLIC_BASE_URL: "https://public.example.com",
94
- MW_PUBLIC_SITE_ENV: "preview",
95
- MW_PUBLIC_SITE_PROPERTY_KEY: "main-site",
96
- };
97
- if (
98
- command === "storybook" &&
99
- !/\bmax-old-space-size=/.test(childEnv.NODE_OPTIONS ?? "")
100
- ) {
101
- childEnv.NODE_OPTIONS = [childEnv.NODE_OPTIONS, `--max-old-space-size=${storybookHeapMb}`]
102
- .filter(Boolean)
103
- .join(" ");
104
- }
105
- const child = spawn(command, args, {
106
- env: childEnv,
107
- stdio: "inherit",
108
- });
32
+ child.once("error", (error) => {
33
+ console.error(error instanceof Error ? error.message : String(error));
34
+ process.exitCode = 1;
35
+ });
109
36
 
110
- child.once("error", reject);
111
- child.once("exit", (code, signal) => {
112
- if (signal) {
113
- reject(new Error(`Validation fixture command exited via signal ${signal}.`));
114
- return;
115
- }
116
- resolve(code ?? 1);
117
- });
118
- });
37
+ child.once("exit", (code, signal) => {
38
+ if (signal) {
39
+ console.error(`Validation fixture command exited via signal ${signal}.`);
40
+ process.exitCode = 1;
41
+ return;
42
+ }
119
43
 
120
- process.exitCode = exitCode;
121
- } finally {
122
- await new Promise((resolve, reject) => {
123
- server.close((error) => {
124
- if (error) {
125
- reject(error);
126
- return;
127
- }
128
- resolve(undefined);
129
- });
130
- });
131
- }
44
+ process.exitCode = code ?? 1;
45
+ });
@@ -11,13 +11,9 @@ export default defineConfig({
11
11
  },
12
12
  test: {
13
13
  env: {
14
- MW_PLATFORM_BASE_URL: "http://127.0.0.1:8000",
15
- MW_PUBLIC_CONTENT_SOURCE: "minutework_cms",
16
- MW_CONTENT_API_TOKEN: "test-content-token",
14
+ NEXT_PUBLIC_MW_APP_ID: "tenant.app",
15
+ MW_TEMPLATE_APP_NAME: "Tenant App",
17
16
  MW_PUBLIC_BASE_URL: "http://127.0.0.1:3000",
18
- MW_PUBLIC_SITE_PROPERTY_KEY: "main-site",
19
- MW_PUBLIC_SITE_ENV: "preview",
20
- MW_STATIC_PUBLIC_CONTENT_PATH: "content/public-site.json",
21
17
  },
22
18
  environment: "node",
23
19
  globals: true,
@@ -11,12 +11,9 @@ export async function bootstrapVmLocalWorkspaceEnv(options) {
11
11
  }
12
12
  if (options.starters.tenantApp) {
13
13
  await writeManagedEnvBlock(path.join(options.workspaceRoot, "tenant-app", ".env.local"), "tenant-app", {
14
- MW_PLATFORM_BASE_URL: workspaceWiring.platform_base_url ?? "",
15
- MW_CONTENT_API_TOKEN: workspaceWiring.content_token_plaintext ?? "",
14
+ NEXT_PUBLIC_MW_APP_ID: workspaceWiring.app_id ?? "tenant.app",
15
+ MW_TEMPLATE_APP_NAME: workspaceWiring.workspace_name ?? "Tenant App",
16
16
  MW_PUBLIC_BASE_URL: workspaceWiring.public_base_url ?? "",
17
- MW_PUBLIC_SITE_PROPERTY_KEY: workspaceWiring.public_site_property_key ?? "",
18
- MW_PUBLIC_SITE_ENV: workspaceWiring.public_site_env ?? "preview",
19
- MW_ENABLE_RUNTIME_COMMAND_EXAMPLE: "false",
20
17
  });
21
18
  }
22
19
  if (options.starters.sidecar) {
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-bootstrap.js","sourceRoot":"","sources":["../src/workspace-bootstrap.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace-bootstrap.js","sourceRoot":"","sources":["../src/workspace-bootstrap.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minutework",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "description": "MinuteWork CLI for workspace scaffolding, local preview workflows, and hosted preview deploys.",
5
5
  "type": "module",
6
6
  "bin": {