minutework 0.1.31 → 0.1.33

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 (168) hide show
  1. package/EXTERNAL_ALPHA.md +33 -33
  2. package/README.md +34 -34
  3. package/assets/claude-local/CLAUDE.md.template +12 -12
  4. package/assets/claude-local/skills/README.md +3 -1
  5. package/assets/claude-local/skills/app-pack-authoring/SKILL.md +17 -4
  6. package/assets/claude-local/skills/capability-gap-reporting/SKILL.md +3 -3
  7. package/assets/claude-local/skills/contract-first-public-intake/SKILL.md +11 -3
  8. package/assets/claude-local/skills/generated-workspace-architecture/SKILL.md +12 -5
  9. package/assets/claude-local/skills/layering-and-import-modes/SKILL.md +2 -2
  10. package/assets/claude-local/skills/openclaw-skill-importer/SKILL.md +2 -2
  11. package/assets/claude-local/skills/project-overview-and-strategy/SKILL.md +8 -8
  12. package/assets/claude-local/skills/published-web-and-mw-core-site/SKILL.md +11 -8
  13. package/assets/claude-local/skills/standalone-mobile-client/SKILL.md +6 -5
  14. package/assets/claude-local/skills/vuilder-discovery-output-contract/SKILL.md +6 -6
  15. package/assets/claude-local/skills/workspace-guidance-refresh/SKILL.md +4 -4
  16. package/assets/templates/fastapi-sidecar/pyproject.toml +1 -1
  17. package/assets/templates/fastapi-sidecar/src/fastapi_sidecar/main.py +1 -1
  18. package/assets/templates/mobile-app/.env.example +4 -4
  19. package/assets/templates/mobile-app/AGENTS.md +3 -3
  20. package/assets/templates/mobile-app/README.md +10 -10
  21. package/assets/templates/mobile-app/app/(app)/_layout.tsx +2 -2
  22. package/assets/templates/mobile-app/app/(app)/index.tsx +2 -2
  23. package/assets/templates/mobile-app/app/(auth)/login.tsx +3 -3
  24. package/assets/templates/mobile-app/app/_layout.tsx +1 -1
  25. package/assets/templates/mobile-app/babel.config.js +1 -1
  26. package/assets/templates/mobile-app/eas.json +1 -1
  27. package/assets/templates/mobile-app/expo-env.d.ts +1 -1
  28. package/assets/templates/mobile-app/metro.config.js +2 -2
  29. package/assets/templates/mobile-app/package.json +1 -1
  30. package/assets/templates/mobile-app/src/mw/client.ts +3 -3
  31. package/assets/templates/mobile-app/src/mw/contracts.ts +2 -2
  32. package/assets/templates/mobile-app/src/mw/endpoints.ts +2 -2
  33. package/assets/templates/mobile-app/src/mw/env.ts +4 -4
  34. package/assets/templates/mobile-app/src/mw/session.ts +1 -1
  35. package/assets/templates/mobile-app/template.json +1 -1
  36. package/assets/templates/mobile-app/tools/template/validate-template.mjs +2 -2
  37. package/assets/templates/mobile-app/tsconfig.json +1 -1
  38. package/assets/templates/next-tenant-app/.env.example +1 -1
  39. package/assets/templates/next-tenant-app/README.md +26 -138
  40. package/assets/templates/next-tenant-app/package.json +1 -0
  41. package/assets/templates/next-tenant-app/src/app/app/demo/page.tsx +15 -0
  42. package/assets/templates/next-tenant-app/src/app/app/layout.tsx +1 -4
  43. package/assets/templates/next-tenant-app/src/app/app/page.tsx +2 -17
  44. package/assets/templates/next-tenant-app/src/app/blog/[slug]/page.tsx +9 -67
  45. package/assets/templates/next-tenant-app/src/app/blog/page.tsx +10 -46
  46. package/assets/templates/next-tenant-app/src/app/docs/[...slug]/page.tsx +9 -65
  47. package/assets/templates/next-tenant-app/src/app/docs/page.tsx +10 -46
  48. package/assets/templates/next-tenant-app/src/app/layout.tsx +8 -10
  49. package/assets/templates/next-tenant-app/src/app/login/page.tsx +3 -23
  50. package/assets/templates/next-tenant-app/src/app/page.tsx +11 -44
  51. package/assets/templates/next-tenant-app/src/app/pricing/page.tsx +10 -44
  52. package/assets/templates/next-tenant-app/src/app/providers.tsx +2 -1
  53. package/assets/templates/next-tenant-app/src/app/robots.ts +7 -18
  54. package/assets/templates/next-tenant-app/src/app/sitemap.ts +4 -39
  55. package/assets/templates/next-tenant-app/src/features/auth/components/login-screen.tsx +97 -98
  56. package/assets/templates/next-tenant-app/src/features/dashboard/components/tenant-dashboard.tsx +43 -78
  57. package/assets/templates/next-tenant-app/src/features/demo/components/manifest-demo.tsx +89 -0
  58. package/assets/templates/next-tenant-app/src/features/public-shell/components/static-public-page.tsx +58 -0
  59. package/assets/templates/next-tenant-app/src/features/shell/components/private-app-shell.tsx +48 -552
  60. package/assets/templates/next-tenant-app/src/lib/app-routes.ts +2 -2
  61. package/assets/templates/next-tenant-app/src/lib/public-site.test.ts +1 -1
  62. package/assets/templates/next-tenant-app/src/lib/public-site.ts +5 -30
  63. package/assets/templates/next-tenant-app/src/mw/client.ts +18 -0
  64. package/assets/templates/next-tenant-app/src/mw/mock.test.ts +21 -0
  65. package/assets/templates/next-tenant-app/src/mw/mock.ts +35 -0
  66. package/assets/templates/next-tenant-app/src/mw/provider.tsx +17 -0
  67. package/assets/templates/next-tenant-app/template.json +3 -3
  68. package/assets/templates/next-tenant-app/template.schema.json +1 -0
  69. package/assets/templates/next-tenant-app/tools/template/validate-route-contract.mjs +4 -5
  70. package/assets/templates/next-tenant-app/tools/template/with-public-site-fixture.mjs +2 -2
  71. package/bin/minutework.js +1 -1
  72. package/dist/agent.js +7 -7
  73. package/dist/agent.js.map +1 -1
  74. package/dist/auth.js +7 -7
  75. package/dist/auth.js.map +1 -1
  76. package/dist/compile.js +5 -5
  77. package/dist/config.js +6 -6
  78. package/dist/config.js.map +1 -1
  79. package/dist/deploy.js +7 -7
  80. package/dist/deploy.js.map +1 -1
  81. package/dist/developer-client.js +2 -2
  82. package/dist/developer-client.js.map +1 -1
  83. package/dist/index.js +30 -30
  84. package/dist/index.js.map +1 -1
  85. package/dist/init.js +10 -10
  86. package/dist/init.js.map +1 -1
  87. package/dist/launcher.js +1 -1
  88. package/dist/launcher.js.map +1 -1
  89. package/dist/managed-engine.js +6 -6
  90. package/dist/managed-engine.js.map +1 -1
  91. package/dist/orchestrator-context.js +1 -1
  92. package/dist/orchestrator-context.js.map +1 -1
  93. package/dist/orchestrator.js +15 -15
  94. package/dist/orchestrator.js.map +1 -1
  95. package/dist/paths.js +1 -1
  96. package/dist/paths.js.map +1 -1
  97. package/dist/publish.js +3 -3
  98. package/dist/publish.js.map +1 -1
  99. package/dist/reporting.js +8 -8
  100. package/dist/reporting.js.map +1 -1
  101. package/dist/sandbox.js +5 -5
  102. package/dist/sandbox.js.map +1 -1
  103. package/dist/state.js +1 -1
  104. package/dist/state.js.map +1 -1
  105. package/dist/tokens.js +9 -9
  106. package/dist/tokens.js.map +1 -1
  107. package/dist/workspace-assets.js +6 -6
  108. package/dist/workspace-assets.js.map +1 -1
  109. package/dist/workspace.js +3 -3
  110. package/dist/workspace.js.map +1 -1
  111. package/package.json +3 -3
  112. package/vendor/workspace-mcp/context.d.ts +6 -6
  113. package/vendor/workspace-mcp/context.js +56 -56
  114. package/vendor/workspace-mcp/context.js.map +1 -1
  115. package/vendor/workspace-mcp/types.d.ts +4 -0
  116. package/assets/templates/next-tenant-app/src/app/(cms)/[...path]/page.tsx +0 -89
  117. package/assets/templates/next-tenant-app/src/app/api/auth/context/route.test.ts +0 -90
  118. package/assets/templates/next-tenant-app/src/app/api/auth/context/route.ts +0 -78
  119. package/assets/templates/next-tenant-app/src/app/api/auth/login/route.ts +0 -31
  120. package/assets/templates/next-tenant-app/src/app/api/auth/logout/route.ts +0 -16
  121. package/assets/templates/next-tenant-app/src/app/api/auth/password-change/route.test.ts +0 -79
  122. package/assets/templates/next-tenant-app/src/app/api/auth/password-change/route.ts +0 -40
  123. package/assets/templates/next-tenant-app/src/app/api/auth/password-status/route.test.ts +0 -42
  124. package/assets/templates/next-tenant-app/src/app/api/auth/password-status/route.ts +0 -29
  125. package/assets/templates/next-tenant-app/src/app/api/auth/session/route.ts +0 -26
  126. package/assets/templates/next-tenant-app/src/app/api/gateway/commands/[runId]/route.test.ts +0 -40
  127. package/assets/templates/next-tenant-app/src/app/api/gateway/commands/[runId]/route.ts +0 -47
  128. package/assets/templates/next-tenant-app/src/app/api/gateway/commands/route.test.ts +0 -43
  129. package/assets/templates/next-tenant-app/src/app/api/gateway/commands/route.ts +0 -45
  130. package/assets/templates/next-tenant-app/src/app/app/examples/runtime-commands/page.test.ts +0 -83
  131. package/assets/templates/next-tenant-app/src/app/app/examples/runtime-commands/page.tsx +0 -30
  132. package/assets/templates/next-tenant-app/src/app/app/page.test.ts +0 -62
  133. package/assets/templates/next-tenant-app/src/app/app/private-content-source.test.ts +0 -88
  134. package/assets/templates/next-tenant-app/src/app/blog/[slug]/page.test.ts +0 -70
  135. package/assets/templates/next-tenant-app/src/app/blog/page.test.ts +0 -46
  136. package/assets/templates/next-tenant-app/src/app/docs/[...slug]/page.test.ts +0 -70
  137. package/assets/templates/next-tenant-app/src/app/docs/page.test.ts +0 -46
  138. package/assets/templates/next-tenant-app/src/app/login/page.test.ts +0 -55
  139. package/assets/templates/next-tenant-app/src/app/page.test.ts +0 -90
  140. package/assets/templates/next-tenant-app/src/app/pricing/page.test.ts +0 -59
  141. package/assets/templates/next-tenant-app/src/app/robots.test.ts +0 -40
  142. package/assets/templates/next-tenant-app/src/app/sitemap.test.ts +0 -63
  143. package/assets/templates/next-tenant-app/src/features/examples/runtime-command-demo/components/runtime-command-demo.tsx +0 -342
  144. package/assets/templates/next-tenant-app/src/features/public-shell/components/content-article.tsx +0 -66
  145. package/assets/templates/next-tenant-app/src/features/public-shell/components/content-collection.tsx +0 -108
  146. package/assets/templates/next-tenant-app/src/features/public-shell/components/marketing-page-canvas.tsx +0 -111
  147. package/assets/templates/next-tenant-app/src/features/public-shell/components/public-site-shell.tsx +0 -111
  148. package/assets/templates/next-tenant-app/src/features/public-shell/components/section-renderer.test.ts +0 -38
  149. package/assets/templates/next-tenant-app/src/features/public-shell/components/section-renderer.tsx +0 -145
  150. package/assets/templates/next-tenant-app/src/lib/content/__fixtures__/public-site-snapshot.ts +0 -189
  151. package/assets/templates/next-tenant-app/src/lib/content/adapter.server.test.ts +0 -444
  152. package/assets/templates/next-tenant-app/src/lib/content/adapter.server.ts +0 -383
  153. package/assets/templates/next-tenant-app/src/lib/content/contracts.test.ts +0 -138
  154. package/assets/templates/next-tenant-app/src/lib/content/contracts.ts +0 -399
  155. package/assets/templates/next-tenant-app/src/lib/content/custom-adapter.ts +0 -5
  156. package/assets/templates/next-tenant-app/src/lib/content/empty-state.ts +0 -96
  157. package/assets/templates/next-tenant-app/src/lib/content/release-manifest.test.ts +0 -93
  158. package/assets/templates/next-tenant-app/src/lib/content/release-manifest.ts +0 -123
  159. package/assets/templates/next-tenant-app/src/lib/platform/auth.server.test.ts +0 -75
  160. package/assets/templates/next-tenant-app/src/lib/platform/auth.server.ts +0 -25
  161. package/assets/templates/next-tenant-app/src/lib/platform/client.server.test.ts +0 -170
  162. package/assets/templates/next-tenant-app/src/lib/platform/client.server.ts +0 -661
  163. package/assets/templates/next-tenant-app/src/lib/platform/contracts.ts +0 -131
  164. package/assets/templates/next-tenant-app/src/lib/platform/endpoints.server.ts +0 -34
  165. package/assets/templates/next-tenant-app/src/lib/platform/env.server.test.ts +0 -211
  166. package/assets/templates/next-tenant-app/src/lib/platform/env.server.ts +0 -151
  167. package/assets/templates/next-tenant-app/src/lib/platform/route-response.ts +0 -33
  168. package/assets/templates/next-tenant-app/src/lib/platform/session.server.ts +0 -108
@@ -1,70 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- const notFound = vi.fn(() => {
4
- throw new Error("notFound");
5
- });
6
- const getEntry = vi.fn();
7
- const getSiteConfig = vi.fn();
8
- const listEntries = vi.fn();
9
-
10
- vi.mock("next/navigation", () => ({
11
- notFound,
12
- }));
13
-
14
- vi.mock("@/features/public-shell/components/content-article", () => ({
15
- ContentArticle: () => null,
16
- }));
17
-
18
- vi.mock("@/features/public-shell/components/public-site-shell", () => ({
19
- PublicSiteShell: ({ children }: { children: unknown }) => children,
20
- }));
21
-
22
- vi.mock("@/lib/content/adapter.server", () => ({
23
- getEntry,
24
- getSiteConfig,
25
- listEntries,
26
- }));
27
-
28
- describe("blog article page", () => {
29
- beforeEach(() => {
30
- vi.clearAllMocks();
31
- vi.resetModules();
32
- });
33
-
34
- it("returns notFound for unknown blog slugs", async () => {
35
- getSiteConfig.mockResolvedValue({
36
- collections: {
37
- blog: {
38
- title: "Starter Blog",
39
- description: "Blog",
40
- },
41
- },
42
- siteName: "PandaWork Combined Starter",
43
- });
44
- getEntry.mockResolvedValue(null);
45
-
46
- const page = await import("./page");
47
-
48
- await expect(
49
- page.default({
50
- params: Promise.resolve({ slug: "missing-entry" }),
51
- }),
52
- ).rejects.toThrow("notFound");
53
- expect(notFound).toHaveBeenCalledTimes(1);
54
- });
55
-
56
- it("derives static params from published blog entries", async () => {
57
- listEntries.mockResolvedValue([
58
- { slug: ["public-site-api-default"] },
59
- { slug: ["launch-note"] },
60
- ]);
61
-
62
- const page = await import("./page");
63
-
64
- await expect(page.generateStaticParams()).resolves.toEqual([
65
- { slug: "public-site-api-default" },
66
- { slug: "launch-note" },
67
- ]);
68
- expect(page.dynamicParams).toBe(false);
69
- });
70
- });
@@ -1,46 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- const getSiteConfig = vi.fn();
4
- const listEntries = vi.fn();
5
-
6
- vi.mock("@/features/public-shell/components/content-collection", () => ({
7
- ContentCollection: () => null,
8
- }));
9
-
10
- vi.mock("@/features/public-shell/components/public-site-shell", () => ({
11
- PublicSiteShell: ({ children }: { children: unknown }) => children,
12
- }));
13
-
14
- vi.mock("@/lib/content/adapter.server", () => ({
15
- getSiteConfig,
16
- listEntries,
17
- }));
18
-
19
- vi.mock("next/navigation", () => ({
20
- notFound: vi.fn(),
21
- }));
22
-
23
- describe("blog index page", () => {
24
- beforeEach(() => {
25
- vi.clearAllMocks();
26
- vi.resetModules();
27
- });
28
-
29
- it("renders the public blog collection", async () => {
30
- getSiteConfig.mockResolvedValue({
31
- collections: {
32
- blog: {
33
- eyebrow: "Blog",
34
- title: "Starter Blog",
35
- description: "Combined starter notes.",
36
- },
37
- },
38
- });
39
- listEntries.mockResolvedValue([]);
40
-
41
- const page = await import("./page");
42
-
43
- await expect(page.default()).resolves.toBeDefined();
44
- expect(listEntries).toHaveBeenCalledWith("blog");
45
- });
46
- });
@@ -1,70 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- const notFound = vi.fn(() => {
4
- throw new Error("notFound");
5
- });
6
- const getEntry = vi.fn();
7
- const getSiteConfig = vi.fn();
8
- const listEntries = vi.fn();
9
-
10
- vi.mock("next/navigation", () => ({
11
- notFound,
12
- }));
13
-
14
- vi.mock("@/features/public-shell/components/content-article", () => ({
15
- ContentArticle: () => null,
16
- }));
17
-
18
- vi.mock("@/features/public-shell/components/public-site-shell", () => ({
19
- PublicSiteShell: ({ children }: { children: unknown }) => children,
20
- }));
21
-
22
- vi.mock("@/lib/content/adapter.server", () => ({
23
- getEntry,
24
- getSiteConfig,
25
- listEntries,
26
- }));
27
-
28
- describe("docs article page", () => {
29
- beforeEach(() => {
30
- vi.clearAllMocks();
31
- vi.resetModules();
32
- });
33
-
34
- it("returns notFound for unknown docs slugs", async () => {
35
- getSiteConfig.mockResolvedValue({
36
- collections: {
37
- docs: {
38
- title: "Starter Docs",
39
- description: "Docs",
40
- },
41
- },
42
- siteName: "PandaWork Combined Starter",
43
- });
44
- getEntry.mockResolvedValue(null);
45
-
46
- const page = await import("./page");
47
-
48
- await expect(
49
- page.default({
50
- params: Promise.resolve({ slug: ["missing-entry"] }),
51
- }),
52
- ).rejects.toThrow("notFound");
53
- expect(notFound).toHaveBeenCalledTimes(1);
54
- });
55
-
56
- it("derives static params from published docs entries", async () => {
57
- listEntries.mockResolvedValue([
58
- { slug: ["guides", "public-site-content"] },
59
- { slug: ["reference", "api"] },
60
- ]);
61
-
62
- const page = await import("./page");
63
-
64
- await expect(page.generateStaticParams()).resolves.toEqual([
65
- { slug: ["guides", "public-site-content"] },
66
- { slug: ["reference", "api"] },
67
- ]);
68
- expect(page.dynamicParams).toBe(false);
69
- });
70
- });
@@ -1,46 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- const getSiteConfig = vi.fn();
4
- const listEntries = vi.fn();
5
-
6
- vi.mock("@/features/public-shell/components/content-collection", () => ({
7
- ContentCollection: () => null,
8
- }));
9
-
10
- vi.mock("@/features/public-shell/components/public-site-shell", () => ({
11
- PublicSiteShell: ({ children }: { children: unknown }) => children,
12
- }));
13
-
14
- vi.mock("@/lib/content/adapter.server", () => ({
15
- getSiteConfig,
16
- listEntries,
17
- }));
18
-
19
- vi.mock("next/navigation", () => ({
20
- notFound: vi.fn(),
21
- }));
22
-
23
- describe("docs index page", () => {
24
- beforeEach(() => {
25
- vi.clearAllMocks();
26
- vi.resetModules();
27
- });
28
-
29
- it("renders the public docs collection", async () => {
30
- getSiteConfig.mockResolvedValue({
31
- collections: {
32
- docs: {
33
- eyebrow: "Docs",
34
- title: "Starter Docs",
35
- description: "Route content through the adapter seam.",
36
- },
37
- },
38
- });
39
- listEntries.mockResolvedValue([]);
40
-
41
- const page = await import("./page");
42
-
43
- await expect(page.default()).resolves.toBeDefined();
44
- expect(listEntries).toHaveBeenCalledWith("docs");
45
- });
46
- });
@@ -1,55 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- const redirect = vi.fn((href: string) => {
4
- throw new Error(`redirect:${href}`);
5
- });
6
- const resolveCurrentSession = vi.fn();
7
- const readPlatformAuthState = vi.fn();
8
-
9
- vi.mock("next/navigation", () => ({
10
- redirect,
11
- }));
12
-
13
- vi.mock("@/features/auth/components/login-screen", () => ({
14
- LoginScreen: () => null,
15
- }));
16
-
17
- vi.mock("@/lib/platform/client.server", () => ({
18
- resolveCurrentSession,
19
- }));
20
-
21
- vi.mock("@/lib/platform/endpoints.server", () => ({
22
- platformAuthEndpoints: {
23
- operatorConsole: "http://127.0.0.1:8000/ops/login/",
24
- },
25
- }));
26
-
27
- vi.mock("@/lib/platform/env.server", () => ({
28
- env: {
29
- MW_TEMPLATE_APP_NAME: "PandaWork Combined Starter",
30
- },
31
- }));
32
-
33
- vi.mock("@/lib/platform/session.server", () => ({
34
- readPlatformAuthState,
35
- }));
36
-
37
- describe("login page", () => {
38
- beforeEach(() => {
39
- vi.clearAllMocks();
40
- vi.resetModules();
41
- });
42
-
43
- it("redirects authenticated users back to the home shell", async () => {
44
- readPlatformAuthState.mockResolvedValue({
45
- sessionId: "session-1",
46
- csrfToken: "csrf-1",
47
- });
48
- resolveCurrentSession.mockResolvedValue({ authenticated: true });
49
-
50
- const page = await import("./page");
51
-
52
- await expect(page.default()).rejects.toThrow("redirect:/app");
53
- expect(redirect).toHaveBeenCalledWith("/app");
54
- });
55
- });
@@ -1,90 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- const getMarketingPage = vi.fn();
4
- const getSiteConfig = vi.fn();
5
- const redirect = vi.fn();
6
- const originalPublicContentSource = process.env.MW_PUBLIC_CONTENT_SOURCE;
7
-
8
- vi.mock("@/features/public-shell/components/marketing-page-canvas", () => ({
9
- MarketingPageCanvas: () => null,
10
- }));
11
-
12
- vi.mock("@/features/public-shell/components/public-site-shell", () => ({
13
- PublicSiteShell: ({ children }: { children: unknown }) => children,
14
- }));
15
-
16
- vi.mock("@/lib/content/adapter.server", () => ({
17
- getMarketingPage,
18
- getSiteConfig,
19
- }));
20
-
21
- vi.mock("next/navigation", () => ({
22
- redirect,
23
- }));
24
-
25
- describe("home page", () => {
26
- beforeEach(() => {
27
- if (originalPublicContentSource === undefined) {
28
- delete process.env.MW_PUBLIC_CONTENT_SOURCE;
29
- } else {
30
- process.env.MW_PUBLIC_CONTENT_SOURCE = originalPublicContentSource;
31
- }
32
- vi.clearAllMocks();
33
- vi.resetModules();
34
- });
35
-
36
- afterEach(() => {
37
- if (originalPublicContentSource === undefined) {
38
- delete process.env.MW_PUBLIC_CONTENT_SOURCE;
39
- } else {
40
- process.env.MW_PUBLIC_CONTENT_SOURCE = originalPublicContentSource;
41
- }
42
- });
43
-
44
- it("renders public home content without auth redirects", async () => {
45
- getSiteConfig.mockResolvedValue({
46
- siteName: "PandaWork Combined Starter",
47
- siteDescription: "Combined starter",
48
- primaryCta: { label: "Sign In", href: "/login" },
49
- secondaryCta: { label: "Docs", href: "/docs" },
50
- });
51
- getMarketingPage.mockResolvedValue({
52
- pageKey: "home",
53
- });
54
-
55
- const page = await import("./page");
56
-
57
- await expect(page.default()).resolves.toBeDefined();
58
- expect(getSiteConfig).toHaveBeenCalledTimes(1);
59
- expect(getMarketingPage).toHaveBeenCalledWith("home");
60
- });
61
-
62
- it("renders the built-in empty home shell when no home page has been published", async () => {
63
- getSiteConfig.mockResolvedValue({
64
- siteName: "PandaWork Combined Starter",
65
- siteDescription: "Combined starter",
66
- primaryCta: { label: "Sign In", href: "/login" },
67
- secondaryCta: { label: "Docs", href: "/docs" },
68
- });
69
- getMarketingPage.mockResolvedValue(null);
70
-
71
- const page = await import("./page");
72
-
73
- await expect(page.default()).resolves.toBeDefined();
74
- });
75
-
76
- it("redirects the private-only root route to /app", async () => {
77
- vi.resetModules();
78
- process.env.MW_PUBLIC_CONTENT_SOURCE = "none";
79
- redirect.mockImplementation(() => {
80
- throw new Error("NEXT_REDIRECT");
81
- });
82
-
83
- const page = await import("./page");
84
-
85
- await expect(page.default()).rejects.toThrow("NEXT_REDIRECT");
86
- expect(redirect).toHaveBeenCalledWith("/app");
87
- expect(getSiteConfig).not.toHaveBeenCalled();
88
- expect(getMarketingPage).not.toHaveBeenCalled();
89
- });
90
- });
@@ -1,59 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- const getMarketingPage = vi.fn();
4
- const getSiteConfig = vi.fn();
5
-
6
- vi.mock("@/features/public-shell/components/marketing-page-canvas", () => ({
7
- MarketingPageCanvas: () => null,
8
- }));
9
-
10
- vi.mock("@/features/public-shell/components/public-site-shell", () => ({
11
- PublicSiteShell: ({ children }: { children: unknown }) => children,
12
- }));
13
-
14
- vi.mock("@/lib/content/adapter.server", () => ({
15
- getMarketingPage,
16
- getSiteConfig,
17
- }));
18
-
19
- vi.mock("next/navigation", () => ({
20
- notFound: vi.fn(),
21
- }));
22
-
23
- describe("pricing page", () => {
24
- beforeEach(() => {
25
- vi.clearAllMocks();
26
- vi.resetModules();
27
- });
28
-
29
- it("renders seeded pricing content", async () => {
30
- getSiteConfig.mockResolvedValue({
31
- siteName: "PandaWork Combined Starter",
32
- siteDescription: "Combined starter",
33
- primaryCta: { label: "Sign In", href: "/login" },
34
- secondaryCta: { label: "Docs", href: "/docs" },
35
- });
36
- getMarketingPage.mockResolvedValue({
37
- pageKey: "pricing",
38
- });
39
-
40
- const page = await import("./page");
41
-
42
- await expect(page.default()).resolves.toBeDefined();
43
- expect(getMarketingPage).toHaveBeenCalledWith("pricing");
44
- });
45
-
46
- it("renders the built-in empty pricing shell when no pricing page has been published", async () => {
47
- getSiteConfig.mockResolvedValue({
48
- siteName: "PandaWork Combined Starter",
49
- siteDescription: "Combined starter",
50
- primaryCta: { label: "Sign In", href: "/login" },
51
- secondaryCta: { label: "Docs", href: "/docs" },
52
- });
53
- getMarketingPage.mockResolvedValue(null);
54
-
55
- const page = await import("./page");
56
-
57
- await expect(page.default()).resolves.toBeDefined();
58
- });
59
- });
@@ -1,40 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- describe("robots route", () => {
4
- beforeEach(() => {
5
- vi.resetModules();
6
- vi.doUnmock("@/lib/public-site");
7
- });
8
-
9
- it("returns a public robots policy and sitemap url", async () => {
10
- expect(process.env.MW_PUBLIC_BASE_URL).toBe("http://127.0.0.1:3000");
11
-
12
- const { default: robots } = await import("./robots");
13
- const metadata = robots();
14
-
15
- expect(metadata.host).toBe("127.0.0.1:3000");
16
- expect(metadata.sitemap).toBe("http://127.0.0.1:3000/sitemap.xml");
17
- expect(metadata.rules).toEqual([
18
- {
19
- userAgent: "*",
20
- allow: "/",
21
- },
22
- ]);
23
- });
24
-
25
- it("disallows indexing when public content is disabled", async () => {
26
- vi.doMock("@/lib/public-site", () => ({
27
- isPublicContentDisabled: () => true,
28
- resolvePublicMetadataBase: () => null,
29
- }));
30
-
31
- const { default: robots } = await import("./robots");
32
-
33
- expect(robots().rules).toEqual([
34
- {
35
- userAgent: "*",
36
- disallow: "/",
37
- },
38
- ]);
39
- });
40
- });
@@ -1,63 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- const { listEntries } = vi.hoisted(() => ({
4
- listEntries: vi.fn(),
5
- }));
6
-
7
- vi.mock("@/lib/content/adapter.server", () => ({
8
- listEntries,
9
- }));
10
-
11
- describe("sitemap route", () => {
12
- beforeEach(() => {
13
- vi.clearAllMocks();
14
- vi.resetModules();
15
- vi.doUnmock("@/lib/public-site");
16
- });
17
-
18
- it("includes the public route surface and published content entries", async () => {
19
- listEntries.mockImplementation(async (kind: string) =>
20
- kind === "docs"
21
- ? [{ slug: ["guides", "public-site-content"] }]
22
- : [{ slug: ["public-site-api-default"], publishedAt: "2026-03-26T10:00:00.000Z" }],
23
- );
24
-
25
- expect(process.env.MW_PUBLIC_BASE_URL).toBe("http://127.0.0.1:3000");
26
-
27
- const { default: sitemap } = await import("./sitemap");
28
- const entries = await sitemap();
29
- const urls = entries.map((entry) => entry.url);
30
-
31
- expect(urls).toContain("http://127.0.0.1:3000/");
32
- expect(urls).toContain("http://127.0.0.1:3000/pricing");
33
- expect(urls).toContain("http://127.0.0.1:3000/docs");
34
- expect(urls).toContain("http://127.0.0.1:3000/docs/guides/public-site-content");
35
- expect(urls).toContain("http://127.0.0.1:3000/blog");
36
- expect(urls).toContain("http://127.0.0.1:3000/blog/public-site-api-default");
37
- });
38
-
39
- it("keeps home and pricing in the sitemap even when the snapshot has no marketing pages", async () => {
40
- listEntries.mockResolvedValue([]);
41
-
42
- const { default: sitemap } = await import("./sitemap");
43
- const entries = await sitemap();
44
- const urls = entries.map((entry) => entry.url);
45
-
46
- expect(urls).toContain("http://127.0.0.1:3000/");
47
- expect(urls).toContain("http://127.0.0.1:3000/pricing");
48
- expect(urls).toContain("http://127.0.0.1:3000/docs");
49
- expect(urls).toContain("http://127.0.0.1:3000/blog");
50
- });
51
-
52
- it("returns an empty sitemap when public content is disabled", async () => {
53
- vi.doMock("@/lib/public-site", () => ({
54
- isPublicContentDisabled: () => true,
55
- resolvePublicSiteUrl: vi.fn(),
56
- }));
57
-
58
- const { default: sitemap } = await import("./sitemap");
59
-
60
- await expect(sitemap()).resolves.toEqual([]);
61
- expect(listEntries).not.toHaveBeenCalled();
62
- });
63
- });