zudoku 0.77.0 → 0.78.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.
Files changed (114) hide show
  1. package/dist/cli/cli.js +649 -183
  2. package/dist/cli/worker.js +6 -4
  3. package/dist/declarations/app/adapter.d.ts +12 -0
  4. package/dist/declarations/app/adapters/cloudflare.d.ts +8 -0
  5. package/dist/declarations/app/adapters/lambda.d.ts +13 -0
  6. package/dist/declarations/app/adapters/node.d.ts +12 -0
  7. package/dist/declarations/app/adapters/vercel.d.ts +14 -0
  8. package/dist/declarations/app/entry.client.d.ts +2 -0
  9. package/dist/declarations/app/entry.server.d.ts +13 -0
  10. package/dist/declarations/app/protectChunks.d.ts +17 -0
  11. package/dist/declarations/app/wrapProtectedRoutes.d.ts +4 -0
  12. package/dist/declarations/config/validators/HeaderNavigationSchema.d.ts +216 -80
  13. package/dist/declarations/config/validators/ZudokuConfig.d.ts +122 -30
  14. package/dist/declarations/config/validators/icon-types.d.ts +1 -1
  15. package/dist/declarations/lib/authentication/authentication.d.ts +7 -0
  16. package/dist/declarations/lib/authentication/cookie-sync.d.ts +3 -0
  17. package/dist/declarations/lib/authentication/cookies.d.ts +10 -0
  18. package/dist/declarations/lib/authentication/providers/azureb2c.d.ts +6 -1
  19. package/dist/declarations/lib/authentication/providers/clerk.d.ts +16 -2
  20. package/dist/declarations/lib/authentication/providers/openid.d.ts +9 -2
  21. package/dist/declarations/lib/authentication/providers/supabase.d.ts +2 -0
  22. package/dist/declarations/lib/authentication/session-handler.d.ts +81 -0
  23. package/dist/declarations/lib/authentication/state.d.ts +7 -0
  24. package/dist/declarations/lib/authentication/verify-cache.d.ts +2 -0
  25. package/dist/declarations/lib/components/Bootstrap.d.ts +2 -2
  26. package/dist/declarations/lib/components/Heading.d.ts +1 -1
  27. package/dist/declarations/lib/components/context/RenderContext.d.ts +6 -0
  28. package/dist/declarations/lib/core/RouteGuard.d.ts +11 -0
  29. package/dist/declarations/lib/core/ZudokuContext.d.ts +8 -2
  30. package/dist/declarations/lib/manifest.d.ts +25 -0
  31. package/dist/declarations/lib/plugins/openapi/GeneratedExampleSidecarBox.d.ts +2 -1
  32. package/dist/declarations/lib/plugins/openapi/playground/Playground.d.ts +6 -1
  33. package/dist/declarations/lib/plugins/openapi/playground/buildRequestBody.d.ts +14 -0
  34. package/dist/declarations/lib/plugins/openapi/playground/request-panel/UrlEncodedField.d.ts +8 -0
  35. package/dist/declarations/lib/plugins/openapi/util/createHttpSnippet.d.ts +3 -3
  36. package/dist/declarations/lib/plugins/openapi/util/formatRequestBody.d.ts +11 -0
  37. package/dist/declarations/lib/util/url.d.ts +2 -0
  38. package/dist/flat-config.d.ts +6 -1
  39. package/docs/configuration/api-reference.md +4 -0
  40. package/docs/configuration/docs.md +1 -1
  41. package/docs/configuration/overview.md +6 -0
  42. package/docs/configuration/protected-routes.md +21 -4
  43. package/docs/guides/mcp-servers.md +1 -1
  44. package/docs/guides/server-side-content-protection.md +207 -0
  45. package/package.json +43 -21
  46. package/src/app/adapter.ts +16 -0
  47. package/src/app/adapters/cloudflare.ts +18 -0
  48. package/src/app/adapters/lambda.ts +36 -0
  49. package/src/app/adapters/node.ts +32 -0
  50. package/src/app/adapters/vercel.ts +39 -0
  51. package/src/app/demo.tsx +2 -2
  52. package/src/app/entry.client.tsx +17 -7
  53. package/src/app/entry.server.tsx +128 -9
  54. package/src/app/main.tsx +21 -3
  55. package/src/app/protectChunks.ts +64 -0
  56. package/src/app/standalone.tsx +2 -2
  57. package/src/app/wrapProtectedRoutes.ts +82 -0
  58. package/src/config/validators/ZudokuConfig.ts +22 -2
  59. package/src/config/validators/icon-types.ts +17 -0
  60. package/src/lib/authentication/authentication.ts +15 -0
  61. package/src/lib/authentication/cookie-sync.ts +90 -0
  62. package/src/lib/authentication/cookies.ts +54 -0
  63. package/src/lib/authentication/hook.ts +13 -0
  64. package/src/lib/authentication/providers/azureb2c.tsx +70 -2
  65. package/src/lib/authentication/providers/clerk.tsx +82 -2
  66. package/src/lib/authentication/providers/openid.tsx +65 -1
  67. package/src/lib/authentication/providers/supabase.tsx +30 -2
  68. package/src/lib/authentication/session-handler.ts +164 -0
  69. package/src/lib/authentication/state.ts +36 -5
  70. package/src/lib/authentication/verify-cache.ts +32 -0
  71. package/src/lib/components/Bootstrap.tsx +20 -14
  72. package/src/lib/components/Header.tsx +58 -58
  73. package/src/lib/components/MobileTopNavigation.tsx +68 -68
  74. package/src/lib/components/Zudoku.tsx +14 -1
  75. package/src/lib/components/context/RenderContext.ts +8 -0
  76. package/src/lib/components/context/ZudokuContext.ts +2 -1
  77. package/src/lib/core/RouteGuard.tsx +50 -29
  78. package/src/lib/core/ZudokuContext.ts +42 -6
  79. package/src/lib/errors/RouterError.tsx +43 -1
  80. package/src/lib/manifest.ts +62 -0
  81. package/src/lib/oas/parser/dereference/index.ts +2 -1
  82. package/src/lib/oas/parser/dereference/resolveRef.ts +2 -1
  83. package/src/lib/oas/parser/index.ts +1 -1
  84. package/src/lib/plugins/markdown/MdxPage.tsx +3 -1
  85. package/src/lib/plugins/openapi/GeneratedExampleSidecarBox.tsx +3 -1
  86. package/src/lib/plugins/openapi/MCPEndpoint.tsx +26 -2
  87. package/src/lib/plugins/openapi/Sidecar.tsx +16 -2
  88. package/src/lib/plugins/openapi/SidecarExamples.tsx +9 -27
  89. package/src/lib/plugins/openapi/client/createServer.ts +13 -4
  90. package/src/lib/plugins/openapi/playground/BodyPanel.tsx +64 -3
  91. package/src/lib/plugins/openapi/playground/Playground.tsx +17 -18
  92. package/src/lib/plugins/openapi/playground/buildRequestBody.ts +46 -0
  93. package/src/lib/plugins/openapi/playground/request-panel/UrlEncodedField.tsx +32 -0
  94. package/src/lib/plugins/openapi/util/createHttpSnippet.ts +111 -67
  95. package/src/lib/plugins/openapi/util/formatRequestBody.ts +84 -0
  96. package/src/lib/plugins/search-pagefind/index.tsx +1 -4
  97. package/src/lib/util/flattenAllOf.ts +13 -0
  98. package/src/lib/util/os.ts +1 -0
  99. package/src/lib/util/url.ts +13 -0
  100. package/src/vite/api/SchemaManager.ts +16 -2
  101. package/src/vite/build.ts +84 -24
  102. package/src/vite/config.ts +50 -5
  103. package/src/vite/dev-server.ts +61 -8
  104. package/src/vite/manifest.ts +15 -0
  105. package/src/vite/plugin-api.ts +3 -1
  106. package/src/vite/plugin-markdown-export.ts +3 -9
  107. package/src/vite/prerender/worker.ts +2 -4
  108. package/src/vite/protected/annotator.ts +136 -0
  109. package/src/vite/protected/build.ts +151 -0
  110. package/src/vite/protected/registry.ts +82 -0
  111. package/src/vite/ssr-templates/cloudflare.ts +5 -18
  112. package/src/vite/ssr-templates/lambda.ts +4 -0
  113. package/src/vite/ssr-templates/node.ts +7 -22
  114. package/src/vite/ssr-templates/vercel.ts +6 -20
package/dist/cli/cli.js CHANGED
@@ -131,7 +131,7 @@ import { stat } from "node:fs/promises";
131
131
  var fileExists;
132
132
  var init_file_exists = __esm({
133
133
  "src/config/file-exists.ts"() {
134
- fileExists = (path28) => stat(path28).then(() => true).catch(() => false);
134
+ fileExists = (path30) => stat(path30).then(() => true).catch(() => false);
135
135
  }
136
136
  });
137
137
 
@@ -815,6 +815,7 @@ var init_icon_types = __esm({
815
815
  "arrows-up-from-line",
816
816
  "asterisk",
817
817
  "asterisk-square",
818
+ "astroid",
818
819
  "at-sign",
819
820
  "atom",
820
821
  "audio-lines",
@@ -882,6 +883,7 @@ var init_icon_types = __esm({
882
883
  "beer",
883
884
  "beer-off",
884
885
  "bell",
886
+ "bell-check",
885
887
  "bell-dot",
886
888
  "bell-electric",
887
889
  "bell-minus",
@@ -903,6 +905,7 @@ var init_icon_types = __esm({
903
905
  "birdhouse",
904
906
  "bitcoin",
905
907
  "blend",
908
+ "blender",
906
909
  "blinds",
907
910
  "blocks",
908
911
  "bluetooth",
@@ -968,6 +971,7 @@ var init_icon_types = __esm({
968
971
  "briefcase-conveyor-belt",
969
972
  "briefcase-medical",
970
973
  "bring-to-front",
974
+ "broccoli",
971
975
  "brush",
972
976
  "brush-cleaning",
973
977
  "bubbles",
@@ -1460,6 +1464,7 @@ var init_icon_types = __esm({
1460
1464
  "fold-vertical",
1461
1465
  "folder",
1462
1466
  "folder-archive",
1467
+ "folder-bookmark",
1463
1468
  "folder-check",
1464
1469
  "folder-clock",
1465
1470
  "folder-closed",
@@ -1609,6 +1614,7 @@ var init_icon_types = __esm({
1609
1614
  "heart-off",
1610
1615
  "heart-plus",
1611
1616
  "heart-pulse",
1617
+ "heart-x",
1612
1618
  "heater",
1613
1619
  "helicopter",
1614
1620
  "help-circle",
@@ -1686,6 +1692,7 @@ var init_icon_types = __esm({
1686
1692
  "layers",
1687
1693
  "layers-2",
1688
1694
  "layers-3",
1695
+ "layers-minus",
1689
1696
  "layers-plus",
1690
1697
  "layout",
1691
1698
  "layout-dashboard",
@@ -2094,6 +2101,7 @@ var init_icon_types = __esm({
2094
2101
  "repeat",
2095
2102
  "repeat-1",
2096
2103
  "repeat-2",
2104
+ "repeat-off",
2097
2105
  "replace",
2098
2106
  "replace-all",
2099
2107
  "reply",
@@ -2353,6 +2361,12 @@ var init_icon_types = __esm({
2353
2361
  "stethoscope",
2354
2362
  "sticker",
2355
2363
  "sticky-note",
2364
+ "sticky-note-check",
2365
+ "sticky-note-minus",
2366
+ "sticky-note-off",
2367
+ "sticky-note-plus",
2368
+ "sticky-note-x",
2369
+ "sticky-notes",
2356
2370
  "stone",
2357
2371
  "stop-circle",
2358
2372
  "store",
@@ -2433,6 +2447,7 @@ var init_icon_types = __esm({
2433
2447
  "ticket-x",
2434
2448
  "tickets",
2435
2449
  "tickets-plane",
2450
+ "timeline",
2436
2451
  "timer",
2437
2452
  "timer-off",
2438
2453
  "timer-reset",
@@ -2572,7 +2587,9 @@ var init_icon_types = __esm({
2572
2587
  "waves",
2573
2588
  "waves-arrow-down",
2574
2589
  "waves-arrow-up",
2590
+ "waves-horizontal",
2575
2591
  "waves-ladder",
2592
+ "waves-vertical",
2576
2593
  "waypoints",
2577
2594
  "webcam",
2578
2595
  "webhook",
@@ -2836,6 +2853,16 @@ var init_joinUrl = __esm({
2836
2853
  }
2837
2854
  });
2838
2855
 
2856
+ // src/lib/util/url.ts
2857
+ import { matchPath } from "react-router";
2858
+ var matchesProtectedPattern, matchesAnyProtectedPattern;
2859
+ var init_url = __esm({
2860
+ "src/lib/util/url.ts"() {
2861
+ matchesProtectedPattern = (pattern, path30) => matchPath({ path: pattern, end: true }, path30) != null;
2862
+ matchesAnyProtectedPattern = (patterns, path30) => patterns.some((p) => matchesProtectedPattern(p, path30));
2863
+ }
2864
+ });
2865
+
2839
2866
  // src/lib/core/ZudokuContext.ts
2840
2867
  import { createNanoEvents } from "nanoevents";
2841
2868
  var normalizeProtectedRoutes;
@@ -2963,7 +2990,10 @@ var init_ZudokuConfig = __esm({
2963
2990
  showVersionSelect: z7.enum(["always", "if-available", "hide"]),
2964
2991
  expandAllTags: z7.boolean(),
2965
2992
  showInfoPage: z7.boolean(),
2966
- schemaDownload: z7.object({ enabled: z7.boolean() }).partial(),
2993
+ schemaDownload: z7.object({
2994
+ enabled: z7.boolean(),
2995
+ fileName: z7.string().regex(/^[A-Za-z0-9_-]+$/).optional()
2996
+ }).partial(),
2967
2997
  transformExamples: z7.custom(
2968
2998
  (val) => typeof val === "function"
2969
2999
  ),
@@ -3112,7 +3142,7 @@ var init_ZudokuConfig = __esm({
3112
3142
  }).partial();
3113
3143
  DocsConfigSchema = z7.object({
3114
3144
  files: z7.union([z7.string(), z7.array(z7.string())]).transform((val) => typeof val === "string" ? [val] : val).default([DEFAULT_DOCS_FILES]),
3115
- publishMarkdown: z7.boolean().default(false).describe(
3145
+ publishMarkdown: z7.boolean().default(true).describe(
3116
3146
  "When enabled, generates .md files for each document during build. Access documents at their URL path with .md extension (e.g., /foo/hello.md). Markdown files are generated without frontmatter."
3117
3147
  ),
3118
3148
  defaultOptions: z7.object({
@@ -3214,6 +3244,7 @@ var init_ZudokuConfig = __esm({
3214
3244
  issuer: z7.string(),
3215
3245
  audience: z7.string().optional(),
3216
3246
  scopes: z7.array(z7.string()).optional(),
3247
+ allowInsecureRequests: z7.boolean().optional(),
3217
3248
  redirectToAfterSignUp: z7.string().optional(),
3218
3249
  redirectToAfterSignIn: z7.string().optional(),
3219
3250
  redirectToAfterSignOut: z7.string().optional(),
@@ -3350,6 +3381,9 @@ var init_ZudokuConfig = __esm({
3350
3381
  PlacementPosition = z7.enum(["start", "center", "end"]);
3351
3382
  HeaderConfigSchema = z7.object({
3352
3383
  navigation: HeaderNavigationSchema,
3384
+ themeSwitcher: z7.object({
3385
+ enabled: z7.boolean()
3386
+ }).partial().optional(),
3353
3387
  placements: z7.object({
3354
3388
  navigation: PlacementPosition,
3355
3389
  search: PlacementPosition,
@@ -3589,8 +3623,8 @@ var llms_exports = {};
3589
3623
  __export(llms_exports, {
3590
3624
  generateLlmsTxtFiles: () => generateLlmsTxtFiles
3591
3625
  });
3592
- import { writeFile as writeFile4 } from "node:fs/promises";
3593
- import path19 from "node:path";
3626
+ import { writeFile as writeFile5 } from "node:fs/promises";
3627
+ import path20 from "node:path";
3594
3628
  import colors6 from "picocolors";
3595
3629
  async function generateLlmsTxtFiles({
3596
3630
  markdownFileInfos,
@@ -3625,7 +3659,7 @@ async function generateLlmsTxtFiles({
3625
3659
  }
3626
3660
  }
3627
3661
  const llmsTxt2 = llmsTxtParts.join("\n");
3628
- await writeFile4(path19.join(baseOutputDir, "llms.txt"), llmsTxt2, "utf-8");
3662
+ await writeFile5(path20.join(baseOutputDir, "llms.txt"), llmsTxt2, "utf-8");
3629
3663
  console.log(colors6.blue("\u2713 generated llms.txt"));
3630
3664
  }
3631
3665
  if (llmsTxtFull) {
@@ -3650,8 +3684,8 @@ ${info.content}
3650
3684
  `);
3651
3685
  }
3652
3686
  const llmsFull = llmsFullParts.join("\n");
3653
- await writeFile4(
3654
- path19.join(baseOutputDir, "llms-full.txt"),
3687
+ await writeFile5(
3688
+ path20.join(baseOutputDir, "llms-full.txt"),
3655
3689
  llmsFull,
3656
3690
  "utf-8"
3657
3691
  );
@@ -3670,11 +3704,12 @@ import { hideBin } from "yargs/helpers";
3670
3704
  import yargs from "yargs/yargs";
3671
3705
 
3672
3706
  // src/cli/build/handler.ts
3673
- import path23 from "node:path";
3707
+ import path25 from "node:path";
3674
3708
 
3675
3709
  // src/vite/build.ts
3676
- import { mkdir as mkdir5, readFile as readFile3, rename, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
3677
- import path21 from "node:path";
3710
+ import { existsSync as existsSync2 } from "node:fs";
3711
+ import { mkdir as mkdir6, readFile as readFile4, rename as rename2, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
3712
+ import path23 from "node:path";
3678
3713
  import { build as esbuild } from "esbuild";
3679
3714
  import { createBuilder } from "vite";
3680
3715
 
@@ -3818,7 +3853,7 @@ import {
3818
3853
  // package.json
3819
3854
  var package_default = {
3820
3855
  name: "zudoku",
3821
- version: "0.76.0",
3856
+ version: "0.78.0",
3822
3857
  type: "module",
3823
3858
  sideEffects: [
3824
3859
  "**/*.css",
@@ -3877,6 +3912,11 @@ var package_default = {
3877
3912
  "./react-query": "./src/lib/core/react-query.ts",
3878
3913
  "./icons": "./src/lib/icons.ts",
3879
3914
  "./vite": "./src/vite/index.ts",
3915
+ "./server": "./src/app/entry.server.tsx",
3916
+ "./server/adapters/lambda": "./src/app/adapters/lambda.ts",
3917
+ "./server/adapters/node": "./src/app/adapters/node.ts",
3918
+ "./server/adapters/vercel": "./src/app/adapters/vercel.ts",
3919
+ "./server/adapters/cloudflare": "./src/app/adapters/cloudflare.ts",
3880
3920
  "./app/*": "./src/app/*",
3881
3921
  "./hooks": "./src/lib/hooks/index.ts",
3882
3922
  "./processors/*": "./src/lib/plugins/openapi/processors/*.ts",
@@ -3896,10 +3936,10 @@ var package_default = {
3896
3936
  },
3897
3937
  dependencies: {
3898
3938
  "@apidevtools/json-schema-ref-parser": "15.3.5",
3899
- "@base-ui/react": "^1.4.0",
3939
+ "@base-ui/react": "^1.4.1",
3900
3940
  "@envelop/core": "5.5.1",
3901
3941
  "@graphql-typed-document-node/core": "3.2.0",
3902
- "@hono/node-server": "1.19.13",
3942
+ "@hono/node-server": "2.0.2",
3903
3943
  "@lekoarts/rehype-meta-as-attributes": "3.0.3",
3904
3944
  "@mdx-js/react": "3.1.1",
3905
3945
  "@mdx-js/rollup": "3.1.1",
@@ -3929,7 +3969,8 @@ var package_default = {
3929
3969
  "@radix-ui/react-tooltip": "1.2.8",
3930
3970
  "@radix-ui/react-visually-hidden": "1.2.4",
3931
3971
  "@scalar/openapi-parser": "0.23.13",
3932
- "@sentry/node": "10.49.0",
3972
+ "@scalar/snippetz": "0.9.6",
3973
+ "@sentry/node": "10.52.0",
3933
3974
  "@shikijs/langs": "4.0.2",
3934
3975
  "@shikijs/rehype": "4.0.2",
3935
3976
  "@shikijs/themes": "4.0.2",
@@ -3942,7 +3983,6 @@ var package_default = {
3942
3983
  "@types/react-dom": "catalog:",
3943
3984
  "@vitejs/plugin-react": "6.0.1",
3944
3985
  "@x0k/json-schema-merge": "1.0.2",
3945
- "@zudoku/httpsnippet": "10.0.9",
3946
3986
  "@zudoku/react-helmet-async": "2.0.5",
3947
3987
  "@zuplo/mcp": "0.0.32",
3948
3988
  bs58: "6.0.0",
@@ -3957,24 +3997,25 @@ var package_default = {
3957
3997
  "fast-equals": "6.0.0",
3958
3998
  glob: "13.0.6",
3959
3999
  "glob-parent": "6.0.2",
3960
- graphql: "16.13.2",
4000
+ graphql: "16.14.0",
3961
4001
  "graphql-type-json": "0.3.2",
3962
4002
  "graphql-yoga": "5.18.0",
3963
4003
  "gray-matter": "4.0.3",
3964
4004
  "hast-util-heading-rank": "3.0.0",
3965
4005
  "hast-util-to-jsx-runtime": "2.3.6",
3966
4006
  "hast-util-to-string": "3.0.1",
3967
- hono: "4.12.14",
4007
+ hono: "4.12.18",
3968
4008
  "http-terminator": "3.2.0",
3969
4009
  "javascript-stringify": "2.1.0",
4010
+ jose: "6.2.2",
3970
4011
  "json-schema-to-typescript-lite": "15.0.0",
3971
4012
  loglevel: "1.9.2",
3972
- "lucide-react": "1.8.0",
4013
+ "lucide-react": "1.16.0",
3973
4014
  "mdast-util-from-markdown": "2.0.2",
3974
4015
  "mdast-util-mdx": "3.0.0",
3975
4016
  "mdast-util-mdx-jsx": "3.2.0",
3976
4017
  "micromark-extension-mdxjs": "3.0.0",
3977
- motion: "12.35.1",
4018
+ motion: "12.38.0",
3978
4019
  nanoevents: "9.1.0",
3979
4020
  "next-themes": "0.4.6",
3980
4021
  oauth4webapi: "3.8.5",
@@ -3982,13 +4023,14 @@ var package_default = {
3982
4023
  pagefind: "1.5.2",
3983
4024
  picocolors: "1.1.1",
3984
4025
  piscina: "5.1.4",
3985
- "posthog-node": "5.26.0",
4026
+ "posthog-node": "5.33.4",
4027
+ "quick-lru": "7.3.0",
3986
4028
  "react-error-boundary": "6.1.1",
3987
- "react-hook-form": "7.71.2",
4029
+ "react-hook-form": "7.75.0",
3988
4030
  "react-is": "catalog:",
3989
4031
  "react-markdown": "10.1.0",
3990
4032
  "react-router": "7.14.1",
3991
- "rehype-mdx-import-media": "1.2.0",
4033
+ "rehype-mdx-import-media": "1.4.0",
3992
4034
  "rehype-raw": "7.0.0",
3993
4035
  "rehype-slug": "6.0.0",
3994
4036
  "remark-comment": "1.0.0",
@@ -3997,33 +4039,33 @@ var package_default = {
3997
4039
  "remark-frontmatter": "5.0.0",
3998
4040
  "remark-gfm": "4.0.1",
3999
4041
  "remark-mdx-frontmatter": "5.2.0",
4000
- semver: "7.7.4",
4042
+ semver: "7.8.0",
4001
4043
  shiki: "4.0.2",
4002
4044
  sitemap: "9.0.1",
4003
4045
  "strip-ansi": "7.2.0",
4004
- "tailwind-merge": "3.5.0",
4005
- tailwindcss: "4.2.1",
4046
+ "tailwind-merge": "3.6.0",
4047
+ tailwindcss: "4.3.0",
4006
4048
  "tw-animate-css": "1.4.0",
4007
4049
  unified: "11.0.5",
4008
4050
  "unist-util-visit": "5.1.0",
4009
4051
  vaul: "1.1.2",
4010
4052
  vfile: "6.0.3",
4011
- vite: "8.0.9",
4012
- yaml: "2.8.3",
4053
+ vite: "8.0.13",
4054
+ yaml: "2.8.4",
4013
4055
  yargs: "18.0.0",
4014
4056
  zod: "4.3.6",
4015
- zustand: "5.0.12"
4057
+ zustand: "5.0.13"
4016
4058
  },
4017
4059
  devDependencies: {
4018
- "@clerk/clerk-js": "^6.7.4",
4019
- "@graphql-codegen/cli": "6.3.0",
4060
+ "@graphql-codegen/cli": "7.0.0",
4020
4061
  "@inkeep/cxkit-types": "0.5.117",
4021
4062
  "@testing-library/dom": "catalog:",
4022
4063
  "@testing-library/jest-dom": "catalog:",
4023
4064
  "@testing-library/react": "catalog:",
4024
4065
  "@testing-library/user-event": "catalog:",
4025
- "@types/estree": "1.0.8",
4066
+ "@types/estree": "1.0.9",
4026
4067
  "@types/glob-parent": "5.1.3",
4068
+ "@types/har-format": "^1.2.16",
4027
4069
  "@types/hast": "3.0.4",
4028
4070
  "@types/json-schema": "7.0.15",
4029
4071
  "@types/mdast": "4.0.4",
@@ -4074,6 +4116,46 @@ init_logger();
4074
4116
  init_package_json();
4075
4117
  init_loader();
4076
4118
  init_ZudokuConfig();
4119
+
4120
+ // src/lib/manifest.ts
4121
+ init_ProtectedRoutesSchema();
4122
+
4123
+ // src/lib/authentication/cookies.ts
4124
+ var ACCESS_TOKEN_COOKIE = "zudoku-access-token";
4125
+ var REFRESH_TOKEN_COOKIE = "zudoku-refresh-token";
4126
+ var AUTH_PROFILE_COOKIE = "zudoku-auth-profile";
4127
+
4128
+ // src/lib/manifest.ts
4129
+ init_joinUrl();
4130
+ var PROTECTED_CHUNK_DIR = "_protected";
4131
+ var MANIFEST_VERSION = 1;
4132
+ var MANIFEST_FILENAME = "zudoku-manifest.json";
4133
+ var buildManifest = (config2) => {
4134
+ const protectedRoutes = ProtectedRoutesSchema.parse(config2.protectedRoutes);
4135
+ const routePatterns = protectedRoutes ? Object.keys(protectedRoutes) : [];
4136
+ return {
4137
+ version: MANIFEST_VERSION,
4138
+ basePath: config2.basePath ?? "/",
4139
+ ssrEntry: "server/entry.js",
4140
+ static: {
4141
+ prefixes: [joinUrl(config2.basePath, "assets")]
4142
+ },
4143
+ protected: {
4144
+ chunkPrefix: joinUrl(config2.basePath, PROTECTED_CHUNK_DIR),
4145
+ routePatterns
4146
+ },
4147
+ auth: {
4148
+ sessionEndpoint: joinUrl(config2.basePath, "/__z/auth/session"),
4149
+ cookies: {
4150
+ access: ACCESS_TOKEN_COOKIE,
4151
+ refresh: REFRESH_TOKEN_COOKIE,
4152
+ profile: AUTH_PROFILE_COOKIE
4153
+ }
4154
+ }
4155
+ };
4156
+ };
4157
+
4158
+ // src/vite/config.ts
4077
4159
  init_joinUrl();
4078
4160
 
4079
4161
  // src/vite/package-root.ts
@@ -4362,7 +4444,20 @@ import {
4362
4444
  var { compareSchemaDefinitions, compareSchemaValues } = createComparator();
4363
4445
  var { mergeArrayOfSchemaDefinitions } = createMerger({
4364
4446
  intersectJson: createIntersector(compareSchemaValues),
4365
- deduplicateJsonSchemaDef: createDeduplicator(compareSchemaDefinitions)
4447
+ deduplicateJsonSchemaDef: createDeduplicator(compareSchemaDefinitions),
4448
+ // Default mergers throw on incompatible type/enum pairs, which aborts the
4449
+ // whole flatten even when the bad pair is one branch of a pairwise `anyOf`
4450
+ // combination. Union instead.
4451
+ mergers: {
4452
+ type: (a, b) => {
4453
+ if (a === b) return a;
4454
+ const aArr = Array.isArray(a) ? a : [a];
4455
+ const bArr = Array.isArray(b) ? b : [b];
4456
+ const set = /* @__PURE__ */ new Set([...aArr, ...bArr]);
4457
+ return set.size === 1 ? [...set][0] : [...set];
4458
+ },
4459
+ enum: (a, b) => Array.from(/* @__PURE__ */ new Set([...a, ...b]))
4460
+ }
4366
4461
  });
4367
4462
  var shallowAllOfMerge = createShallowAllOfMerge(
4368
4463
  mergeArrayOfSchemaDefinitions
@@ -4406,14 +4501,14 @@ var flattenAllOf = (schema2) => {
4406
4501
  };
4407
4502
 
4408
4503
  // src/lib/util/traverse.ts
4409
- var traverse = (specification, transform, path28 = []) => {
4410
- const transformed = transform(specification, path28);
4504
+ var traverse = (specification, transform, path30 = []) => {
4505
+ const transformed = transform(specification, path30);
4411
4506
  if (typeof transformed !== "object" || transformed === null) {
4412
4507
  return transformed;
4413
4508
  }
4414
4509
  const result = Array.isArray(transformed) ? [] : {};
4415
4510
  for (const [key, value] of Object.entries(transformed)) {
4416
- const currentPath = [...path28, key];
4511
+ const currentPath = [...path30, key];
4417
4512
  if (Array.isArray(value)) {
4418
4513
  result[key] = value.map(
4419
4514
  (item, index) => typeof item === "object" && item != null ? traverse(item, transform, [...currentPath, index.toString()]) : item
@@ -4432,7 +4527,7 @@ var CIRCULAR_REF = "$[Circular Reference]";
4432
4527
  var SCHEMA_REF_PREFIX = "$ref:";
4433
4528
 
4434
4529
  // src/lib/oas/parser/dereference/resolveRef.ts
4435
- var cache = /* @__PURE__ */ new Map();
4530
+ var cache = /* @__PURE__ */ new WeakMap();
4436
4531
  var resolveLocalRef = (schema2, ref) => {
4437
4532
  if (!cache.has(schema2)) {
4438
4533
  cache.set(schema2, /* @__PURE__ */ new Map());
@@ -4441,9 +4536,9 @@ var resolveLocalRef = (schema2, ref) => {
4441
4536
  if (schemaCache?.has(ref)) {
4442
4537
  return schemaCache.get(ref);
4443
4538
  }
4444
- const path28 = ref.split("/").slice(1);
4539
+ const path30 = ref.split("/").slice(1);
4445
4540
  let current = schema2;
4446
- for (const segment of path28) {
4541
+ for (const segment of path30) {
4447
4542
  if (!current || typeof current !== "object") {
4448
4543
  current = null;
4449
4544
  }
@@ -4454,7 +4549,7 @@ var resolveLocalRef = (schema2, ref) => {
4454
4549
  };
4455
4550
 
4456
4551
  // src/lib/oas/parser/dereference/index.ts
4457
- var cache2 = /* @__PURE__ */ new Map();
4552
+ var cache2 = /* @__PURE__ */ new WeakMap();
4458
4553
  var isIndexableObject = (obj) => obj !== null && typeof obj === "object";
4459
4554
  var dereference = async (schema2, resolvers = []) => {
4460
4555
  if (cache2.has(schema2)) {
@@ -4462,7 +4557,7 @@ var dereference = async (schema2, resolvers = []) => {
4462
4557
  }
4463
4558
  const cloned = structuredClone(schema2);
4464
4559
  const visited = /* @__PURE__ */ new Set();
4465
- const resolve = async (current, path28) => {
4560
+ const resolve = async (current, path30) => {
4466
4561
  if (isIndexableObject(current)) {
4467
4562
  if (visited.has(current)) {
4468
4563
  return CIRCULAR_REF;
@@ -4470,7 +4565,7 @@ var dereference = async (schema2, resolvers = []) => {
4470
4565
  visited.add(current);
4471
4566
  if (Array.isArray(current)) {
4472
4567
  for (let index = 0; index < current.length; index++) {
4473
- current[index] = await resolve(current[index], `${path28}/${index}`);
4568
+ current[index] = await resolve(current[index], `${path30}/${index}`);
4474
4569
  }
4475
4570
  } else {
4476
4571
  if ("$ref" in current && typeof current.$ref === "string") {
@@ -4481,13 +4576,13 @@ var dereference = async (schema2, resolvers = []) => {
4481
4576
  for (const resolver of resolvers) {
4482
4577
  const resolved = await resolver($ref);
4483
4578
  if (resolved) {
4484
- result2 = await resolve(resolved, path28);
4579
+ result2 = await resolve(resolved, path30);
4485
4580
  break;
4486
4581
  }
4487
4582
  }
4488
4583
  if (result2 === void 0) {
4489
4584
  const resolved = await resolveLocalRef(cloned, $ref);
4490
- result2 = await resolve(resolved, path28);
4585
+ result2 = await resolve(resolved, path30);
4491
4586
  }
4492
4587
  if (hasSiblings) {
4493
4588
  if (result2 === CIRCULAR_REF) {
@@ -4500,7 +4595,7 @@ var dereference = async (schema2, resolvers = []) => {
4500
4595
  return result2;
4501
4596
  }
4502
4597
  for (const key in current) {
4503
- current[key] = await resolve(current[key], `${path28}/${key}`);
4598
+ current[key] = await resolve(current[key], `${path30}/${key}`);
4504
4599
  }
4505
4600
  }
4506
4601
  visited.delete(current);
@@ -4539,9 +4634,9 @@ var upgradeSchema = (schema2) => {
4539
4634
  }
4540
4635
  return sub;
4541
4636
  });
4542
- schema2 = traverse(schema2, (sub, path28) => {
4637
+ schema2 = traverse(schema2, (sub, path30) => {
4543
4638
  if (sub.example !== void 0) {
4544
- if (isSchemaPath(path28 ?? [])) {
4639
+ if (isSchemaPath(path30 ?? [])) {
4545
4640
  sub.examples = [sub.example];
4546
4641
  } else {
4547
4642
  sub.examples = {
@@ -4554,11 +4649,11 @@ var upgradeSchema = (schema2) => {
4554
4649
  }
4555
4650
  return sub;
4556
4651
  });
4557
- schema2 = traverse(schema2, (schema3, path28) => {
4652
+ schema2 = traverse(schema2, (schema3, path30) => {
4558
4653
  if (schema3.type === "object" && schema3.properties !== void 0) {
4559
- const parentPath = path28?.slice(0, -1);
4654
+ const parentPath = path30?.slice(0, -1);
4560
4655
  const isMultipart = parentPath?.some((segment, index) => {
4561
- return segment === "content" && path28?.[index + 1] === "multipart/form-data";
4656
+ return segment === "content" && path30?.[index + 1] === "multipart/form-data";
4562
4657
  });
4563
4658
  if (isMultipart) {
4564
4659
  const entries = Object.entries(schema3.properties);
@@ -4572,8 +4667,8 @@ var upgradeSchema = (schema2) => {
4572
4667
  }
4573
4668
  return schema3;
4574
4669
  });
4575
- schema2 = traverse(schema2, (schema3, path28) => {
4576
- if (path28?.includes("content") && path28.includes("application/octet-stream")) {
4670
+ schema2 = traverse(schema2, (schema3, path30) => {
4671
+ if (path30?.includes("content") && path30.includes("application/octet-stream")) {
4577
4672
  return {};
4578
4673
  }
4579
4674
  if (schema3.type === "string" && schema3.format === "binary") {
@@ -4599,11 +4694,11 @@ var upgradeSchema = (schema2) => {
4599
4694
  }
4600
4695
  return sub;
4601
4696
  });
4602
- schema2 = traverse(schema2, (schema3, path28) => {
4697
+ schema2 = traverse(schema2, (schema3, path30) => {
4603
4698
  if (schema3.type === "string" && schema3.format === "byte") {
4604
- const parentPath = path28?.slice(0, -1);
4699
+ const parentPath = path30?.slice(0, -1);
4605
4700
  const contentMediaType = parentPath?.find(
4606
- (_, index) => path28?.[index - 1] === "content"
4701
+ (_, index) => path30?.[index - 1] === "content"
4607
4702
  );
4608
4703
  return {
4609
4704
  type: "string",
@@ -4615,7 +4710,7 @@ var upgradeSchema = (schema2) => {
4615
4710
  });
4616
4711
  return schema2;
4617
4712
  };
4618
- function isSchemaPath(path28) {
4713
+ function isSchemaPath(path30) {
4619
4714
  const schemaLocations = [
4620
4715
  ["components", "schemas"],
4621
4716
  "properties",
@@ -4628,10 +4723,10 @@ function isSchemaPath(path28) {
4628
4723
  ];
4629
4724
  return schemaLocations.some((location) => {
4630
4725
  if (Array.isArray(location)) {
4631
- return location.every((segment, index) => path28[index] === segment);
4726
+ return location.every((segment, index) => path30[index] === segment);
4632
4727
  }
4633
- return path28.includes(location);
4634
- }) || path28.includes("schema") || path28.some((segment) => segment.endsWith("Schema"));
4728
+ return path30.includes(location);
4729
+ }) || path30.includes("schema") || path30.some((segment) => segment.endsWith("Schema"));
4635
4730
  }
4636
4731
 
4637
4732
  // src/lib/oas/parser/index.ts
@@ -4651,7 +4746,7 @@ var parseSchemaInput = async (schemaInput) => {
4651
4746
  let response;
4652
4747
  try {
4653
4748
  response = await fetch(schemaInput, {
4654
- cache: "force-cache"
4749
+ cache: typeof window !== "undefined" ? "force-cache" : void 0
4655
4750
  });
4656
4751
  } catch (err) {
4657
4752
  throw new GraphQLError("Failed to fetch schema", {
@@ -4713,13 +4808,13 @@ var OPENAPI_PROPS = /* @__PURE__ */ new Set([
4713
4808
  "anyOf",
4714
4809
  "oneOf"
4715
4810
  ]);
4716
- var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path28 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
4811
+ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path30 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
4717
4812
  if (obj === null || typeof obj !== "object") return obj;
4718
4813
  const refPath = obj.__$ref;
4719
4814
  const isCircular = currentPath.has(obj) || typeof refPath === "string" && currentRefPaths.has(refPath);
4720
4815
  if (isCircular) {
4721
4816
  if (typeof refPath === "string") return SCHEMA_REF_PREFIX + refPath;
4722
- const circularProp = path28.find((p) => !OPENAPI_PROPS.has(p)) || path28[0];
4817
+ const circularProp = path30.find((p) => !OPENAPI_PROPS.has(p)) || path30[0];
4723
4818
  return [CIRCULAR_REF, circularProp].filter(Boolean).join(":");
4724
4819
  }
4725
4820
  if (refs.has(obj)) return refs.get(obj);
@@ -4729,7 +4824,7 @@ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs
4729
4824
  value,
4730
4825
  currentPath,
4731
4826
  refs,
4732
- [...path28, key],
4827
+ [...path30, key],
4733
4828
  currentRefPaths
4734
4829
  );
4735
4830
  const result = Array.isArray(obj) ? obj.map((item, i) => recurse(item, i.toString())) : Object.fromEntries(
@@ -4767,7 +4862,7 @@ var resolveExtensions = (obj) => Object.fromEntries(
4767
4862
  var getAllTags = (schema2) => {
4768
4863
  const rootTags = schema2.tags ?? [];
4769
4864
  const operations = Object.values(schema2.paths ?? {}).flatMap(
4770
- (path28) => HttpMethods.map((k) => path28?.[k]).filter((op) => op != null)
4865
+ (path30) => HttpMethods.map((k) => path30?.[k]).filter((op) => op != null)
4771
4866
  );
4772
4867
  const operationTags = new Set(operations.flatMap((op) => op.tags ?? []));
4773
4868
  const hasUntaggedOperations = operations.some(
@@ -4822,7 +4917,7 @@ var getAllSlugs = (ops, schemaTags = []) => {
4822
4917
  var getOperationSlugKey = (op) => [op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
4823
4918
  var getAllOperations = (paths) => {
4824
4919
  const operations = Object.entries(paths ?? {}).flatMap(
4825
- ([path28, value]) => HttpMethods.flatMap((method) => {
4920
+ ([path30, value]) => HttpMethods.flatMap((method) => {
4826
4921
  if (!value?.[method]) return [];
4827
4922
  const operation = value[method];
4828
4923
  const pathParameters = value.parameters ?? [];
@@ -4842,7 +4937,7 @@ var getAllOperations = (paths) => {
4842
4937
  return {
4843
4938
  ...operation,
4844
4939
  method,
4845
- path: path28,
4940
+ path: path30,
4846
4941
  parameters,
4847
4942
  servers,
4848
4943
  tags: operation.tags ?? []
@@ -5044,10 +5139,10 @@ var resolveSecuritySchemes = (schema2) => {
5044
5139
  var resolveSecurityRequirements = (securityArray, securitySchemes) => {
5045
5140
  if (!securityArray) return [];
5046
5141
  return securityArray.map((req) => ({
5047
- schemes: Object.entries(req).filter(([key]) => !key.startsWith("__")).flatMap(([schemeName, scopes]) => {
5142
+ schemes: Object.entries(req).filter(([key]) => !key.startsWith("__")).flatMap(([schemeName, scopes2]) => {
5048
5143
  const scheme = securitySchemes.find((s) => s.name === schemeName);
5049
5144
  if (!scheme) return [];
5050
- return [{ scheme, scopes }];
5145
+ return [{ scheme, scopes: scopes2 }];
5051
5146
  })
5052
5147
  }));
5053
5148
  };
@@ -5340,8 +5435,8 @@ var Schema = builder.objectRef("Schema").implement({
5340
5435
  }),
5341
5436
  paths: t.field({
5342
5437
  type: [PathItem],
5343
- resolve: (root) => Object.entries(root.paths ?? {}).map(([path28, value]) => ({
5344
- path: path28,
5438
+ resolve: (root) => Object.entries(root.paths ?? {}).map(([path30, value]) => ({
5439
+ path: path30,
5345
5440
  // biome-ignore lint/style/noNonNullAssertion: value is guaranteed to be defined
5346
5441
  methods: Object.keys(value)
5347
5442
  }))
@@ -5497,7 +5592,7 @@ init_joinUrl();
5497
5592
 
5498
5593
  // src/vite/api/schema-codegen.ts
5499
5594
  var unescapeJsonPointer = (uri) => decodeURIComponent(uri.replace(/~1/g, "/").replace(/~0/g, "~"));
5500
- var getSegmentsFromPath = (path28) => path28.split("/").slice(1).map(unescapeJsonPointer);
5595
+ var getSegmentsFromPath = (path30) => path30.split("/").slice(1).map(unescapeJsonPointer);
5501
5596
  var createLocalRefMap = (obj) => {
5502
5597
  const refMap = /* @__PURE__ */ new Map();
5503
5598
  const siblingsMap = /* @__PURE__ */ new Map();
@@ -5528,16 +5623,16 @@ var replaceMarkers = (code, mergedRefs) => code.replace(/"__refMap:(.*?)"/g, '__
5528
5623
  /"__refMap\+Siblings:(.*?)"/g,
5529
5624
  (_, key) => mergedRefs.get(key) ?? `__refMap["${key}"]`
5530
5625
  );
5531
- var lookup = (schema2, path28, filePath) => {
5532
- const parts = getSegmentsFromPath(path28);
5626
+ var lookup = (schema2, path30, filePath) => {
5627
+ const parts = getSegmentsFromPath(path30);
5533
5628
  let val = schema2;
5534
5629
  for (const part of parts) {
5535
5630
  while (val.$ref?.startsWith("#/")) {
5536
- val = val.$ref === path28 ? val : lookup(schema2, val.$ref, filePath);
5631
+ val = val.$ref === path30 ? val : lookup(schema2, val.$ref, filePath);
5537
5632
  }
5538
5633
  if (val[part] === void 0) {
5539
5634
  throw new Error(
5540
- `Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path28}`
5635
+ `Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path30}`
5541
5636
  );
5542
5637
  }
5543
5638
  val = val[part];
@@ -5705,6 +5800,9 @@ var SchemaManager = class {
5705
5800
  const existingSchema = schemas[index];
5706
5801
  const schemaVersion = processedSchema.info.version ?? FALLBACK_VERSION;
5707
5802
  const versionPath = existingSchema?.path && existingSchema.path.length > 0 ? existingSchema.path : paramsPath(params) || schemaVersion;
5803
+ const config2 = ensureArray(this.config.apis ?? []).find(
5804
+ (c) => c.path === configuredPath
5805
+ );
5708
5806
  const processed = {
5709
5807
  schema: processedSchema,
5710
5808
  version: schemaVersion,
@@ -5717,7 +5815,8 @@ var SchemaManager = class {
5717
5815
  filePath,
5718
5816
  versionPath,
5719
5817
  configuredPath,
5720
- params
5818
+ params,
5819
+ config2?.options
5721
5820
  ),
5722
5821
  processedJsonPath,
5723
5822
  processedTime
@@ -5782,8 +5881,8 @@ var SchemaManager = class {
5782
5881
  }
5783
5882
  }
5784
5883
  };
5785
- getLatestSchema = (path28) => this.processedSchemas[path28]?.at(0);
5786
- getSchemasForPath = (path28) => this.processedSchemas[path28];
5884
+ getLatestSchema = (path30) => this.processedSchemas[path30]?.at(0);
5885
+ getSchemasForPath = (path30) => this.processedSchemas[path30];
5787
5886
  getSchemaImports = () => Object.values(this.processedSchemas).flat().filter((s) => s.importKey);
5788
5887
  getUrlToFilePathMap = () => {
5789
5888
  const map = /* @__PURE__ */ new Map();
@@ -5803,14 +5902,15 @@ var SchemaManager = class {
5803
5902
  }
5804
5903
  return map;
5805
5904
  };
5806
- createSchemaPath = (inputPath, versionPath, apiPath, params) => {
5905
+ createSchemaPath = (inputPath, versionPath, apiPath, params, config2) => {
5807
5906
  const suffix = paramsSuffix(params);
5808
5907
  const extension = suffix ? ".json" : path7.extname(inputPath);
5908
+ const fileName = config2?.schemaDownload?.fileName ?? this.config.defaults?.apis?.schemaDownload?.fileName ?? "schema";
5809
5909
  return joinUrl(
5810
5910
  this.config.basePath,
5811
5911
  apiPath,
5812
5912
  versionPath,
5813
- `schema${suffix}${extension}`
5913
+ `${fileName}${suffix}${extension}`
5814
5914
  );
5815
5915
  };
5816
5916
  validateSchema = async (schema2, filePath) => {
@@ -6148,6 +6248,7 @@ var viteConfigReloadPlugin = () => ({
6148
6248
  });
6149
6249
 
6150
6250
  // src/vite/plugin-api.ts
6251
+ var PROCESSED_STORE_SUBPATH = "node_modules/.zudoku/processed";
6151
6252
  var viteApiPlugin = async () => {
6152
6253
  const virtualModuleId4 = "virtual:zudoku-api-plugins";
6153
6254
  const resolvedVirtualModuleId4 = `\0${virtualModuleId4}`;
@@ -6159,7 +6260,7 @@ var viteApiPlugin = async () => {
6159
6260
  const buildProcessors = buildConfig?.processors ?? [];
6160
6261
  const tmpStoreDir = path11.posix.join(
6161
6262
  initialConfig.__meta.rootDir,
6162
- "node_modules/.zudoku/processed"
6263
+ PROCESSED_STORE_SUBPATH
6163
6264
  );
6164
6265
  const processors = [...buildProcessors, ...zuploProcessors];
6165
6266
  const schemaManager = new SchemaManager({
@@ -6739,7 +6840,7 @@ init_ProtectedRoutesSchema();
6739
6840
  init_joinUrl();
6740
6841
  import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
6741
6842
  import path13 from "node:path";
6742
- import { matchPath } from "react-router";
6843
+ init_url();
6743
6844
  var processMarkdownFile = async (filePath) => {
6744
6845
  const { content: markdownContent, data: frontmatter } = await readFrontmatter(filePath);
6745
6846
  let finalMarkdown = markdownContent;
@@ -6791,13 +6892,9 @@ var viteMarkdownExportPlugin = () => {
6791
6892
  config2.protectedRoutes
6792
6893
  );
6793
6894
  if (protectedRoutes) {
6794
- const isProtectedRoute = (routePath) => {
6795
- return Object.keys(protectedRoutes).some(
6796
- (route) => matchPath({ path: route, end: true }, routePath)
6797
- );
6798
- };
6895
+ const patterns = Object.keys(protectedRoutes);
6799
6896
  for (const routePath of Object.keys(markdownFiles)) {
6800
- if (isProtectedRoute(routePath)) {
6897
+ if (matchesAnyProtectedPattern(patterns, routePath)) {
6801
6898
  delete markdownFiles[routePath];
6802
6899
  }
6803
6900
  }
@@ -7476,6 +7573,155 @@ function vitePlugin() {
7476
7573
  ];
7477
7574
  }
7478
7575
 
7576
+ // src/vite/protected/registry.ts
7577
+ init_ProtectedRoutesSchema();
7578
+ init_joinUrl();
7579
+ init_url();
7580
+ import { matchPath as matchPath2 } from "react-router";
7581
+ var scopes = /* @__PURE__ */ new Map();
7582
+ var clearProtectedRegistry = () => scopes.clear();
7583
+ var registerProtectedScope = (moduleId, scope) => {
7584
+ const list = scopes.get(moduleId);
7585
+ if (list) list.push(scope);
7586
+ else scopes.set(moduleId, [scope]);
7587
+ };
7588
+ var scopeMatchesPattern = (scope, pattern) => {
7589
+ if (scope.type === "route") {
7590
+ return matchesProtectedPattern(pattern, joinUrl(scope.path));
7591
+ }
7592
+ const root = joinUrl(scope.root);
7593
+ if (!pattern.includes("*")) {
7594
+ return matchesProtectedPattern(pattern, root);
7595
+ }
7596
+ return matchPath2({ path: pattern, end: false }, root) != null;
7597
+ };
7598
+ var getProtectedSourceMatcher = (config2) => {
7599
+ const protectedRoutes = ProtectedRoutesSchema.parse(config2.protectedRoutes);
7600
+ const patterns = protectedRoutes ? Object.keys(protectedRoutes) : [];
7601
+ if (patterns.length === 0) {
7602
+ return { match: () => false, enabled: false, patterns };
7603
+ }
7604
+ return {
7605
+ enabled: true,
7606
+ patterns,
7607
+ match: (id) => {
7608
+ const pathOnly = id.split("?")[0] ?? id;
7609
+ const moduleScopes = scopes.get(pathOnly);
7610
+ if (!moduleScopes) return false;
7611
+ return moduleScopes.some(
7612
+ (s) => patterns.some((p) => scopeMatchesPattern(s, p))
7613
+ );
7614
+ }
7615
+ };
7616
+ };
7617
+ var findUnmatchedProtectedPatterns = (patterns) => patterns.filter(
7618
+ (p) => ![...scopes.values()].some(
7619
+ (scopeList) => scopeList.some((s) => scopeMatchesPattern(s, p))
7620
+ )
7621
+ );
7622
+
7623
+ // src/vite/protected/annotator.ts
7624
+ var walk = (node, visit9) => {
7625
+ if (!node || typeof node !== "object") return;
7626
+ if (typeof node.type === "string") visit9(node);
7627
+ for (const key of Object.keys(node)) {
7628
+ if (key === "loc" || key === "start" || key === "end" || key === "range") {
7629
+ continue;
7630
+ }
7631
+ const val = node[key];
7632
+ if (Array.isArray(val)) {
7633
+ for (const v of val) walk(v, visit9);
7634
+ } else if (val && typeof val === "object") {
7635
+ walk(val, visit9);
7636
+ }
7637
+ }
7638
+ };
7639
+ var literalString = (node) => node?.type === "Literal" && typeof node.value === "string" ? node.value : void 0;
7640
+ var propKey = (prop) => prop.key?.type === "Identifier" ? prop.key.name : literalString(prop.key);
7641
+ var collectImportSpecs = (node) => {
7642
+ const out = [];
7643
+ walk(node, (n) => {
7644
+ if (n.type === "ImportExpression") {
7645
+ const spec = literalString(n.source);
7646
+ if (spec) out.push(spec);
7647
+ }
7648
+ });
7649
+ return out;
7650
+ };
7651
+ var matchPathObject = (node) => {
7652
+ if (node.type !== "ObjectExpression") return;
7653
+ let root;
7654
+ const siblingValues = [];
7655
+ for (const prop of node.properties ?? []) {
7656
+ if (prop.type !== "Property") continue;
7657
+ const name = propKey(prop);
7658
+ const strValue = literalString(prop.value);
7659
+ if (name === "path" && strValue) root = strValue;
7660
+ else siblingValues.push(prop.value);
7661
+ }
7662
+ if (!root) return;
7663
+ const specs = siblingValues.flatMap(collectImportSpecs);
7664
+ if (specs.length === 0) return;
7665
+ return { root, specs };
7666
+ };
7667
+ var matchRouteDict = (node) => {
7668
+ if (node.type !== "ObjectExpression") return;
7669
+ const props = node.properties ?? [];
7670
+ if (props.length === 0) return;
7671
+ const pairs = [];
7672
+ for (const prop of props) {
7673
+ if (prop.type !== "Property") return;
7674
+ const key = literalString(prop.key);
7675
+ if (!key?.startsWith("/") || key.includes(".")) return;
7676
+ if (prop.value?.type !== "ArrowFunctionExpression" || prop.value.body?.type !== "ImportExpression") {
7677
+ return;
7678
+ }
7679
+ const spec = literalString(prop.value.body.source);
7680
+ if (!spec) return;
7681
+ pairs.push({ root: key, spec });
7682
+ }
7683
+ return pairs;
7684
+ };
7685
+ var protectedAnnotatorPlugin = () => ({
7686
+ name: "zudoku:protected-annotator",
7687
+ buildStart() {
7688
+ clearProtectedRegistry();
7689
+ },
7690
+ async transform(code, id) {
7691
+ if (id.includes("/node_modules/")) return;
7692
+ if (!code.includes("import(")) return;
7693
+ let ast;
7694
+ try {
7695
+ ast = this.parse(code);
7696
+ } catch (err) {
7697
+ this.warn(
7698
+ `protected-annotator: failed to parse ${id}: ${err instanceof Error ? err.message : String(err)}. Protected gating will NOT apply to this module.`
7699
+ );
7700
+ return;
7701
+ }
7702
+ const tasks = [];
7703
+ walk(ast, (node) => {
7704
+ const a = matchPathObject(node);
7705
+ if (a) for (const spec of a.specs) tasks.push({ spec, root: a.root });
7706
+ const b = matchRouteDict(node);
7707
+ if (b) for (const { spec, root } of b) tasks.push({ spec, root });
7708
+ });
7709
+ for (const { spec, root } of tasks) {
7710
+ const resolved = await this.resolve(spec, id);
7711
+ if (!resolved || resolved.external) {
7712
+ this.warn(
7713
+ `Route-shaped import "${spec}" in ${id} did not resolve to a first-party module; protected gating will not apply.`
7714
+ );
7715
+ continue;
7716
+ }
7717
+ registerProtectedScope(resolved.id.split("?")[0] ?? resolved.id, {
7718
+ type: "subtree",
7719
+ root
7720
+ });
7721
+ }
7722
+ }
7723
+ });
7724
+
7479
7725
  // src/vite/config.ts
7480
7726
  var getAppClientEntryPath = () => path16.posix.join(getZudokuRootDir(), "src/app/entry.client.tsx");
7481
7727
  var getAppServerEntryPath = () => path16.posix.join(getZudokuRootDir(), "src/app/entry.server.tsx");
@@ -7487,11 +7733,15 @@ var defineEnvVars = (vars) => Object.fromEntries(
7487
7733
  [`import.meta.env.${v}`, JSON.stringify(process.env[v])]
7488
7734
  ])
7489
7735
  );
7490
- async function getViteConfig(dir, configEnv) {
7736
+ async function getViteConfig(dir, configEnv, options = {}) {
7491
7737
  const { config: config2, publicEnv: publicEnv2, envPrefix: envPrefix2 } = await loadZudokuConfig(
7492
7738
  configEnv,
7493
7739
  dir
7494
7740
  );
7741
+ const { match: isProtectedSource, enabled: hasProtectedSources } = getProtectedSourceMatcher(config2);
7742
+ const shouldProtectChunks = hasProtectedSources && options.ssr === true;
7743
+ const isProtectedChunk = (chunk) => chunk.facadeModuleId && isProtectedSource(chunk.facadeModuleId) || chunk.moduleIds.some(isProtectedSource);
7744
+ const isWorker = options.adapter === "cloudflare";
7495
7745
  const cdnUrl = CdnUrlSchema.parse(config2.cdnUrl);
7496
7746
  const base = cdnUrl?.base ? joinUrl(cdnUrl.base, config2.basePath) : config2.basePath;
7497
7747
  if (cdnUrl && !hasLoggedCdnInfo) {
@@ -7517,6 +7767,10 @@ async function getViteConfig(dir, configEnv) {
7517
7767
  root: dir,
7518
7768
  base,
7519
7769
  appType: "custom",
7770
+ // Cloudflare Workers: `webworker` makes Vite pick the browser platform
7771
+ // for rolldown and avoids emitting `createRequire(import.meta.url)`,
7772
+ // which is undefined in Workers. See vitejs/vite#21969 (fix in 8.0.4+).
7773
+ ssr: isWorker ? { target: "webworker" } : void 0,
7520
7774
  configFile: false,
7521
7775
  clearScreen: false,
7522
7776
  logLevel: process.env.LOG_LEVEL ?? "info",
@@ -7574,13 +7828,19 @@ async function getViteConfig(dir, configEnv) {
7574
7828
  environments: {
7575
7829
  client: {
7576
7830
  build: {
7831
+ manifest: true,
7577
7832
  rolldownOptions: {
7578
- input: "zudoku/app/entry.client.tsx"
7833
+ input: "zudoku/app/entry.client.tsx",
7834
+ output: shouldProtectChunks ? {
7835
+ entryFileNames: (chunk) => isProtectedChunk(chunk) ? `${PROTECTED_CHUNK_DIR}/[name]-[hash].js` : "assets/[name]-[hash].js",
7836
+ chunkFileNames: (chunk) => isProtectedChunk(chunk) ? `${PROTECTED_CHUNK_DIR}/[name]-[hash].js` : "assets/[name]-[hash].js"
7837
+ } : void 0
7579
7838
  }
7580
7839
  }
7581
7840
  },
7582
7841
  ssr: {
7583
- resolve: {
7842
+ // Build: bundle all for self-contained SSR output; dev uses minimal externals for speed.
7843
+ resolve: configEnv.command === "build" ? { noExternal: true } : {
7584
7844
  noExternal: [/zudoku/, "@mdx-js/react"],
7585
7845
  external: ["@shikijs/themes", "@shikijs/langs"]
7586
7846
  },
@@ -7588,7 +7848,9 @@ async function getViteConfig(dir, configEnv) {
7588
7848
  outDir: path16.resolve(
7589
7849
  path16.join(dir, "dist", config2.basePath ?? "", "server")
7590
7850
  ),
7851
+ copyPublicDir: false,
7591
7852
  rolldownOptions: {
7853
+ logLevel: "warn",
7592
7854
  input: ["zudoku/app/entry.server.tsx", config2.__meta.configPath]
7593
7855
  }
7594
7856
  }
@@ -7596,6 +7858,9 @@ async function getViteConfig(dir, configEnv) {
7596
7858
  },
7597
7859
  experimental: {
7598
7860
  renderBuiltUrl(filename) {
7861
+ if (filename.startsWith(`${PROTECTED_CHUNK_DIR}/`)) {
7862
+ return joinUrl(config2.basePath, `/${filename}`);
7863
+ }
7599
7864
  if (cdnUrl?.base && [".js", ".css"].includes(path16.extname(filename))) {
7600
7865
  return joinUrl(cdnUrl.base, filename);
7601
7866
  }
@@ -7620,7 +7885,7 @@ async function getViteConfig(dir, configEnv) {
7620
7885
  "/__z/entry.client.tsx",
7621
7886
  "**/pagefind.js"
7622
7887
  ],
7623
- plugins: [vitePlugin()],
7888
+ plugins: [protectedAnnotatorPlugin(), vitePlugin()],
7624
7889
  future: {
7625
7890
  removeServerModuleGraph: "warn",
7626
7891
  removeSsrLoadModule: "warn",
@@ -7703,12 +7968,24 @@ ${cssLinks}
7703
7968
  `.trim();
7704
7969
  }
7705
7970
 
7971
+ // src/vite/manifest.ts
7972
+ import { writeFile as writeFile3 } from "node:fs/promises";
7973
+ import path17 from "node:path";
7974
+ var writeManifest = async (distDir, config2) => {
7975
+ await writeFile3(
7976
+ path17.join(distDir, MANIFEST_FILENAME),
7977
+ `${JSON.stringify(buildManifest(config2), null, 2)}
7978
+ `,
7979
+ "utf-8"
7980
+ );
7981
+ };
7982
+
7706
7983
  // src/vite/output.ts
7707
7984
  init_package_json();
7708
7985
  init_joinUrl();
7709
7986
  import assert from "node:assert";
7710
- import { cp, mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
7711
- import path17 from "node:path";
7987
+ import { cp, mkdir as mkdir3, writeFile as writeFile4 } from "node:fs/promises";
7988
+ import path18 from "node:path";
7712
7989
  var pkgJson = getZudokuPackageJson();
7713
7990
  function generateOutput({
7714
7991
  config: config2,
@@ -7765,10 +8042,10 @@ async function writeOutput(dir, {
7765
8042
  rewrites
7766
8043
  }) {
7767
8044
  const output = generateOutput({ config: config2, redirects, rewrites });
7768
- const outputDir = process.env.VERCEL ? path17.join(dir, ".vercel/output") : path17.join(dir, "dist/.output");
8045
+ const outputDir = process.env.VERCEL ? path18.join(dir, ".vercel/output") : path18.join(dir, "dist/.output");
7769
8046
  await mkdir3(outputDir, { recursive: true });
7770
- const outputFile = path17.join(outputDir, "config.json");
7771
- await writeFile3(outputFile, JSON.stringify(output, null, 2), "utf-8");
8047
+ const outputFile = path18.join(outputDir, "config.json");
8048
+ await writeFile4(outputFile, JSON.stringify(output, null, 2), "utf-8");
7772
8049
  if (process.env.VERCEL) {
7773
8050
  console.log("Wrote Vercel output to", outputDir);
7774
8051
  }
@@ -7780,7 +8057,7 @@ init_file_exists();
7780
8057
  import { readFileSync as readFileSync2 } from "node:fs";
7781
8058
  import { readFile as readFile2, rm } from "node:fs/promises";
7782
8059
  import os from "node:os";
7783
- import path20 from "node:path";
8060
+ import path21 from "node:path";
7784
8061
  import { pathToFileURL } from "node:url";
7785
8062
  import { createIndex } from "pagefind";
7786
8063
  import colors7 from "picocolors";
@@ -7822,7 +8099,7 @@ function throttle(fn) {
7822
8099
  init_joinUrl();
7823
8100
  import { createWriteStream, existsSync } from "node:fs";
7824
8101
  import { mkdir as mkdir4 } from "node:fs/promises";
7825
- import path18 from "node:path";
8102
+ import path19 from "node:path";
7826
8103
  import colors5 from "picocolors";
7827
8104
  import { SitemapStream } from "sitemap";
7828
8105
  async function generateSitemap({
@@ -7836,11 +8113,11 @@ async function generateSitemap({
7836
8113
  return;
7837
8114
  }
7838
8115
  const sitemap = new SitemapStream({ hostname: config2.siteUrl });
7839
- const outputDir = path18.resolve(baseOutputDir, config2.outDir ?? "");
8116
+ const outputDir = path19.resolve(baseOutputDir, config2.outDir ?? "");
7840
8117
  if (!existsSync(outputDir)) {
7841
8118
  await mkdir4(outputDir, { recursive: true });
7842
8119
  }
7843
- const sitemapOutputPath = path18.join(outputDir, "sitemap.xml");
8120
+ const sitemapOutputPath = path19.join(outputDir, "sitemap.xml");
7844
8121
  const writeStream = createWriteStream(sitemapOutputPath);
7845
8122
  sitemap.pipe(writeStream);
7846
8123
  let lastmod;
@@ -7870,14 +8147,14 @@ async function generateSitemap({
7870
8147
 
7871
8148
  // src/vite/prerender/utils.ts
7872
8149
  init_joinUrl();
7873
- var resolveRoutePath = (path28) => {
7874
- const segments = path28.split("/");
8150
+ var resolveRoutePath = (path30) => {
8151
+ const segments = path30.split("/");
7875
8152
  if (segments.some((s) => s.startsWith(":") && !s.endsWith("?"))) {
7876
8153
  return void 0;
7877
8154
  }
7878
8155
  return segments.filter((s) => !s.startsWith(":")).join("/") || void 0;
7879
8156
  };
7880
- var isSkipped = (path28) => path28.includes("*") || /^\d+$/.test(path28);
8157
+ var isSkipped = (path30) => path30.includes("*") || /^\d+$/.test(path30);
7881
8158
  var resolveRoutes = (routes, parentPath = "") => routes.flatMap((route) => {
7882
8159
  if (route.path && isSkipped(route.path)) return [];
7883
8160
  const routePath = route.path ? resolveRoutePath(route.path) : void 0;
@@ -7926,12 +8203,12 @@ var prerender = async ({
7926
8203
  serverConfigFilename,
7927
8204
  writeRedirects = true
7928
8205
  }) => {
7929
- const distDir = path20.join(dir, "dist", basePath);
8206
+ const distDir = path21.join(dir, "dist", basePath);
7930
8207
  const serverConfigPath = pathToFileURL(
7931
- path20.join(distDir, "server", serverConfigFilename)
8208
+ path21.join(distDir, "server", serverConfigFilename)
7932
8209
  ).href;
7933
8210
  const entryServerPath = pathToFileURL(
7934
- path20.join(distDir, "server/entry.server.js")
8211
+ path21.join(distDir, "server/entry.server.js")
7935
8212
  ).href;
7936
8213
  const rawConfig = await import(serverConfigPath).then(
7937
8214
  (m) => m.default
@@ -8043,7 +8320,7 @@ var prerender = async ({
8043
8320
  }
8044
8321
  if (isTTY()) writeLine("");
8045
8322
  const { outputPath } = await pagefindIndex.writeFiles({
8046
- outputPath: path20.join(distDir, "pagefind")
8323
+ outputPath: path21.join(distDir, "pagefind")
8047
8324
  });
8048
8325
  if (outputPath) {
8049
8326
  const duration = (performance.now() - pagefindStart) / 1e3;
@@ -8067,7 +8344,7 @@ var prerender = async ({
8067
8344
  const { generateLlmsTxtFiles: generateLlmsTxtFiles2 } = await Promise.resolve().then(() => (init_llms(), llms_exports));
8068
8345
  const docsConfig = DocsConfigSchema2.parse(config2.docs);
8069
8346
  const llmsConfig = docsConfig.llms ?? {};
8070
- const markdownInfoPath = path20.join(
8347
+ const markdownInfoPath = path21.join(
8071
8348
  dir,
8072
8349
  "node_modules/.zudoku/markdown-info.json"
8073
8350
  );
@@ -8092,7 +8369,7 @@ var prerender = async ({
8092
8369
  await Promise.all(
8093
8370
  markdownFileInfos.map((info) => {
8094
8371
  const outputPath = getMarkdownOutputPath(distDir, info.routePath);
8095
- if (!path20.resolve(outputPath).startsWith(path20.resolve(distDir))) {
8372
+ if (!path21.resolve(outputPath).startsWith(path21.resolve(distDir))) {
8096
8373
  return;
8097
8374
  }
8098
8375
  return rm(outputPath).catch(() => {
@@ -8150,19 +8427,123 @@ var getContainerMemoryLimitMb = () => {
8150
8427
  return void 0;
8151
8428
  };
8152
8429
 
8430
+ // src/vite/protected/build.ts
8431
+ import { mkdir as mkdir5, readdir, readFile as readFile3, rename, rm as rm2 } from "node:fs/promises";
8432
+ import path22 from "node:path";
8433
+ init_joinUrl();
8434
+ var assertProtectedPatternsCovered = (config2) => {
8435
+ const { patterns } = getProtectedSourceMatcher(config2);
8436
+ const unmatched = findUnmatchedProtectedPatterns(patterns);
8437
+ if (unmatched.length === 0) return;
8438
+ throw new Error(
8439
+ `[zudoku] protectedRoutes patterns with no matching content: ${unmatched.map((p) => `"${p}"`).join(", ")}.
8440
+ Either the pattern is a typo, or the route uses an inline element / dynamic path that isn't code-split. Load the route via dynamic import so it gets its own chunk, otherwise its JS ships in the public bundle.`
8441
+ );
8442
+ };
8443
+ var findProtectedLeaks = (output) => {
8444
+ const isProtected = (fileName) => fileName.startsWith(`${PROTECTED_CHUNK_DIR}/`);
8445
+ const chunks = output.filter((o) => o.type === "chunk");
8446
+ const byFileName = new Map(chunks.map((c) => [c.fileName, c]));
8447
+ const leaks = [];
8448
+ for (const entry of chunks.filter(
8449
+ (c) => c.isEntry && !isProtected(c.fileName)
8450
+ )) {
8451
+ const visited = /* @__PURE__ */ new Set();
8452
+ const stack = [{ fileName: entry.fileName, path: [entry.fileName] }];
8453
+ while (stack.length > 0) {
8454
+ const { fileName, path: path30 } = stack.pop();
8455
+ if (visited.has(fileName)) continue;
8456
+ visited.add(fileName);
8457
+ for (const imp of byFileName.get(fileName)?.imports ?? []) {
8458
+ const next = [...path30, imp];
8459
+ if (isProtected(imp)) {
8460
+ leaks.push(next.join(" -> "));
8461
+ continue;
8462
+ }
8463
+ stack.push({ fileName: imp, path: next });
8464
+ }
8465
+ }
8466
+ }
8467
+ return leaks;
8468
+ };
8469
+ var assertNoProtectedLeaks = (output) => {
8470
+ const protectedEntries = output.filter((o) => o.type === "chunk").filter(
8471
+ (c) => c.isEntry && c.fileName.startsWith(`${PROTECTED_CHUNK_DIR}/`)
8472
+ ).map((c) => c.fileName);
8473
+ if (protectedEntries.length > 0) {
8474
+ throw new Error(
8475
+ `Protected chunk(s) marked as entries:
8476
+ ${protectedEntries.join("\n ")}
8477
+ Entry chunks are loaded outside the gated import path. Move the entry to a public chunk that dynamically imports the protected one.`
8478
+ );
8479
+ }
8480
+ const leaks = findProtectedLeaks(output);
8481
+ if (leaks.length === 0) return;
8482
+ throw new Error(
8483
+ `Protected chunk(s) statically reachable from public entry:
8484
+ ${leaks.join("\n ")}
8485
+ This eagerly pulls gated content into the public bundle. Check that nothing in non-protected entry code statically imports the protected module.`
8486
+ );
8487
+ };
8488
+ var moveProtectedChunks = async (clientOutDir, serverOutDir) => {
8489
+ const srcDir = path22.join(clientOutDir, PROTECTED_CHUNK_DIR);
8490
+ const files = await readdir(srcDir).catch((err) => {
8491
+ if (err.code === "ENOENT") return null;
8492
+ throw err;
8493
+ });
8494
+ if (!files) return;
8495
+ const destDir = path22.join(serverOutDir, PROTECTED_CHUNK_DIR);
8496
+ await mkdir5(destDir, { recursive: true });
8497
+ await Promise.all(
8498
+ files.map(
8499
+ (file) => rename(path22.join(srcDir, file), path22.join(destDir, file))
8500
+ )
8501
+ );
8502
+ const leftover = await readdir(srcDir).catch(() => []);
8503
+ if (leftover.length > 0) {
8504
+ throw new Error(
8505
+ `moveProtectedChunks left ${leftover.length} file(s) in ${srcDir}: ${leftover.join(", ")}.
8506
+ These would be served publicly. Aborting build.`
8507
+ );
8508
+ }
8509
+ await rm2(srcDir, { recursive: true, force: true });
8510
+ };
8511
+ var assertCloudflareWranglerGatesProtected = async (dir, config2) => {
8512
+ const { enabled } = getProtectedSourceMatcher(config2);
8513
+ if (!enabled) return;
8514
+ const protectedPrefix = `${joinUrl(config2.basePath, PROTECTED_CHUNK_DIR)}/`;
8515
+ const candidates = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
8516
+ for (const name of candidates) {
8517
+ const file = await readFile3(path22.join(dir, name), "utf-8").catch(
8518
+ () => void 0
8519
+ );
8520
+ if (file === void 0) continue;
8521
+ if (file.includes("run_worker_first") && file.includes(protectedPrefix)) {
8522
+ return;
8523
+ }
8524
+ throw new Error(
8525
+ `[zudoku] ${name} must configure \`run_worker_first\` to include \`${protectedPrefix}*\` so the auth gate runs before the assets binding serves protected chunks. Without it, ${protectedPrefix}* is publicly readable. See https://developers.cloudflare.com/workers/static-assets/binding/#run_worker_first.`
8526
+ );
8527
+ }
8528
+ throw new Error(
8529
+ `[zudoku] No wrangler config found in ${dir} (looked for ${candidates.join(", ")}). Cloudflare adapter requires wrangler config with \`run_worker_first\` covering \`${protectedPrefix}*\`.`
8530
+ );
8531
+ };
8532
+
8153
8533
  // src/vite/build.ts
8154
8534
  var DIST_DIR = "dist";
8155
8535
  async function runBuild(options) {
8156
8536
  const { dir, ssr, adapter = "node" } = options;
8157
- const viteConfig = await getViteConfig(dir, {
8158
- mode: "production",
8159
- command: "build"
8160
- });
8537
+ const viteConfig = await getViteConfig(
8538
+ dir,
8539
+ { mode: "production", command: "build" },
8540
+ { adapter, ssr }
8541
+ );
8161
8542
  const builder2 = await createBuilder(viteConfig);
8162
8543
  invariant(builder2.environments.client, "Client environment is missing");
8163
8544
  invariant(builder2.environments.ssr, "SSR environment is missing");
8164
- const distDir = path21.resolve(path21.join(dir, "dist"));
8165
- await rm2(distDir, { recursive: true, force: true });
8545
+ const distDir = path23.resolve(path23.join(dir, "dist"));
8546
+ await rm3(distDir, { recursive: true, force: true });
8166
8547
  const [clientResult, serverResult] = await Promise.all([
8167
8548
  builder2.build(builder2.environments.client),
8168
8549
  builder2.build(builder2.environments.ssr)
@@ -8201,10 +8582,23 @@ async function runBuild(options) {
8201
8582
  dir,
8202
8583
  adapter,
8203
8584
  serverOutDir,
8204
- html,
8205
- basePath: config2.basePath
8585
+ html
8206
8586
  });
8207
- await rm2(path21.join(clientOutDir, "index.html"), { force: true });
8587
+ assertProtectedPatternsCovered(config2);
8588
+ assertNoProtectedLeaks(clientResult.output);
8589
+ if (adapter !== "cloudflare") {
8590
+ await moveProtectedChunks(clientOutDir, serverOutDir);
8591
+ } else {
8592
+ await assertCloudflareWranglerGatesProtected(dir, config2);
8593
+ }
8594
+ await writeFile6(
8595
+ path23.join(distDir, "package.json"),
8596
+ `${JSON.stringify({ type: "module" }, null, 2)}
8597
+ `,
8598
+ "utf-8"
8599
+ );
8600
+ await writeManifest(distDir, config2);
8601
+ await rm3(path23.join(clientOutDir, "index.html"), { force: true });
8208
8602
  } else {
8209
8603
  await runPrerender({
8210
8604
  dir,
@@ -8235,25 +8629,25 @@ var runPrerender = async (options) => {
8235
8629
  serverConfigFilename,
8236
8630
  writeRedirects: process.env.VERCEL === void 0
8237
8631
  });
8238
- const indexHtml = path21.join(clientOutDir, "index.html");
8632
+ const indexHtml = path23.join(clientOutDir, "index.html");
8239
8633
  if (!workerResults.find((r) => r.outputPath === indexHtml)) {
8240
- await writeFile5(indexHtml, html, "utf-8");
8634
+ await writeFile6(indexHtml, html, "utf-8");
8241
8635
  }
8242
8636
  const statusPages = workerResults.flatMap(
8243
- (r) => /^(400|404|500)\.html$/.test(path21.basename(r.outputPath)) ? r.outputPath : []
8637
+ (r) => /^(400|404|500)\.html$/.test(path23.basename(r.outputPath)) ? r.outputPath : []
8244
8638
  );
8245
8639
  for (const statusPage of statusPages) {
8246
- await rename(
8640
+ await rename2(
8247
8641
  statusPage,
8248
- path21.join(dir, DIST_DIR, path21.basename(statusPage))
8642
+ path23.join(dir, DIST_DIR, path23.basename(statusPage))
8249
8643
  );
8250
8644
  }
8251
- await rm2(serverOutDir, { recursive: true, force: true });
8645
+ await rm3(serverOutDir, { recursive: true, force: true });
8252
8646
  if (process.env.VERCEL) {
8253
- await mkdir5(path21.join(dir, ".vercel/output/static"), { recursive: true });
8254
- await rename(
8255
- path21.join(dir, DIST_DIR),
8256
- path21.join(dir, ".vercel/output/static")
8647
+ await mkdir6(path23.join(dir, ".vercel/output/static"), { recursive: true });
8648
+ await rename2(
8649
+ path23.join(dir, DIST_DIR),
8650
+ path23.join(dir, ".vercel/output/static")
8257
8651
  );
8258
8652
  }
8259
8653
  await writeOutput(dir, {
@@ -8263,8 +8657,8 @@ var runPrerender = async (options) => {
8263
8657
  });
8264
8658
  if (ZuploEnv.isZuplo && issuer) {
8265
8659
  const provider = config2.authentication?.type;
8266
- await writeFile5(
8267
- path21.join(dir, DIST_DIR, ".output/zuplo.json"),
8660
+ await writeFile6(
8661
+ path23.join(dir, DIST_DIR, ".output/zuplo.json"),
8268
8662
  JSON.stringify({ issuer, provider }, null, 2),
8269
8663
  "utf-8"
8270
8664
  );
@@ -8274,33 +8668,63 @@ var runPrerender = async (options) => {
8274
8668
  throw e;
8275
8669
  }
8276
8670
  };
8671
+ var findUserEntry = (dir) => {
8672
+ for (const ext of ["ts", "tsx", "js", "mjs"]) {
8673
+ const candidate = path23.join(dir, `zudoku.server.${ext}`);
8674
+ if (existsSync2(candidate)) return candidate;
8675
+ }
8676
+ };
8277
8677
  var bundleSSREntry = async (options) => {
8278
- const { dir, adapter, serverOutDir, html, basePath } = options;
8279
- const tempEntryPath = path21.join(dir, "__ssr-entry.ts");
8678
+ const { dir, adapter, serverOutDir, html } = options;
8280
8679
  const packageRoot = getZudokuRootDir();
8281
- const templateContent = await readFile3(
8282
- path21.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
8283
- "utf-8"
8284
- );
8285
- const entryContent = templateContent.replace('"__TEMPLATE__"', JSON.stringify(html)).replace(
8286
- '"__BASE_PATH__"',
8287
- basePath ? JSON.stringify(basePath) : "undefined"
8288
- );
8289
- await writeFile5(tempEntryPath, entryContent, "utf-8");
8680
+ const userEntry = findUserEntry(dir);
8681
+ let entryPoint = userEntry;
8682
+ let tempEntryPath;
8683
+ if (!entryPoint) {
8684
+ tempEntryPath = path23.join(dir, "__ssr-entry.ts");
8685
+ const templateContent = await readFile4(
8686
+ path23.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
8687
+ "utf-8"
8688
+ );
8689
+ await writeFile6(tempEntryPath, templateContent, "utf-8");
8690
+ entryPoint = tempEntryPath;
8691
+ }
8692
+ const frameworkPath = path23.join(serverOutDir, "entry.server.js");
8290
8693
  try {
8291
8694
  await esbuild({
8292
- entryPoints: [tempEntryPath],
8695
+ entryPoints: [entryPoint],
8293
8696
  bundle: true,
8294
- platform: adapter === "node" ? "node" : "neutral",
8697
+ platform: ["node", "lambda"].includes(adapter) ? "node" : "neutral",
8295
8698
  target: "es2022",
8296
8699
  format: "esm",
8297
- outfile: path21.join(serverOutDir, "entry.js"),
8298
- external: ["./entry.server.js", "./zudoku.config.js"],
8299
- nodePaths: [path21.join(packageRoot, "node_modules")],
8300
- banner: { js: "// Bundled SSR entry" }
8700
+ outfile: path23.join(serverOutDir, "entry.js"),
8701
+ external: ["./zudoku.config.js"],
8702
+ nodePaths: [path23.join(packageRoot, "node_modules")],
8703
+ banner: { js: "// Bundled SSR entry" },
8704
+ define: {
8705
+ __ZUDOKU_TEMPLATE__: JSON.stringify(html)
8706
+ },
8707
+ plugins: [
8708
+ {
8709
+ name: "zudoku-ssr-entry",
8710
+ setup(build2) {
8711
+ build2.onResolve({ filter: /^zudoku\/server$/ }, () => ({
8712
+ path: frameworkPath
8713
+ }));
8714
+ }
8715
+ }
8716
+ ]
8301
8717
  });
8718
+ await Promise.all([
8719
+ rm3(frameworkPath, { force: true }),
8720
+ rm3(`${frameworkPath}.map`, { force: true }),
8721
+ rm3(path23.join(serverOutDir, "assets"), {
8722
+ recursive: true,
8723
+ force: true
8724
+ })
8725
+ ]);
8302
8726
  } finally {
8303
- await rm2(tempEntryPath, { force: true });
8727
+ if (tempEntryPath) await rm3(tempEntryPath, { force: true });
8304
8728
  }
8305
8729
  };
8306
8730
 
@@ -8330,11 +8754,11 @@ function printWarningToConsole(message) {
8330
8754
  init_package_json();
8331
8755
 
8332
8756
  // src/cli/preview/handler.ts
8333
- import path22 from "node:path";
8757
+ import path24 from "node:path";
8334
8758
  import { preview as vitePreview } from "vite";
8335
8759
  var DEFAULT_PREVIEW_PORT = 4e3;
8336
8760
  async function preview(argv) {
8337
- const dir = path22.resolve(process.cwd(), argv.dir);
8761
+ const dir = path24.resolve(process.cwd(), argv.dir);
8338
8762
  const viteConfig = await getViteConfig(dir, {
8339
8763
  command: "serve",
8340
8764
  mode: "production",
@@ -8372,7 +8796,7 @@ async function build(argv) {
8372
8796
  printDiagnosticsToConsole(`Starting Zudoku build v${packageJson2.version}`);
8373
8797
  printDiagnosticsToConsole("");
8374
8798
  printDiagnosticsToConsole("");
8375
- const dir = path23.resolve(process.cwd(), argv.dir);
8799
+ const dir = path25.resolve(process.cwd(), argv.dir);
8376
8800
  try {
8377
8801
  await runBuild({
8378
8802
  dir,
@@ -8513,8 +8937,8 @@ var build_default = {
8513
8937
  default: false
8514
8938
  }).option("adapter", {
8515
8939
  type: "string",
8516
- describe: "SSR adapter (node, cloudflare, vercel)",
8517
- choices: ["node", "cloudflare", "vercel"],
8940
+ describe: "SSR adapter (node, cloudflare, vercel, lambda)",
8941
+ choices: ["node", "cloudflare", "vercel", "lambda"],
8518
8942
  default: "node"
8519
8943
  }),
8520
8944
  handler: async (argv) => {
@@ -8526,14 +8950,14 @@ var build_default = {
8526
8950
 
8527
8951
  // src/cli/dev/handler.ts
8528
8952
  init_joinUrl();
8529
- import path26 from "node:path";
8953
+ import path28 from "node:path";
8530
8954
 
8531
8955
  // src/vite/dev-server.ts
8532
8956
  init_logger();
8533
8957
  import fs3 from "node:fs/promises";
8534
8958
  import http from "node:http";
8535
8959
  import https from "node:https";
8536
- import path25 from "node:path";
8960
+ import path27 from "node:path";
8537
8961
  import { createHttpTerminator } from "http-terminator";
8538
8962
  import {
8539
8963
  createServer as createViteServer,
@@ -8562,11 +8986,12 @@ async function findAvailablePort(startPort) {
8562
8986
 
8563
8987
  // src/vite/dev-server.ts
8564
8988
  init_loader();
8989
+ init_joinUrl();
8565
8990
 
8566
8991
  // src/vite/pagefind-dev-index.ts
8567
8992
  init_invariant();
8568
8993
  init_joinUrl();
8569
- import path24 from "node:path";
8994
+ import path26 from "node:path";
8570
8995
  import { createIndex as createIndex2 } from "pagefind";
8571
8996
  import { isRunnableDevEnvironment } from "vite";
8572
8997
  async function* buildPagefindDevIndex(vite, config2) {
@@ -8619,7 +9044,7 @@ async function* buildPagefindDevIndex(vite, config2) {
8619
9044
  path: urlPath
8620
9045
  };
8621
9046
  }
8622
- const outputPath = path24.join(vite.config.publicDir, "pagefind");
9047
+ const outputPath = path26.join(vite.config.publicDir, "pagefind");
8623
9048
  await pagefindIndex.writeFiles({ outputPath });
8624
9049
  yield { type: "complete", success: true, indexed };
8625
9050
  }
@@ -8639,9 +9064,9 @@ var DevServer = class {
8639
9064
  this.protocol = "https";
8640
9065
  const { dir } = this.#options;
8641
9066
  const [key, cert, ca] = await Promise.all([
8642
- fs3.readFile(path25.resolve(dir, config2.https.key)),
8643
- fs3.readFile(path25.resolve(dir, config2.https.cert)),
8644
- config2.https.ca ? fs3.readFile(path25.resolve(dir, config2.https.ca)) : void 0
9067
+ fs3.readFile(path27.resolve(dir, config2.https.key)),
9068
+ fs3.readFile(path27.resolve(dir, config2.https.cert)),
9069
+ config2.https.ca ? fs3.readFile(path27.resolve(dir, config2.https.ca)) : void 0
8645
9070
  ]);
8646
9071
  return https.createServer({ key, cert, ca });
8647
9072
  }
@@ -8667,7 +9092,7 @@ var DevServer = class {
8667
9092
  // built-in transform middleware which would treat the path as a static asset.
8668
9093
  name: "zudoku:entry-client",
8669
9094
  configureServer(server2) {
8670
- const entryPath = path25.posix.join(
9095
+ const entryPath = path27.posix.join(
8671
9096
  server2.config.base,
8672
9097
  "/__z/entry.client.tsx"
8673
9098
  );
@@ -8698,6 +9123,46 @@ var DevServer = class {
8698
9123
  next();
8699
9124
  });
8700
9125
  vite.middlewares.use(graphql.graphqlEndpoint, graphql);
9126
+ const sessionEndpoint = joinUrl(config2.basePath, "/__z/auth/session");
9127
+ vite.middlewares.use(sessionEndpoint, async (req, res) => {
9128
+ if (req.method !== "POST" && req.method !== "DELETE") {
9129
+ res.writeHead(405, { Allow: "POST, DELETE" });
9130
+ res.end();
9131
+ return;
9132
+ }
9133
+ const ssrEnvironment = vite.environments.ssr;
9134
+ if (!isRunnableDevEnvironment2(ssrEnvironment)) {
9135
+ res.writeHead(500);
9136
+ res.end("SSR environment not available");
9137
+ return;
9138
+ }
9139
+ const entryServer = await ssrEnvironment.runner.import(
9140
+ getAppServerEntryPath()
9141
+ );
9142
+ const url = `${this.protocol}://${req.headers.host}${req.originalUrl ?? req.url}`;
9143
+ const body = req.method === "POST" ? await new Promise((resolve) => {
9144
+ let data = "";
9145
+ req.on("data", (chunk) => data += chunk);
9146
+ req.on("end", () => resolve(data));
9147
+ }) : void 0;
9148
+ const request = new Request(url, {
9149
+ method: req.method,
9150
+ headers: req.headers,
9151
+ body
9152
+ });
9153
+ const app = entryServer.createServer({ template: "" });
9154
+ const response = await app.fetch(request);
9155
+ for (const [name, value] of response.headers) {
9156
+ if (name.toLowerCase() === "set-cookie") continue;
9157
+ res.setHeader(name, value);
9158
+ }
9159
+ for (const cookie of response.headers.getSetCookie()) {
9160
+ res.appendHeader("Set-Cookie", cookie);
9161
+ }
9162
+ res.writeHead(response.status);
9163
+ const text = await response.text();
9164
+ res.end(text);
9165
+ });
8701
9166
  vite.middlewares.use("/__z/pagefind-reindex", async (_req, res) => {
8702
9167
  res.writeHead(200, {
8703
9168
  "Content-Type": "text/event-stream",
@@ -8740,13 +9205,13 @@ var DevServer = class {
8740
9205
  `Server-side rendering ${this.#options.ssr ? "enabled" : "disabled"}`
8741
9206
  );
8742
9207
  if (config2.search?.type === "pagefind") {
8743
- const pagefindPath = path25.join(
9208
+ const pagefindPath = path27.join(
8744
9209
  vite.config.publicDir,
8745
9210
  "pagefind/pagefind.js"
8746
9211
  );
8747
9212
  const exists = await fs3.stat(pagefindPath).catch(() => false);
8748
9213
  if (!exists) {
8749
- await fs3.mkdir(path25.dirname(pagefindPath), { recursive: true });
9214
+ await fs3.mkdir(path27.dirname(pagefindPath), { recursive: true });
8750
9215
  await fs3.writeFile(pagefindPath, 'throw new Error("NOT_BUILT_YET");');
8751
9216
  }
8752
9217
  }
@@ -8788,14 +9253,15 @@ var DevServer = class {
8788
9253
  duplex: hasBody ? "half" : void 0
8789
9254
  }
8790
9255
  );
8791
- const response = await entryServer.handleRequest({
8792
- template,
8793
- request,
8794
- routes: entryServer.getRoutesByConfig(currentConfig),
8795
- basePath: currentConfig.basePath
9256
+ const app = entryServer.createServer({ template });
9257
+ const response = await app.fetch(request);
9258
+ response.headers.forEach((value, key) => {
9259
+ if (key.toLowerCase() !== "set-cookie") {
9260
+ res.setHeader(key, value);
9261
+ }
8796
9262
  });
8797
- for (const [key, value] of response.headers) {
8798
- res.appendHeader(key, value);
9263
+ for (const cookie of response.headers.getSetCookie()) {
9264
+ res.appendHeader("Set-Cookie", cookie);
8799
9265
  }
8800
9266
  res.writeHead(response.status);
8801
9267
  for await (const chunk of response.body ?? []) {
@@ -8844,7 +9310,7 @@ init_package_json();
8844
9310
  async function dev(argv) {
8845
9311
  const packageJson2 = getZudokuPackageJson();
8846
9312
  process.env.NODE_ENV = "development";
8847
- const dir = path26.resolve(process.cwd(), argv.dir);
9313
+ const dir = path28.resolve(process.cwd(), argv.dir);
8848
9314
  const server = new DevServer({
8849
9315
  dir,
8850
9316
  argPort: argv.port,
@@ -8949,8 +9415,8 @@ var previewCommand = {
8949
9415
  var preview_default = previewCommand;
8950
9416
 
8951
9417
  // src/cli/common/outdated.ts
8952
- import { existsSync as existsSync2, mkdirSync } from "node:fs";
8953
- import { readFile as readFile4, writeFile as writeFile6 } from "node:fs/promises";
9418
+ import { existsSync as existsSync3, mkdirSync } from "node:fs";
9419
+ import { readFile as readFile5, writeFile as writeFile7 } from "node:fs/promises";
8954
9420
  import { join } from "node:path";
8955
9421
  import colors10 from "picocolors";
8956
9422
  import { gt } from "semver";
@@ -9000,12 +9466,12 @@ function box(message, {
9000
9466
 
9001
9467
  // src/cli/common/xdg/lib.ts
9002
9468
  import { homedir } from "node:os";
9003
- import path27 from "node:path";
9469
+ import path29 from "node:path";
9004
9470
  function defineDirectoryWithFallback(xdgName, fallback) {
9005
9471
  if (process.env[xdgName]) {
9006
9472
  return process.env[xdgName];
9007
9473
  } else {
9008
- return path27.join(homedir(), fallback);
9474
+ return path29.join(homedir(), fallback);
9009
9475
  }
9010
9476
  }
9011
9477
  var XDG_CONFIG_HOME = defineDirectoryWithFallback(
@@ -9020,15 +9486,15 @@ var XDG_STATE_HOME = defineDirectoryWithFallback(
9020
9486
  "XDG_DATA_HOME",
9021
9487
  ".local/state"
9022
9488
  );
9023
- var ZUDOKU_XDG_CONFIG_HOME = path27.join(
9489
+ var ZUDOKU_XDG_CONFIG_HOME = path29.join(
9024
9490
  XDG_CONFIG_HOME,
9025
9491
  CLI_XDG_FOLDER_NAME
9026
9492
  );
9027
- var ZUDOKU_XDG_DATA_HOME = path27.join(
9493
+ var ZUDOKU_XDG_DATA_HOME = path29.join(
9028
9494
  XDG_DATA_HOME,
9029
9495
  CLI_XDG_FOLDER_NAME
9030
9496
  );
9031
- var ZUDOKU_XDG_STATE_HOME = path27.join(
9497
+ var ZUDOKU_XDG_STATE_HOME = path29.join(
9032
9498
  XDG_STATE_HOME,
9033
9499
  CLI_XDG_FOLDER_NAME
9034
9500
  );
@@ -9070,14 +9536,14 @@ async function getLatestVersion() {
9070
9536
  return void 0;
9071
9537
  }
9072
9538
  async function getVersionCheckInfo() {
9073
- if (!existsSync2(ZUDOKU_XDG_STATE_HOME)) {
9539
+ if (!existsSync3(ZUDOKU_XDG_STATE_HOME)) {
9074
9540
  mkdirSync(ZUDOKU_XDG_STATE_HOME, { recursive: true });
9075
9541
  }
9076
9542
  const versionCheckPath = join(ZUDOKU_XDG_STATE_HOME, VERSION_CHECK_FILE);
9077
9543
  let versionCheckInfo;
9078
- if (existsSync2(versionCheckPath)) {
9544
+ if (existsSync3(versionCheckPath)) {
9079
9545
  try {
9080
- versionCheckInfo = await readFile4(versionCheckPath, "utf-8").then(
9546
+ versionCheckInfo = await readFile5(versionCheckPath, "utf-8").then(
9081
9547
  JSON.parse
9082
9548
  );
9083
9549
  } catch {
@@ -9102,7 +9568,7 @@ async function getVersionCheckInfo() {
9102
9568
  lastCheck: Date.now(),
9103
9569
  latestVersion
9104
9570
  };
9105
- await writeFile6(
9571
+ await writeFile7(
9106
9572
  versionCheckPath,
9107
9573
  JSON.stringify(versionCheckInfo),
9108
9574
  "utf-8"