zudoku 0.78.0 → 0.78.2

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 (91) hide show
  1. package/dist/cli/cli.js +602 -160
  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 +81 -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 +1 -0
  20. package/dist/declarations/lib/authentication/providers/openid.d.ts +2 -1
  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 +5 -2
  30. package/dist/declarations/lib/manifest.d.ts +25 -0
  31. package/dist/declarations/lib/util/url.d.ts +2 -0
  32. package/dist/flat-config.d.ts +1 -1
  33. package/docs/configuration/protected-routes.md +21 -4
  34. package/docs/guides/server-side-content-protection.md +207 -0
  35. package/package.json +26 -4
  36. package/src/app/adapter.ts +16 -0
  37. package/src/app/adapters/cloudflare.ts +18 -0
  38. package/src/app/adapters/lambda.ts +36 -0
  39. package/src/app/adapters/node.ts +32 -0
  40. package/src/app/adapters/vercel.ts +39 -0
  41. package/src/app/demo.tsx +2 -2
  42. package/src/app/entry.client.tsx +19 -7
  43. package/src/app/entry.server.tsx +133 -9
  44. package/src/app/main.tsx +21 -3
  45. package/src/app/protectChunks.ts +64 -0
  46. package/src/app/standalone.tsx +2 -2
  47. package/src/app/wrapProtectedRoutes.ts +82 -0
  48. package/src/config/validators/icon-types.ts +17 -0
  49. package/src/lib/authentication/authentication.ts +15 -0
  50. package/src/lib/authentication/cookie-sync.ts +90 -0
  51. package/src/lib/authentication/cookies.ts +54 -0
  52. package/src/lib/authentication/hook.ts +13 -0
  53. package/src/lib/authentication/providers/azureb2c.tsx +70 -2
  54. package/src/lib/authentication/providers/clerk.tsx +49 -0
  55. package/src/lib/authentication/providers/openid.tsx +46 -0
  56. package/src/lib/authentication/providers/supabase.tsx +30 -2
  57. package/src/lib/authentication/session-handler.ts +164 -0
  58. package/src/lib/authentication/state.ts +36 -5
  59. package/src/lib/authentication/verify-cache.ts +32 -0
  60. package/src/lib/components/Bootstrap.tsx +20 -14
  61. package/src/lib/components/Header.tsx +56 -57
  62. package/src/lib/components/MobileTopNavigation.tsx +66 -67
  63. package/src/lib/components/Zudoku.tsx +14 -1
  64. package/src/lib/components/context/RenderContext.ts +8 -0
  65. package/src/lib/components/context/ZudokuContext.ts +2 -1
  66. package/src/lib/core/RouteGuard.tsx +50 -29
  67. package/src/lib/core/ZudokuContext.ts +39 -6
  68. package/src/lib/errors/RouterError.tsx +43 -1
  69. package/src/lib/manifest.ts +62 -0
  70. package/src/lib/oas/parser/dereference/index.ts +2 -1
  71. package/src/lib/oas/parser/dereference/resolveRef.ts +2 -1
  72. package/src/lib/oas/parser/index.ts +1 -1
  73. package/src/lib/plugins/openapi/client/createServer.ts +13 -4
  74. package/src/lib/plugins/search-pagefind/index.tsx +1 -4
  75. package/src/lib/util/os.ts +1 -0
  76. package/src/lib/util/url.ts +13 -0
  77. package/src/vite/build.ts +84 -24
  78. package/src/vite/config.ts +51 -5
  79. package/src/vite/dev-server.ts +61 -8
  80. package/src/vite/manifest.ts +15 -0
  81. package/src/vite/plugin-api.ts +3 -1
  82. package/src/vite/plugin-markdown-export.ts +3 -9
  83. package/src/vite/prerender/worker.ts +2 -4
  84. package/src/vite/protected/annotator.ts +136 -0
  85. package/src/vite/protected/build.ts +151 -0
  86. package/src/vite/protected/registry.ts +82 -0
  87. package/src/vite/ssr-templates/cloudflare.ts +5 -18
  88. package/src/vite/ssr-templates/lambda.ts +4 -0
  89. package/src/vite/ssr-templates/node.ts +7 -22
  90. package/src/vite/ssr-templates/vercel.ts +6 -20
  91. package/src/vite-env.d.ts +1 -0
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;
@@ -3596,8 +3623,8 @@ var llms_exports = {};
3596
3623
  __export(llms_exports, {
3597
3624
  generateLlmsTxtFiles: () => generateLlmsTxtFiles
3598
3625
  });
3599
- import { writeFile as writeFile4 } from "node:fs/promises";
3600
- import path19 from "node:path";
3626
+ import { writeFile as writeFile5 } from "node:fs/promises";
3627
+ import path20 from "node:path";
3601
3628
  import colors6 from "picocolors";
3602
3629
  async function generateLlmsTxtFiles({
3603
3630
  markdownFileInfos,
@@ -3632,7 +3659,7 @@ async function generateLlmsTxtFiles({
3632
3659
  }
3633
3660
  }
3634
3661
  const llmsTxt2 = llmsTxtParts.join("\n");
3635
- await writeFile4(path19.join(baseOutputDir, "llms.txt"), llmsTxt2, "utf-8");
3662
+ await writeFile5(path20.join(baseOutputDir, "llms.txt"), llmsTxt2, "utf-8");
3636
3663
  console.log(colors6.blue("\u2713 generated llms.txt"));
3637
3664
  }
3638
3665
  if (llmsTxtFull) {
@@ -3657,8 +3684,8 @@ ${info.content}
3657
3684
  `);
3658
3685
  }
3659
3686
  const llmsFull = llmsFullParts.join("\n");
3660
- await writeFile4(
3661
- path19.join(baseOutputDir, "llms-full.txt"),
3687
+ await writeFile5(
3688
+ path20.join(baseOutputDir, "llms-full.txt"),
3662
3689
  llmsFull,
3663
3690
  "utf-8"
3664
3691
  );
@@ -3677,11 +3704,12 @@ import { hideBin } from "yargs/helpers";
3677
3704
  import yargs from "yargs/yargs";
3678
3705
 
3679
3706
  // src/cli/build/handler.ts
3680
- import path23 from "node:path";
3707
+ import path25 from "node:path";
3681
3708
 
3682
3709
  // src/vite/build.ts
3683
- import { mkdir as mkdir5, readFile as readFile3, rename, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
3684
- 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";
3685
3713
  import { build as esbuild } from "esbuild";
3686
3714
  import { createBuilder } from "vite";
3687
3715
 
@@ -3825,7 +3853,7 @@ import {
3825
3853
  // package.json
3826
3854
  var package_default = {
3827
3855
  name: "zudoku",
3828
- version: "0.77.0",
3856
+ version: "0.78.1",
3829
3857
  type: "module",
3830
3858
  sideEffects: [
3831
3859
  "**/*.css",
@@ -3884,6 +3912,11 @@ var package_default = {
3884
3912
  "./react-query": "./src/lib/core/react-query.ts",
3885
3913
  "./icons": "./src/lib/icons.ts",
3886
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",
3887
3920
  "./app/*": "./src/app/*",
3888
3921
  "./hooks": "./src/lib/hooks/index.ts",
3889
3922
  "./processors/*": "./src/lib/plugins/openapi/processors/*.ts",
@@ -3974,9 +4007,10 @@ var package_default = {
3974
4007
  hono: "4.12.18",
3975
4008
  "http-terminator": "3.2.0",
3976
4009
  "javascript-stringify": "2.1.0",
4010
+ jose: "6.2.2",
3977
4011
  "json-schema-to-typescript-lite": "15.0.0",
3978
4012
  loglevel: "1.9.2",
3979
- "lucide-react": "1.8.0",
4013
+ "lucide-react": "1.16.0",
3980
4014
  "mdast-util-from-markdown": "2.0.2",
3981
4015
  "mdast-util-mdx": "3.0.0",
3982
4016
  "mdast-util-mdx-jsx": "3.2.0",
@@ -3990,6 +4024,7 @@ var package_default = {
3990
4024
  picocolors: "1.1.1",
3991
4025
  piscina: "5.1.4",
3992
4026
  "posthog-node": "5.33.4",
4027
+ "quick-lru": "7.3.0",
3993
4028
  "react-error-boundary": "6.1.1",
3994
4029
  "react-hook-form": "7.75.0",
3995
4030
  "react-is": "catalog:",
@@ -4009,13 +4044,13 @@ var package_default = {
4009
4044
  sitemap: "9.0.1",
4010
4045
  "strip-ansi": "7.2.0",
4011
4046
  "tailwind-merge": "3.6.0",
4012
- tailwindcss: "4.2.1",
4047
+ tailwindcss: "4.3.0",
4013
4048
  "tw-animate-css": "1.4.0",
4014
4049
  unified: "11.0.5",
4015
4050
  "unist-util-visit": "5.1.0",
4016
4051
  vaul: "1.1.2",
4017
4052
  vfile: "6.0.3",
4018
- vite: "8.0.9",
4053
+ vite: "8.0.13",
4019
4054
  yaml: "2.8.4",
4020
4055
  yargs: "18.0.0",
4021
4056
  zod: "4.3.6",
@@ -4081,6 +4116,46 @@ init_logger();
4081
4116
  init_package_json();
4082
4117
  init_loader();
4083
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
4084
4159
  init_joinUrl();
4085
4160
 
4086
4161
  // src/vite/package-root.ts
@@ -4426,14 +4501,14 @@ var flattenAllOf = (schema2) => {
4426
4501
  };
4427
4502
 
4428
4503
  // src/lib/util/traverse.ts
4429
- var traverse = (specification, transform, path28 = []) => {
4430
- const transformed = transform(specification, path28);
4504
+ var traverse = (specification, transform, path30 = []) => {
4505
+ const transformed = transform(specification, path30);
4431
4506
  if (typeof transformed !== "object" || transformed === null) {
4432
4507
  return transformed;
4433
4508
  }
4434
4509
  const result = Array.isArray(transformed) ? [] : {};
4435
4510
  for (const [key, value] of Object.entries(transformed)) {
4436
- const currentPath = [...path28, key];
4511
+ const currentPath = [...path30, key];
4437
4512
  if (Array.isArray(value)) {
4438
4513
  result[key] = value.map(
4439
4514
  (item, index) => typeof item === "object" && item != null ? traverse(item, transform, [...currentPath, index.toString()]) : item
@@ -4452,7 +4527,7 @@ var CIRCULAR_REF = "$[Circular Reference]";
4452
4527
  var SCHEMA_REF_PREFIX = "$ref:";
4453
4528
 
4454
4529
  // src/lib/oas/parser/dereference/resolveRef.ts
4455
- var cache = /* @__PURE__ */ new Map();
4530
+ var cache = /* @__PURE__ */ new WeakMap();
4456
4531
  var resolveLocalRef = (schema2, ref) => {
4457
4532
  if (!cache.has(schema2)) {
4458
4533
  cache.set(schema2, /* @__PURE__ */ new Map());
@@ -4461,9 +4536,9 @@ var resolveLocalRef = (schema2, ref) => {
4461
4536
  if (schemaCache?.has(ref)) {
4462
4537
  return schemaCache.get(ref);
4463
4538
  }
4464
- const path28 = ref.split("/").slice(1);
4539
+ const path30 = ref.split("/").slice(1);
4465
4540
  let current = schema2;
4466
- for (const segment of path28) {
4541
+ for (const segment of path30) {
4467
4542
  if (!current || typeof current !== "object") {
4468
4543
  current = null;
4469
4544
  }
@@ -4474,7 +4549,7 @@ var resolveLocalRef = (schema2, ref) => {
4474
4549
  };
4475
4550
 
4476
4551
  // src/lib/oas/parser/dereference/index.ts
4477
- var cache2 = /* @__PURE__ */ new Map();
4552
+ var cache2 = /* @__PURE__ */ new WeakMap();
4478
4553
  var isIndexableObject = (obj) => obj !== null && typeof obj === "object";
4479
4554
  var dereference = async (schema2, resolvers = []) => {
4480
4555
  if (cache2.has(schema2)) {
@@ -4482,7 +4557,7 @@ var dereference = async (schema2, resolvers = []) => {
4482
4557
  }
4483
4558
  const cloned = structuredClone(schema2);
4484
4559
  const visited = /* @__PURE__ */ new Set();
4485
- const resolve = async (current, path28) => {
4560
+ const resolve = async (current, path30) => {
4486
4561
  if (isIndexableObject(current)) {
4487
4562
  if (visited.has(current)) {
4488
4563
  return CIRCULAR_REF;
@@ -4490,7 +4565,7 @@ var dereference = async (schema2, resolvers = []) => {
4490
4565
  visited.add(current);
4491
4566
  if (Array.isArray(current)) {
4492
4567
  for (let index = 0; index < current.length; index++) {
4493
- current[index] = await resolve(current[index], `${path28}/${index}`);
4568
+ current[index] = await resolve(current[index], `${path30}/${index}`);
4494
4569
  }
4495
4570
  } else {
4496
4571
  if ("$ref" in current && typeof current.$ref === "string") {
@@ -4501,13 +4576,13 @@ var dereference = async (schema2, resolvers = []) => {
4501
4576
  for (const resolver of resolvers) {
4502
4577
  const resolved = await resolver($ref);
4503
4578
  if (resolved) {
4504
- result2 = await resolve(resolved, path28);
4579
+ result2 = await resolve(resolved, path30);
4505
4580
  break;
4506
4581
  }
4507
4582
  }
4508
4583
  if (result2 === void 0) {
4509
4584
  const resolved = await resolveLocalRef(cloned, $ref);
4510
- result2 = await resolve(resolved, path28);
4585
+ result2 = await resolve(resolved, path30);
4511
4586
  }
4512
4587
  if (hasSiblings) {
4513
4588
  if (result2 === CIRCULAR_REF) {
@@ -4520,7 +4595,7 @@ var dereference = async (schema2, resolvers = []) => {
4520
4595
  return result2;
4521
4596
  }
4522
4597
  for (const key in current) {
4523
- current[key] = await resolve(current[key], `${path28}/${key}`);
4598
+ current[key] = await resolve(current[key], `${path30}/${key}`);
4524
4599
  }
4525
4600
  }
4526
4601
  visited.delete(current);
@@ -4559,9 +4634,9 @@ var upgradeSchema = (schema2) => {
4559
4634
  }
4560
4635
  return sub;
4561
4636
  });
4562
- schema2 = traverse(schema2, (sub, path28) => {
4637
+ schema2 = traverse(schema2, (sub, path30) => {
4563
4638
  if (sub.example !== void 0) {
4564
- if (isSchemaPath(path28 ?? [])) {
4639
+ if (isSchemaPath(path30 ?? [])) {
4565
4640
  sub.examples = [sub.example];
4566
4641
  } else {
4567
4642
  sub.examples = {
@@ -4574,11 +4649,11 @@ var upgradeSchema = (schema2) => {
4574
4649
  }
4575
4650
  return sub;
4576
4651
  });
4577
- schema2 = traverse(schema2, (schema3, path28) => {
4652
+ schema2 = traverse(schema2, (schema3, path30) => {
4578
4653
  if (schema3.type === "object" && schema3.properties !== void 0) {
4579
- const parentPath = path28?.slice(0, -1);
4654
+ const parentPath = path30?.slice(0, -1);
4580
4655
  const isMultipart = parentPath?.some((segment, index) => {
4581
- return segment === "content" && path28?.[index + 1] === "multipart/form-data";
4656
+ return segment === "content" && path30?.[index + 1] === "multipart/form-data";
4582
4657
  });
4583
4658
  if (isMultipart) {
4584
4659
  const entries = Object.entries(schema3.properties);
@@ -4592,8 +4667,8 @@ var upgradeSchema = (schema2) => {
4592
4667
  }
4593
4668
  return schema3;
4594
4669
  });
4595
- schema2 = traverse(schema2, (schema3, path28) => {
4596
- 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")) {
4597
4672
  return {};
4598
4673
  }
4599
4674
  if (schema3.type === "string" && schema3.format === "binary") {
@@ -4619,11 +4694,11 @@ var upgradeSchema = (schema2) => {
4619
4694
  }
4620
4695
  return sub;
4621
4696
  });
4622
- schema2 = traverse(schema2, (schema3, path28) => {
4697
+ schema2 = traverse(schema2, (schema3, path30) => {
4623
4698
  if (schema3.type === "string" && schema3.format === "byte") {
4624
- const parentPath = path28?.slice(0, -1);
4699
+ const parentPath = path30?.slice(0, -1);
4625
4700
  const contentMediaType = parentPath?.find(
4626
- (_, index) => path28?.[index - 1] === "content"
4701
+ (_, index) => path30?.[index - 1] === "content"
4627
4702
  );
4628
4703
  return {
4629
4704
  type: "string",
@@ -4635,7 +4710,7 @@ var upgradeSchema = (schema2) => {
4635
4710
  });
4636
4711
  return schema2;
4637
4712
  };
4638
- function isSchemaPath(path28) {
4713
+ function isSchemaPath(path30) {
4639
4714
  const schemaLocations = [
4640
4715
  ["components", "schemas"],
4641
4716
  "properties",
@@ -4648,10 +4723,10 @@ function isSchemaPath(path28) {
4648
4723
  ];
4649
4724
  return schemaLocations.some((location) => {
4650
4725
  if (Array.isArray(location)) {
4651
- return location.every((segment, index) => path28[index] === segment);
4726
+ return location.every((segment, index) => path30[index] === segment);
4652
4727
  }
4653
- return path28.includes(location);
4654
- }) || path28.includes("schema") || path28.some((segment) => segment.endsWith("Schema"));
4728
+ return path30.includes(location);
4729
+ }) || path30.includes("schema") || path30.some((segment) => segment.endsWith("Schema"));
4655
4730
  }
4656
4731
 
4657
4732
  // src/lib/oas/parser/index.ts
@@ -4671,7 +4746,7 @@ var parseSchemaInput = async (schemaInput) => {
4671
4746
  let response;
4672
4747
  try {
4673
4748
  response = await fetch(schemaInput, {
4674
- cache: "force-cache"
4749
+ cache: typeof window !== "undefined" ? "force-cache" : void 0
4675
4750
  });
4676
4751
  } catch (err) {
4677
4752
  throw new GraphQLError("Failed to fetch schema", {
@@ -4733,13 +4808,13 @@ var OPENAPI_PROPS = /* @__PURE__ */ new Set([
4733
4808
  "anyOf",
4734
4809
  "oneOf"
4735
4810
  ]);
4736
- 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()) => {
4737
4812
  if (obj === null || typeof obj !== "object") return obj;
4738
4813
  const refPath = obj.__$ref;
4739
4814
  const isCircular = currentPath.has(obj) || typeof refPath === "string" && currentRefPaths.has(refPath);
4740
4815
  if (isCircular) {
4741
4816
  if (typeof refPath === "string") return SCHEMA_REF_PREFIX + refPath;
4742
- const circularProp = path28.find((p) => !OPENAPI_PROPS.has(p)) || path28[0];
4817
+ const circularProp = path30.find((p) => !OPENAPI_PROPS.has(p)) || path30[0];
4743
4818
  return [CIRCULAR_REF, circularProp].filter(Boolean).join(":");
4744
4819
  }
4745
4820
  if (refs.has(obj)) return refs.get(obj);
@@ -4749,7 +4824,7 @@ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs
4749
4824
  value,
4750
4825
  currentPath,
4751
4826
  refs,
4752
- [...path28, key],
4827
+ [...path30, key],
4753
4828
  currentRefPaths
4754
4829
  );
4755
4830
  const result = Array.isArray(obj) ? obj.map((item, i) => recurse(item, i.toString())) : Object.fromEntries(
@@ -4787,7 +4862,7 @@ var resolveExtensions = (obj) => Object.fromEntries(
4787
4862
  var getAllTags = (schema2) => {
4788
4863
  const rootTags = schema2.tags ?? [];
4789
4864
  const operations = Object.values(schema2.paths ?? {}).flatMap(
4790
- (path28) => HttpMethods.map((k) => path28?.[k]).filter((op) => op != null)
4865
+ (path30) => HttpMethods.map((k) => path30?.[k]).filter((op) => op != null)
4791
4866
  );
4792
4867
  const operationTags = new Set(operations.flatMap((op) => op.tags ?? []));
4793
4868
  const hasUntaggedOperations = operations.some(
@@ -4842,7 +4917,7 @@ var getAllSlugs = (ops, schemaTags = []) => {
4842
4917
  var getOperationSlugKey = (op) => [op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
4843
4918
  var getAllOperations = (paths) => {
4844
4919
  const operations = Object.entries(paths ?? {}).flatMap(
4845
- ([path28, value]) => HttpMethods.flatMap((method) => {
4920
+ ([path30, value]) => HttpMethods.flatMap((method) => {
4846
4921
  if (!value?.[method]) return [];
4847
4922
  const operation = value[method];
4848
4923
  const pathParameters = value.parameters ?? [];
@@ -4862,7 +4937,7 @@ var getAllOperations = (paths) => {
4862
4937
  return {
4863
4938
  ...operation,
4864
4939
  method,
4865
- path: path28,
4940
+ path: path30,
4866
4941
  parameters,
4867
4942
  servers,
4868
4943
  tags: operation.tags ?? []
@@ -5064,10 +5139,10 @@ var resolveSecuritySchemes = (schema2) => {
5064
5139
  var resolveSecurityRequirements = (securityArray, securitySchemes) => {
5065
5140
  if (!securityArray) return [];
5066
5141
  return securityArray.map((req) => ({
5067
- schemes: Object.entries(req).filter(([key]) => !key.startsWith("__")).flatMap(([schemeName, scopes]) => {
5142
+ schemes: Object.entries(req).filter(([key]) => !key.startsWith("__")).flatMap(([schemeName, scopes2]) => {
5068
5143
  const scheme = securitySchemes.find((s) => s.name === schemeName);
5069
5144
  if (!scheme) return [];
5070
- return [{ scheme, scopes }];
5145
+ return [{ scheme, scopes: scopes2 }];
5071
5146
  })
5072
5147
  }));
5073
5148
  };
@@ -5360,8 +5435,8 @@ var Schema = builder.objectRef("Schema").implement({
5360
5435
  }),
5361
5436
  paths: t.field({
5362
5437
  type: [PathItem],
5363
- resolve: (root) => Object.entries(root.paths ?? {}).map(([path28, value]) => ({
5364
- path: path28,
5438
+ resolve: (root) => Object.entries(root.paths ?? {}).map(([path30, value]) => ({
5439
+ path: path30,
5365
5440
  // biome-ignore lint/style/noNonNullAssertion: value is guaranteed to be defined
5366
5441
  methods: Object.keys(value)
5367
5442
  }))
@@ -5517,7 +5592,7 @@ init_joinUrl();
5517
5592
 
5518
5593
  // src/vite/api/schema-codegen.ts
5519
5594
  var unescapeJsonPointer = (uri) => decodeURIComponent(uri.replace(/~1/g, "/").replace(/~0/g, "~"));
5520
- var getSegmentsFromPath = (path28) => path28.split("/").slice(1).map(unescapeJsonPointer);
5595
+ var getSegmentsFromPath = (path30) => path30.split("/").slice(1).map(unescapeJsonPointer);
5521
5596
  var createLocalRefMap = (obj) => {
5522
5597
  const refMap = /* @__PURE__ */ new Map();
5523
5598
  const siblingsMap = /* @__PURE__ */ new Map();
@@ -5548,16 +5623,16 @@ var replaceMarkers = (code, mergedRefs) => code.replace(/"__refMap:(.*?)"/g, '__
5548
5623
  /"__refMap\+Siblings:(.*?)"/g,
5549
5624
  (_, key) => mergedRefs.get(key) ?? `__refMap["${key}"]`
5550
5625
  );
5551
- var lookup = (schema2, path28, filePath) => {
5552
- const parts = getSegmentsFromPath(path28);
5626
+ var lookup = (schema2, path30, filePath) => {
5627
+ const parts = getSegmentsFromPath(path30);
5553
5628
  let val = schema2;
5554
5629
  for (const part of parts) {
5555
5630
  while (val.$ref?.startsWith("#/")) {
5556
- val = val.$ref === path28 ? val : lookup(schema2, val.$ref, filePath);
5631
+ val = val.$ref === path30 ? val : lookup(schema2, val.$ref, filePath);
5557
5632
  }
5558
5633
  if (val[part] === void 0) {
5559
5634
  throw new Error(
5560
- `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}`
5561
5636
  );
5562
5637
  }
5563
5638
  val = val[part];
@@ -5806,8 +5881,8 @@ var SchemaManager = class {
5806
5881
  }
5807
5882
  }
5808
5883
  };
5809
- getLatestSchema = (path28) => this.processedSchemas[path28]?.at(0);
5810
- getSchemasForPath = (path28) => this.processedSchemas[path28];
5884
+ getLatestSchema = (path30) => this.processedSchemas[path30]?.at(0);
5885
+ getSchemasForPath = (path30) => this.processedSchemas[path30];
5811
5886
  getSchemaImports = () => Object.values(this.processedSchemas).flat().filter((s) => s.importKey);
5812
5887
  getUrlToFilePathMap = () => {
5813
5888
  const map = /* @__PURE__ */ new Map();
@@ -6173,6 +6248,7 @@ var viteConfigReloadPlugin = () => ({
6173
6248
  });
6174
6249
 
6175
6250
  // src/vite/plugin-api.ts
6251
+ var PROCESSED_STORE_SUBPATH = "node_modules/.zudoku/processed";
6176
6252
  var viteApiPlugin = async () => {
6177
6253
  const virtualModuleId4 = "virtual:zudoku-api-plugins";
6178
6254
  const resolvedVirtualModuleId4 = `\0${virtualModuleId4}`;
@@ -6184,7 +6260,7 @@ var viteApiPlugin = async () => {
6184
6260
  const buildProcessors = buildConfig?.processors ?? [];
6185
6261
  const tmpStoreDir = path11.posix.join(
6186
6262
  initialConfig.__meta.rootDir,
6187
- "node_modules/.zudoku/processed"
6263
+ PROCESSED_STORE_SUBPATH
6188
6264
  );
6189
6265
  const processors = [...buildProcessors, ...zuploProcessors];
6190
6266
  const schemaManager = new SchemaManager({
@@ -6764,7 +6840,7 @@ init_ProtectedRoutesSchema();
6764
6840
  init_joinUrl();
6765
6841
  import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
6766
6842
  import path13 from "node:path";
6767
- import { matchPath } from "react-router";
6843
+ init_url();
6768
6844
  var processMarkdownFile = async (filePath) => {
6769
6845
  const { content: markdownContent, data: frontmatter } = await readFrontmatter(filePath);
6770
6846
  let finalMarkdown = markdownContent;
@@ -6816,13 +6892,9 @@ var viteMarkdownExportPlugin = () => {
6816
6892
  config2.protectedRoutes
6817
6893
  );
6818
6894
  if (protectedRoutes) {
6819
- const isProtectedRoute = (routePath) => {
6820
- return Object.keys(protectedRoutes).some(
6821
- (route) => matchPath({ path: route, end: true }, routePath)
6822
- );
6823
- };
6895
+ const patterns = Object.keys(protectedRoutes);
6824
6896
  for (const routePath of Object.keys(markdownFiles)) {
6825
- if (isProtectedRoute(routePath)) {
6897
+ if (matchesAnyProtectedPattern(patterns, routePath)) {
6826
6898
  delete markdownFiles[routePath];
6827
6899
  }
6828
6900
  }
@@ -7501,6 +7573,155 @@ function vitePlugin() {
7501
7573
  ];
7502
7574
  }
7503
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
+
7504
7725
  // src/vite/config.ts
7505
7726
  var getAppClientEntryPath = () => path16.posix.join(getZudokuRootDir(), "src/app/entry.client.tsx");
7506
7727
  var getAppServerEntryPath = () => path16.posix.join(getZudokuRootDir(), "src/app/entry.server.tsx");
@@ -7512,11 +7733,15 @@ var defineEnvVars = (vars) => Object.fromEntries(
7512
7733
  [`import.meta.env.${v}`, JSON.stringify(process.env[v])]
7513
7734
  ])
7514
7735
  );
7515
- async function getViteConfig(dir, configEnv) {
7736
+ async function getViteConfig(dir, configEnv, options = {}) {
7516
7737
  const { config: config2, publicEnv: publicEnv2, envPrefix: envPrefix2 } = await loadZudokuConfig(
7517
7738
  configEnv,
7518
7739
  dir
7519
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";
7520
7745
  const cdnUrl = CdnUrlSchema.parse(config2.cdnUrl);
7521
7746
  const base = cdnUrl?.base ? joinUrl(cdnUrl.base, config2.basePath) : config2.basePath;
7522
7747
  if (cdnUrl && !hasLoggedCdnInfo) {
@@ -7542,6 +7767,10 @@ async function getViteConfig(dir, configEnv) {
7542
7767
  root: dir,
7543
7768
  base,
7544
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,
7545
7774
  configFile: false,
7546
7775
  clearScreen: false,
7547
7776
  logLevel: process.env.LOG_LEVEL ?? "info",
@@ -7557,6 +7786,7 @@ async function getViteConfig(dir, configEnv) {
7557
7786
  "process.env.ZUDOKU_VERSION": JSON.stringify(package_default.version),
7558
7787
  "process.env.IS_ZUPLO": ZuploEnv.isZuplo,
7559
7788
  "import.meta.env.IS_ZUPLO": ZuploEnv.isZuplo,
7789
+ "import.meta.env.ZUDOKU_HAS_SERVER": JSON.stringify(options.ssr === true),
7560
7790
  "import.meta.env.ZUPLO_PUBLIC_DEPLOYMENT_NAME": JSON.stringify(deploymentName),
7561
7791
  ...defineEnvVars([
7562
7792
  "SENTRY_DSN",
@@ -7599,13 +7829,19 @@ async function getViteConfig(dir, configEnv) {
7599
7829
  environments: {
7600
7830
  client: {
7601
7831
  build: {
7832
+ manifest: true,
7602
7833
  rolldownOptions: {
7603
- input: "zudoku/app/entry.client.tsx"
7834
+ input: "zudoku/app/entry.client.tsx",
7835
+ output: shouldProtectChunks ? {
7836
+ entryFileNames: (chunk) => isProtectedChunk(chunk) ? `${PROTECTED_CHUNK_DIR}/[name]-[hash].js` : "assets/[name]-[hash].js",
7837
+ chunkFileNames: (chunk) => isProtectedChunk(chunk) ? `${PROTECTED_CHUNK_DIR}/[name]-[hash].js` : "assets/[name]-[hash].js"
7838
+ } : void 0
7604
7839
  }
7605
7840
  }
7606
7841
  },
7607
7842
  ssr: {
7608
- resolve: {
7843
+ // Build: bundle all for self-contained SSR output; dev uses minimal externals for speed.
7844
+ resolve: configEnv.command === "build" ? { noExternal: true } : {
7609
7845
  noExternal: [/zudoku/, "@mdx-js/react"],
7610
7846
  external: ["@shikijs/themes", "@shikijs/langs"]
7611
7847
  },
@@ -7613,7 +7849,9 @@ async function getViteConfig(dir, configEnv) {
7613
7849
  outDir: path16.resolve(
7614
7850
  path16.join(dir, "dist", config2.basePath ?? "", "server")
7615
7851
  ),
7852
+ copyPublicDir: false,
7616
7853
  rolldownOptions: {
7854
+ logLevel: "warn",
7617
7855
  input: ["zudoku/app/entry.server.tsx", config2.__meta.configPath]
7618
7856
  }
7619
7857
  }
@@ -7621,6 +7859,9 @@ async function getViteConfig(dir, configEnv) {
7621
7859
  },
7622
7860
  experimental: {
7623
7861
  renderBuiltUrl(filename) {
7862
+ if (filename.startsWith(`${PROTECTED_CHUNK_DIR}/`)) {
7863
+ return joinUrl(config2.basePath, `/${filename}`);
7864
+ }
7624
7865
  if (cdnUrl?.base && [".js", ".css"].includes(path16.extname(filename))) {
7625
7866
  return joinUrl(cdnUrl.base, filename);
7626
7867
  }
@@ -7645,7 +7886,7 @@ async function getViteConfig(dir, configEnv) {
7645
7886
  "/__z/entry.client.tsx",
7646
7887
  "**/pagefind.js"
7647
7888
  ],
7648
- plugins: [vitePlugin()],
7889
+ plugins: [protectedAnnotatorPlugin(), vitePlugin()],
7649
7890
  future: {
7650
7891
  removeServerModuleGraph: "warn",
7651
7892
  removeSsrLoadModule: "warn",
@@ -7728,12 +7969,24 @@ ${cssLinks}
7728
7969
  `.trim();
7729
7970
  }
7730
7971
 
7972
+ // src/vite/manifest.ts
7973
+ import { writeFile as writeFile3 } from "node:fs/promises";
7974
+ import path17 from "node:path";
7975
+ var writeManifest = async (distDir, config2) => {
7976
+ await writeFile3(
7977
+ path17.join(distDir, MANIFEST_FILENAME),
7978
+ `${JSON.stringify(buildManifest(config2), null, 2)}
7979
+ `,
7980
+ "utf-8"
7981
+ );
7982
+ };
7983
+
7731
7984
  // src/vite/output.ts
7732
7985
  init_package_json();
7733
7986
  init_joinUrl();
7734
7987
  import assert from "node:assert";
7735
- import { cp, mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
7736
- import path17 from "node:path";
7988
+ import { cp, mkdir as mkdir3, writeFile as writeFile4 } from "node:fs/promises";
7989
+ import path18 from "node:path";
7737
7990
  var pkgJson = getZudokuPackageJson();
7738
7991
  function generateOutput({
7739
7992
  config: config2,
@@ -7790,10 +8043,10 @@ async function writeOutput(dir, {
7790
8043
  rewrites
7791
8044
  }) {
7792
8045
  const output = generateOutput({ config: config2, redirects, rewrites });
7793
- const outputDir = process.env.VERCEL ? path17.join(dir, ".vercel/output") : path17.join(dir, "dist/.output");
8046
+ const outputDir = process.env.VERCEL ? path18.join(dir, ".vercel/output") : path18.join(dir, "dist/.output");
7794
8047
  await mkdir3(outputDir, { recursive: true });
7795
- const outputFile = path17.join(outputDir, "config.json");
7796
- await writeFile3(outputFile, JSON.stringify(output, null, 2), "utf-8");
8048
+ const outputFile = path18.join(outputDir, "config.json");
8049
+ await writeFile4(outputFile, JSON.stringify(output, null, 2), "utf-8");
7797
8050
  if (process.env.VERCEL) {
7798
8051
  console.log("Wrote Vercel output to", outputDir);
7799
8052
  }
@@ -7805,7 +8058,7 @@ init_file_exists();
7805
8058
  import { readFileSync as readFileSync2 } from "node:fs";
7806
8059
  import { readFile as readFile2, rm } from "node:fs/promises";
7807
8060
  import os from "node:os";
7808
- import path20 from "node:path";
8061
+ import path21 from "node:path";
7809
8062
  import { pathToFileURL } from "node:url";
7810
8063
  import { createIndex } from "pagefind";
7811
8064
  import colors7 from "picocolors";
@@ -7847,7 +8100,7 @@ function throttle(fn) {
7847
8100
  init_joinUrl();
7848
8101
  import { createWriteStream, existsSync } from "node:fs";
7849
8102
  import { mkdir as mkdir4 } from "node:fs/promises";
7850
- import path18 from "node:path";
8103
+ import path19 from "node:path";
7851
8104
  import colors5 from "picocolors";
7852
8105
  import { SitemapStream } from "sitemap";
7853
8106
  async function generateSitemap({
@@ -7861,11 +8114,11 @@ async function generateSitemap({
7861
8114
  return;
7862
8115
  }
7863
8116
  const sitemap = new SitemapStream({ hostname: config2.siteUrl });
7864
- const outputDir = path18.resolve(baseOutputDir, config2.outDir ?? "");
8117
+ const outputDir = path19.resolve(baseOutputDir, config2.outDir ?? "");
7865
8118
  if (!existsSync(outputDir)) {
7866
8119
  await mkdir4(outputDir, { recursive: true });
7867
8120
  }
7868
- const sitemapOutputPath = path18.join(outputDir, "sitemap.xml");
8121
+ const sitemapOutputPath = path19.join(outputDir, "sitemap.xml");
7869
8122
  const writeStream = createWriteStream(sitemapOutputPath);
7870
8123
  sitemap.pipe(writeStream);
7871
8124
  let lastmod;
@@ -7895,14 +8148,14 @@ async function generateSitemap({
7895
8148
 
7896
8149
  // src/vite/prerender/utils.ts
7897
8150
  init_joinUrl();
7898
- var resolveRoutePath = (path28) => {
7899
- const segments = path28.split("/");
8151
+ var resolveRoutePath = (path30) => {
8152
+ const segments = path30.split("/");
7900
8153
  if (segments.some((s) => s.startsWith(":") && !s.endsWith("?"))) {
7901
8154
  return void 0;
7902
8155
  }
7903
8156
  return segments.filter((s) => !s.startsWith(":")).join("/") || void 0;
7904
8157
  };
7905
- var isSkipped = (path28) => path28.includes("*") || /^\d+$/.test(path28);
8158
+ var isSkipped = (path30) => path30.includes("*") || /^\d+$/.test(path30);
7906
8159
  var resolveRoutes = (routes, parentPath = "") => routes.flatMap((route) => {
7907
8160
  if (route.path && isSkipped(route.path)) return [];
7908
8161
  const routePath = route.path ? resolveRoutePath(route.path) : void 0;
@@ -7951,12 +8204,12 @@ var prerender = async ({
7951
8204
  serverConfigFilename,
7952
8205
  writeRedirects = true
7953
8206
  }) => {
7954
- const distDir = path20.join(dir, "dist", basePath);
8207
+ const distDir = path21.join(dir, "dist", basePath);
7955
8208
  const serverConfigPath = pathToFileURL(
7956
- path20.join(distDir, "server", serverConfigFilename)
8209
+ path21.join(distDir, "server", serverConfigFilename)
7957
8210
  ).href;
7958
8211
  const entryServerPath = pathToFileURL(
7959
- path20.join(distDir, "server/entry.server.js")
8212
+ path21.join(distDir, "server/entry.server.js")
7960
8213
  ).href;
7961
8214
  const rawConfig = await import(serverConfigPath).then(
7962
8215
  (m) => m.default
@@ -8068,7 +8321,7 @@ var prerender = async ({
8068
8321
  }
8069
8322
  if (isTTY()) writeLine("");
8070
8323
  const { outputPath } = await pagefindIndex.writeFiles({
8071
- outputPath: path20.join(distDir, "pagefind")
8324
+ outputPath: path21.join(distDir, "pagefind")
8072
8325
  });
8073
8326
  if (outputPath) {
8074
8327
  const duration = (performance.now() - pagefindStart) / 1e3;
@@ -8092,7 +8345,7 @@ var prerender = async ({
8092
8345
  const { generateLlmsTxtFiles: generateLlmsTxtFiles2 } = await Promise.resolve().then(() => (init_llms(), llms_exports));
8093
8346
  const docsConfig = DocsConfigSchema2.parse(config2.docs);
8094
8347
  const llmsConfig = docsConfig.llms ?? {};
8095
- const markdownInfoPath = path20.join(
8348
+ const markdownInfoPath = path21.join(
8096
8349
  dir,
8097
8350
  "node_modules/.zudoku/markdown-info.json"
8098
8351
  );
@@ -8117,7 +8370,7 @@ var prerender = async ({
8117
8370
  await Promise.all(
8118
8371
  markdownFileInfos.map((info) => {
8119
8372
  const outputPath = getMarkdownOutputPath(distDir, info.routePath);
8120
- if (!path20.resolve(outputPath).startsWith(path20.resolve(distDir))) {
8373
+ if (!path21.resolve(outputPath).startsWith(path21.resolve(distDir))) {
8121
8374
  return;
8122
8375
  }
8123
8376
  return rm(outputPath).catch(() => {
@@ -8175,19 +8428,123 @@ var getContainerMemoryLimitMb = () => {
8175
8428
  return void 0;
8176
8429
  };
8177
8430
 
8431
+ // src/vite/protected/build.ts
8432
+ import { mkdir as mkdir5, readdir, readFile as readFile3, rename, rm as rm2 } from "node:fs/promises";
8433
+ import path22 from "node:path";
8434
+ init_joinUrl();
8435
+ var assertProtectedPatternsCovered = (config2) => {
8436
+ const { patterns } = getProtectedSourceMatcher(config2);
8437
+ const unmatched = findUnmatchedProtectedPatterns(patterns);
8438
+ if (unmatched.length === 0) return;
8439
+ throw new Error(
8440
+ `[zudoku] protectedRoutes patterns with no matching content: ${unmatched.map((p) => `"${p}"`).join(", ")}.
8441
+ 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.`
8442
+ );
8443
+ };
8444
+ var findProtectedLeaks = (output) => {
8445
+ const isProtected = (fileName) => fileName.startsWith(`${PROTECTED_CHUNK_DIR}/`);
8446
+ const chunks = output.filter((o) => o.type === "chunk");
8447
+ const byFileName = new Map(chunks.map((c) => [c.fileName, c]));
8448
+ const leaks = [];
8449
+ for (const entry of chunks.filter(
8450
+ (c) => c.isEntry && !isProtected(c.fileName)
8451
+ )) {
8452
+ const visited = /* @__PURE__ */ new Set();
8453
+ const stack = [{ fileName: entry.fileName, path: [entry.fileName] }];
8454
+ while (stack.length > 0) {
8455
+ const { fileName, path: path30 } = stack.pop();
8456
+ if (visited.has(fileName)) continue;
8457
+ visited.add(fileName);
8458
+ for (const imp of byFileName.get(fileName)?.imports ?? []) {
8459
+ const next = [...path30, imp];
8460
+ if (isProtected(imp)) {
8461
+ leaks.push(next.join(" -> "));
8462
+ continue;
8463
+ }
8464
+ stack.push({ fileName: imp, path: next });
8465
+ }
8466
+ }
8467
+ }
8468
+ return leaks;
8469
+ };
8470
+ var assertNoProtectedLeaks = (output) => {
8471
+ const protectedEntries = output.filter((o) => o.type === "chunk").filter(
8472
+ (c) => c.isEntry && c.fileName.startsWith(`${PROTECTED_CHUNK_DIR}/`)
8473
+ ).map((c) => c.fileName);
8474
+ if (protectedEntries.length > 0) {
8475
+ throw new Error(
8476
+ `Protected chunk(s) marked as entries:
8477
+ ${protectedEntries.join("\n ")}
8478
+ Entry chunks are loaded outside the gated import path. Move the entry to a public chunk that dynamically imports the protected one.`
8479
+ );
8480
+ }
8481
+ const leaks = findProtectedLeaks(output);
8482
+ if (leaks.length === 0) return;
8483
+ throw new Error(
8484
+ `Protected chunk(s) statically reachable from public entry:
8485
+ ${leaks.join("\n ")}
8486
+ This eagerly pulls gated content into the public bundle. Check that nothing in non-protected entry code statically imports the protected module.`
8487
+ );
8488
+ };
8489
+ var moveProtectedChunks = async (clientOutDir, serverOutDir) => {
8490
+ const srcDir = path22.join(clientOutDir, PROTECTED_CHUNK_DIR);
8491
+ const files = await readdir(srcDir).catch((err) => {
8492
+ if (err.code === "ENOENT") return null;
8493
+ throw err;
8494
+ });
8495
+ if (!files) return;
8496
+ const destDir = path22.join(serverOutDir, PROTECTED_CHUNK_DIR);
8497
+ await mkdir5(destDir, { recursive: true });
8498
+ await Promise.all(
8499
+ files.map(
8500
+ (file) => rename(path22.join(srcDir, file), path22.join(destDir, file))
8501
+ )
8502
+ );
8503
+ const leftover = await readdir(srcDir).catch(() => []);
8504
+ if (leftover.length > 0) {
8505
+ throw new Error(
8506
+ `moveProtectedChunks left ${leftover.length} file(s) in ${srcDir}: ${leftover.join(", ")}.
8507
+ These would be served publicly. Aborting build.`
8508
+ );
8509
+ }
8510
+ await rm2(srcDir, { recursive: true, force: true });
8511
+ };
8512
+ var assertCloudflareWranglerGatesProtected = async (dir, config2) => {
8513
+ const { enabled } = getProtectedSourceMatcher(config2);
8514
+ if (!enabled) return;
8515
+ const protectedPrefix = `${joinUrl(config2.basePath, PROTECTED_CHUNK_DIR)}/`;
8516
+ const candidates = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
8517
+ for (const name of candidates) {
8518
+ const file = await readFile3(path22.join(dir, name), "utf-8").catch(
8519
+ () => void 0
8520
+ );
8521
+ if (file === void 0) continue;
8522
+ if (file.includes("run_worker_first") && file.includes(protectedPrefix)) {
8523
+ return;
8524
+ }
8525
+ throw new Error(
8526
+ `[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.`
8527
+ );
8528
+ }
8529
+ throw new Error(
8530
+ `[zudoku] No wrangler config found in ${dir} (looked for ${candidates.join(", ")}). Cloudflare adapter requires wrangler config with \`run_worker_first\` covering \`${protectedPrefix}*\`.`
8531
+ );
8532
+ };
8533
+
8178
8534
  // src/vite/build.ts
8179
8535
  var DIST_DIR = "dist";
8180
8536
  async function runBuild(options) {
8181
8537
  const { dir, ssr, adapter = "node" } = options;
8182
- const viteConfig = await getViteConfig(dir, {
8183
- mode: "production",
8184
- command: "build"
8185
- });
8538
+ const viteConfig = await getViteConfig(
8539
+ dir,
8540
+ { mode: "production", command: "build" },
8541
+ { adapter, ssr }
8542
+ );
8186
8543
  const builder2 = await createBuilder(viteConfig);
8187
8544
  invariant(builder2.environments.client, "Client environment is missing");
8188
8545
  invariant(builder2.environments.ssr, "SSR environment is missing");
8189
- const distDir = path21.resolve(path21.join(dir, "dist"));
8190
- await rm2(distDir, { recursive: true, force: true });
8546
+ const distDir = path23.resolve(path23.join(dir, "dist"));
8547
+ await rm3(distDir, { recursive: true, force: true });
8191
8548
  const [clientResult, serverResult] = await Promise.all([
8192
8549
  builder2.build(builder2.environments.client),
8193
8550
  builder2.build(builder2.environments.ssr)
@@ -8226,10 +8583,23 @@ async function runBuild(options) {
8226
8583
  dir,
8227
8584
  adapter,
8228
8585
  serverOutDir,
8229
- html,
8230
- basePath: config2.basePath
8586
+ html
8231
8587
  });
8232
- await rm2(path21.join(clientOutDir, "index.html"), { force: true });
8588
+ assertProtectedPatternsCovered(config2);
8589
+ assertNoProtectedLeaks(clientResult.output);
8590
+ if (adapter !== "cloudflare") {
8591
+ await moveProtectedChunks(clientOutDir, serverOutDir);
8592
+ } else {
8593
+ await assertCloudflareWranglerGatesProtected(dir, config2);
8594
+ }
8595
+ await writeFile6(
8596
+ path23.join(distDir, "package.json"),
8597
+ `${JSON.stringify({ type: "module" }, null, 2)}
8598
+ `,
8599
+ "utf-8"
8600
+ );
8601
+ await writeManifest(distDir, config2);
8602
+ await rm3(path23.join(clientOutDir, "index.html"), { force: true });
8233
8603
  } else {
8234
8604
  await runPrerender({
8235
8605
  dir,
@@ -8260,25 +8630,25 @@ var runPrerender = async (options) => {
8260
8630
  serverConfigFilename,
8261
8631
  writeRedirects: process.env.VERCEL === void 0
8262
8632
  });
8263
- const indexHtml = path21.join(clientOutDir, "index.html");
8633
+ const indexHtml = path23.join(clientOutDir, "index.html");
8264
8634
  if (!workerResults.find((r) => r.outputPath === indexHtml)) {
8265
- await writeFile5(indexHtml, html, "utf-8");
8635
+ await writeFile6(indexHtml, html, "utf-8");
8266
8636
  }
8267
8637
  const statusPages = workerResults.flatMap(
8268
- (r) => /^(400|404|500)\.html$/.test(path21.basename(r.outputPath)) ? r.outputPath : []
8638
+ (r) => /^(400|404|500)\.html$/.test(path23.basename(r.outputPath)) ? r.outputPath : []
8269
8639
  );
8270
8640
  for (const statusPage of statusPages) {
8271
- await rename(
8641
+ await rename2(
8272
8642
  statusPage,
8273
- path21.join(dir, DIST_DIR, path21.basename(statusPage))
8643
+ path23.join(dir, DIST_DIR, path23.basename(statusPage))
8274
8644
  );
8275
8645
  }
8276
- await rm2(serverOutDir, { recursive: true, force: true });
8646
+ await rm3(serverOutDir, { recursive: true, force: true });
8277
8647
  if (process.env.VERCEL) {
8278
- await mkdir5(path21.join(dir, ".vercel/output/static"), { recursive: true });
8279
- await rename(
8280
- path21.join(dir, DIST_DIR),
8281
- path21.join(dir, ".vercel/output/static")
8648
+ await mkdir6(path23.join(dir, ".vercel/output/static"), { recursive: true });
8649
+ await rename2(
8650
+ path23.join(dir, DIST_DIR),
8651
+ path23.join(dir, ".vercel/output/static")
8282
8652
  );
8283
8653
  }
8284
8654
  await writeOutput(dir, {
@@ -8288,8 +8658,8 @@ var runPrerender = async (options) => {
8288
8658
  });
8289
8659
  if (ZuploEnv.isZuplo && issuer) {
8290
8660
  const provider = config2.authentication?.type;
8291
- await writeFile5(
8292
- path21.join(dir, DIST_DIR, ".output/zuplo.json"),
8661
+ await writeFile6(
8662
+ path23.join(dir, DIST_DIR, ".output/zuplo.json"),
8293
8663
  JSON.stringify({ issuer, provider }, null, 2),
8294
8664
  "utf-8"
8295
8665
  );
@@ -8299,33 +8669,63 @@ var runPrerender = async (options) => {
8299
8669
  throw e;
8300
8670
  }
8301
8671
  };
8672
+ var findUserEntry = (dir) => {
8673
+ for (const ext of ["ts", "tsx", "js", "mjs"]) {
8674
+ const candidate = path23.join(dir, `zudoku.server.${ext}`);
8675
+ if (existsSync2(candidate)) return candidate;
8676
+ }
8677
+ };
8302
8678
  var bundleSSREntry = async (options) => {
8303
- const { dir, adapter, serverOutDir, html, basePath } = options;
8304
- const tempEntryPath = path21.join(dir, "__ssr-entry.ts");
8679
+ const { dir, adapter, serverOutDir, html } = options;
8305
8680
  const packageRoot = getZudokuRootDir();
8306
- const templateContent = await readFile3(
8307
- path21.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
8308
- "utf-8"
8309
- );
8310
- const entryContent = templateContent.replace('"__TEMPLATE__"', JSON.stringify(html)).replace(
8311
- '"__BASE_PATH__"',
8312
- basePath ? JSON.stringify(basePath) : "undefined"
8313
- );
8314
- await writeFile5(tempEntryPath, entryContent, "utf-8");
8681
+ const userEntry = findUserEntry(dir);
8682
+ let entryPoint = userEntry;
8683
+ let tempEntryPath;
8684
+ if (!entryPoint) {
8685
+ tempEntryPath = path23.join(dir, "__ssr-entry.ts");
8686
+ const templateContent = await readFile4(
8687
+ path23.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
8688
+ "utf-8"
8689
+ );
8690
+ await writeFile6(tempEntryPath, templateContent, "utf-8");
8691
+ entryPoint = tempEntryPath;
8692
+ }
8693
+ const frameworkPath = path23.join(serverOutDir, "entry.server.js");
8315
8694
  try {
8316
8695
  await esbuild({
8317
- entryPoints: [tempEntryPath],
8696
+ entryPoints: [entryPoint],
8318
8697
  bundle: true,
8319
- platform: adapter === "node" ? "node" : "neutral",
8698
+ platform: ["node", "lambda"].includes(adapter) ? "node" : "neutral",
8320
8699
  target: "es2022",
8321
8700
  format: "esm",
8322
- outfile: path21.join(serverOutDir, "entry.js"),
8323
- external: ["./entry.server.js", "./zudoku.config.js"],
8324
- nodePaths: [path21.join(packageRoot, "node_modules")],
8325
- banner: { js: "// Bundled SSR entry" }
8701
+ outfile: path23.join(serverOutDir, "entry.js"),
8702
+ external: ["./zudoku.config.js"],
8703
+ nodePaths: [path23.join(packageRoot, "node_modules")],
8704
+ banner: { js: "// Bundled SSR entry" },
8705
+ define: {
8706
+ __ZUDOKU_TEMPLATE__: JSON.stringify(html)
8707
+ },
8708
+ plugins: [
8709
+ {
8710
+ name: "zudoku-ssr-entry",
8711
+ setup(build2) {
8712
+ build2.onResolve({ filter: /^zudoku\/server$/ }, () => ({
8713
+ path: frameworkPath
8714
+ }));
8715
+ }
8716
+ }
8717
+ ]
8326
8718
  });
8719
+ await Promise.all([
8720
+ rm3(frameworkPath, { force: true }),
8721
+ rm3(`${frameworkPath}.map`, { force: true }),
8722
+ rm3(path23.join(serverOutDir, "assets"), {
8723
+ recursive: true,
8724
+ force: true
8725
+ })
8726
+ ]);
8327
8727
  } finally {
8328
- await rm2(tempEntryPath, { force: true });
8728
+ if (tempEntryPath) await rm3(tempEntryPath, { force: true });
8329
8729
  }
8330
8730
  };
8331
8731
 
@@ -8355,11 +8755,11 @@ function printWarningToConsole(message) {
8355
8755
  init_package_json();
8356
8756
 
8357
8757
  // src/cli/preview/handler.ts
8358
- import path22 from "node:path";
8758
+ import path24 from "node:path";
8359
8759
  import { preview as vitePreview } from "vite";
8360
8760
  var DEFAULT_PREVIEW_PORT = 4e3;
8361
8761
  async function preview(argv) {
8362
- const dir = path22.resolve(process.cwd(), argv.dir);
8762
+ const dir = path24.resolve(process.cwd(), argv.dir);
8363
8763
  const viteConfig = await getViteConfig(dir, {
8364
8764
  command: "serve",
8365
8765
  mode: "production",
@@ -8397,7 +8797,7 @@ async function build(argv) {
8397
8797
  printDiagnosticsToConsole(`Starting Zudoku build v${packageJson2.version}`);
8398
8798
  printDiagnosticsToConsole("");
8399
8799
  printDiagnosticsToConsole("");
8400
- const dir = path23.resolve(process.cwd(), argv.dir);
8800
+ const dir = path25.resolve(process.cwd(), argv.dir);
8401
8801
  try {
8402
8802
  await runBuild({
8403
8803
  dir,
@@ -8538,8 +8938,8 @@ var build_default = {
8538
8938
  default: false
8539
8939
  }).option("adapter", {
8540
8940
  type: "string",
8541
- describe: "SSR adapter (node, cloudflare, vercel)",
8542
- choices: ["node", "cloudflare", "vercel"],
8941
+ describe: "SSR adapter (node, cloudflare, vercel, lambda)",
8942
+ choices: ["node", "cloudflare", "vercel", "lambda"],
8543
8943
  default: "node"
8544
8944
  }),
8545
8945
  handler: async (argv) => {
@@ -8551,14 +8951,14 @@ var build_default = {
8551
8951
 
8552
8952
  // src/cli/dev/handler.ts
8553
8953
  init_joinUrl();
8554
- import path26 from "node:path";
8954
+ import path28 from "node:path";
8555
8955
 
8556
8956
  // src/vite/dev-server.ts
8557
8957
  init_logger();
8558
8958
  import fs3 from "node:fs/promises";
8559
8959
  import http from "node:http";
8560
8960
  import https from "node:https";
8561
- import path25 from "node:path";
8961
+ import path27 from "node:path";
8562
8962
  import { createHttpTerminator } from "http-terminator";
8563
8963
  import {
8564
8964
  createServer as createViteServer,
@@ -8587,11 +8987,12 @@ async function findAvailablePort(startPort) {
8587
8987
 
8588
8988
  // src/vite/dev-server.ts
8589
8989
  init_loader();
8990
+ init_joinUrl();
8590
8991
 
8591
8992
  // src/vite/pagefind-dev-index.ts
8592
8993
  init_invariant();
8593
8994
  init_joinUrl();
8594
- import path24 from "node:path";
8995
+ import path26 from "node:path";
8595
8996
  import { createIndex as createIndex2 } from "pagefind";
8596
8997
  import { isRunnableDevEnvironment } from "vite";
8597
8998
  async function* buildPagefindDevIndex(vite, config2) {
@@ -8644,7 +9045,7 @@ async function* buildPagefindDevIndex(vite, config2) {
8644
9045
  path: urlPath
8645
9046
  };
8646
9047
  }
8647
- const outputPath = path24.join(vite.config.publicDir, "pagefind");
9048
+ const outputPath = path26.join(vite.config.publicDir, "pagefind");
8648
9049
  await pagefindIndex.writeFiles({ outputPath });
8649
9050
  yield { type: "complete", success: true, indexed };
8650
9051
  }
@@ -8664,9 +9065,9 @@ var DevServer = class {
8664
9065
  this.protocol = "https";
8665
9066
  const { dir } = this.#options;
8666
9067
  const [key, cert, ca] = await Promise.all([
8667
- fs3.readFile(path25.resolve(dir, config2.https.key)),
8668
- fs3.readFile(path25.resolve(dir, config2.https.cert)),
8669
- config2.https.ca ? fs3.readFile(path25.resolve(dir, config2.https.ca)) : void 0
9068
+ fs3.readFile(path27.resolve(dir, config2.https.key)),
9069
+ fs3.readFile(path27.resolve(dir, config2.https.cert)),
9070
+ config2.https.ca ? fs3.readFile(path27.resolve(dir, config2.https.ca)) : void 0
8670
9071
  ]);
8671
9072
  return https.createServer({ key, cert, ca });
8672
9073
  }
@@ -8692,7 +9093,7 @@ var DevServer = class {
8692
9093
  // built-in transform middleware which would treat the path as a static asset.
8693
9094
  name: "zudoku:entry-client",
8694
9095
  configureServer(server2) {
8695
- const entryPath = path25.posix.join(
9096
+ const entryPath = path27.posix.join(
8696
9097
  server2.config.base,
8697
9098
  "/__z/entry.client.tsx"
8698
9099
  );
@@ -8723,6 +9124,46 @@ var DevServer = class {
8723
9124
  next();
8724
9125
  });
8725
9126
  vite.middlewares.use(graphql.graphqlEndpoint, graphql);
9127
+ const sessionEndpoint = joinUrl(config2.basePath, "/__z/auth/session");
9128
+ vite.middlewares.use(sessionEndpoint, async (req, res) => {
9129
+ if (req.method !== "POST" && req.method !== "DELETE") {
9130
+ res.writeHead(405, { Allow: "POST, DELETE" });
9131
+ res.end();
9132
+ return;
9133
+ }
9134
+ const ssrEnvironment = vite.environments.ssr;
9135
+ if (!isRunnableDevEnvironment2(ssrEnvironment)) {
9136
+ res.writeHead(500);
9137
+ res.end("SSR environment not available");
9138
+ return;
9139
+ }
9140
+ const entryServer = await ssrEnvironment.runner.import(
9141
+ getAppServerEntryPath()
9142
+ );
9143
+ const url = `${this.protocol}://${req.headers.host}${req.originalUrl ?? req.url}`;
9144
+ const body = req.method === "POST" ? await new Promise((resolve) => {
9145
+ let data = "";
9146
+ req.on("data", (chunk) => data += chunk);
9147
+ req.on("end", () => resolve(data));
9148
+ }) : void 0;
9149
+ const request = new Request(url, {
9150
+ method: req.method,
9151
+ headers: req.headers,
9152
+ body
9153
+ });
9154
+ const app = entryServer.createServer({ template: "" });
9155
+ const response = await app.fetch(request);
9156
+ for (const [name, value] of response.headers) {
9157
+ if (name.toLowerCase() === "set-cookie") continue;
9158
+ res.setHeader(name, value);
9159
+ }
9160
+ for (const cookie of response.headers.getSetCookie()) {
9161
+ res.appendHeader("Set-Cookie", cookie);
9162
+ }
9163
+ res.writeHead(response.status);
9164
+ const text = await response.text();
9165
+ res.end(text);
9166
+ });
8726
9167
  vite.middlewares.use("/__z/pagefind-reindex", async (_req, res) => {
8727
9168
  res.writeHead(200, {
8728
9169
  "Content-Type": "text/event-stream",
@@ -8765,13 +9206,13 @@ var DevServer = class {
8765
9206
  `Server-side rendering ${this.#options.ssr ? "enabled" : "disabled"}`
8766
9207
  );
8767
9208
  if (config2.search?.type === "pagefind") {
8768
- const pagefindPath = path25.join(
9209
+ const pagefindPath = path27.join(
8769
9210
  vite.config.publicDir,
8770
9211
  "pagefind/pagefind.js"
8771
9212
  );
8772
9213
  const exists = await fs3.stat(pagefindPath).catch(() => false);
8773
9214
  if (!exists) {
8774
- await fs3.mkdir(path25.dirname(pagefindPath), { recursive: true });
9215
+ await fs3.mkdir(path27.dirname(pagefindPath), { recursive: true });
8775
9216
  await fs3.writeFile(pagefindPath, 'throw new Error("NOT_BUILT_YET");');
8776
9217
  }
8777
9218
  }
@@ -8813,14 +9254,15 @@ var DevServer = class {
8813
9254
  duplex: hasBody ? "half" : void 0
8814
9255
  }
8815
9256
  );
8816
- const response = await entryServer.handleRequest({
8817
- template,
8818
- request,
8819
- routes: entryServer.getRoutesByConfig(currentConfig),
8820
- basePath: currentConfig.basePath
9257
+ const app = entryServer.createServer({ template });
9258
+ const response = await app.fetch(request);
9259
+ response.headers.forEach((value, key) => {
9260
+ if (key.toLowerCase() !== "set-cookie") {
9261
+ res.setHeader(key, value);
9262
+ }
8821
9263
  });
8822
- for (const [key, value] of response.headers) {
8823
- res.appendHeader(key, value);
9264
+ for (const cookie of response.headers.getSetCookie()) {
9265
+ res.appendHeader("Set-Cookie", cookie);
8824
9266
  }
8825
9267
  res.writeHead(response.status);
8826
9268
  for await (const chunk of response.body ?? []) {
@@ -8869,7 +9311,7 @@ init_package_json();
8869
9311
  async function dev(argv) {
8870
9312
  const packageJson2 = getZudokuPackageJson();
8871
9313
  process.env.NODE_ENV = "development";
8872
- const dir = path26.resolve(process.cwd(), argv.dir);
9314
+ const dir = path28.resolve(process.cwd(), argv.dir);
8873
9315
  const server = new DevServer({
8874
9316
  dir,
8875
9317
  argPort: argv.port,
@@ -8974,8 +9416,8 @@ var previewCommand = {
8974
9416
  var preview_default = previewCommand;
8975
9417
 
8976
9418
  // src/cli/common/outdated.ts
8977
- import { existsSync as existsSync2, mkdirSync } from "node:fs";
8978
- import { readFile as readFile4, writeFile as writeFile6 } from "node:fs/promises";
9419
+ import { existsSync as existsSync3, mkdirSync } from "node:fs";
9420
+ import { readFile as readFile5, writeFile as writeFile7 } from "node:fs/promises";
8979
9421
  import { join } from "node:path";
8980
9422
  import colors10 from "picocolors";
8981
9423
  import { gt } from "semver";
@@ -9025,12 +9467,12 @@ function box(message, {
9025
9467
 
9026
9468
  // src/cli/common/xdg/lib.ts
9027
9469
  import { homedir } from "node:os";
9028
- import path27 from "node:path";
9470
+ import path29 from "node:path";
9029
9471
  function defineDirectoryWithFallback(xdgName, fallback) {
9030
9472
  if (process.env[xdgName]) {
9031
9473
  return process.env[xdgName];
9032
9474
  } else {
9033
- return path27.join(homedir(), fallback);
9475
+ return path29.join(homedir(), fallback);
9034
9476
  }
9035
9477
  }
9036
9478
  var XDG_CONFIG_HOME = defineDirectoryWithFallback(
@@ -9045,15 +9487,15 @@ var XDG_STATE_HOME = defineDirectoryWithFallback(
9045
9487
  "XDG_DATA_HOME",
9046
9488
  ".local/state"
9047
9489
  );
9048
- var ZUDOKU_XDG_CONFIG_HOME = path27.join(
9490
+ var ZUDOKU_XDG_CONFIG_HOME = path29.join(
9049
9491
  XDG_CONFIG_HOME,
9050
9492
  CLI_XDG_FOLDER_NAME
9051
9493
  );
9052
- var ZUDOKU_XDG_DATA_HOME = path27.join(
9494
+ var ZUDOKU_XDG_DATA_HOME = path29.join(
9053
9495
  XDG_DATA_HOME,
9054
9496
  CLI_XDG_FOLDER_NAME
9055
9497
  );
9056
- var ZUDOKU_XDG_STATE_HOME = path27.join(
9498
+ var ZUDOKU_XDG_STATE_HOME = path29.join(
9057
9499
  XDG_STATE_HOME,
9058
9500
  CLI_XDG_FOLDER_NAME
9059
9501
  );
@@ -9095,14 +9537,14 @@ async function getLatestVersion() {
9095
9537
  return void 0;
9096
9538
  }
9097
9539
  async function getVersionCheckInfo() {
9098
- if (!existsSync2(ZUDOKU_XDG_STATE_HOME)) {
9540
+ if (!existsSync3(ZUDOKU_XDG_STATE_HOME)) {
9099
9541
  mkdirSync(ZUDOKU_XDG_STATE_HOME, { recursive: true });
9100
9542
  }
9101
9543
  const versionCheckPath = join(ZUDOKU_XDG_STATE_HOME, VERSION_CHECK_FILE);
9102
9544
  let versionCheckInfo;
9103
- if (existsSync2(versionCheckPath)) {
9545
+ if (existsSync3(versionCheckPath)) {
9104
9546
  try {
9105
- versionCheckInfo = await readFile4(versionCheckPath, "utf-8").then(
9547
+ versionCheckInfo = await readFile5(versionCheckPath, "utf-8").then(
9106
9548
  JSON.parse
9107
9549
  );
9108
9550
  } catch {
@@ -9127,7 +9569,7 @@ async function getVersionCheckInfo() {
9127
9569
  lastCheck: Date.now(),
9128
9570
  latestVersion
9129
9571
  };
9130
- await writeFile6(
9572
+ await writeFile7(
9131
9573
  versionCheckPath,
9132
9574
  JSON.stringify(versionCheckInfo),
9133
9575
  "utf-8"