polen 0.10.0-next.3 → 0.10.0-next.4

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 (268) hide show
  1. package/README.md +49 -376
  2. package/build/api/api.d.ts +1 -0
  3. package/build/api/api.d.ts.map +1 -1
  4. package/build/api/api.js +1 -0
  5. package/build/api/api.js.map +1 -1
  6. package/build/api/static/index.d.ts +2 -0
  7. package/build/api/static/index.d.ts.map +1 -0
  8. package/build/api/static/index.js +2 -0
  9. package/build/api/static/index.js.map +1 -0
  10. package/build/api/static/manifest.d.ts +18 -0
  11. package/build/api/static/manifest.d.ts.map +1 -0
  12. package/build/api/static/manifest.js +13 -0
  13. package/build/api/static/manifest.js.map +1 -0
  14. package/build/api/static/rebase.d.ts +14 -0
  15. package/build/api/static/rebase.d.ts.map +1 -0
  16. package/build/api/static/rebase.js +110 -0
  17. package/build/api/static/rebase.js.map +1 -0
  18. package/build/api/static/static.d.ts +3 -0
  19. package/build/api/static/static.d.ts.map +1 -0
  20. package/build/api/static/static.js +3 -0
  21. package/build/api/static/static.js.map +1 -0
  22. package/build/api/vite/plugins/build.d.ts.map +1 -1
  23. package/build/api/vite/plugins/build.js +22 -1
  24. package/build/api/vite/plugins/build.js.map +1 -1
  25. package/build/cli/commands/static/$default.d.ts +3 -0
  26. package/build/cli/commands/static/$default.d.ts.map +1 -0
  27. package/build/cli/commands/static/$default.js +38 -0
  28. package/build/cli/commands/static/$default.js.map +1 -0
  29. package/build/cli/commands/static/rebase.d.ts +2 -0
  30. package/build/cli/commands/static/rebase.d.ts.map +1 -0
  31. package/build/cli/commands/static/rebase.js +26 -0
  32. package/build/cli/commands/static/rebase.js.map +1 -0
  33. package/build/cli/commands/static.d.ts +3 -0
  34. package/build/cli/commands/static.d.ts.map +1 -0
  35. package/build/cli/commands/static.js +5 -0
  36. package/build/cli/commands/static.js.map +1 -0
  37. package/build/lib/demos/builder.d.ts +83 -0
  38. package/build/lib/demos/builder.d.ts.map +1 -0
  39. package/build/lib/demos/builder.js +237 -0
  40. package/build/lib/demos/builder.js.map +1 -0
  41. package/build/lib/demos/config-schema.d.ts +243 -0
  42. package/build/lib/demos/config-schema.d.ts.map +1 -0
  43. package/build/lib/demos/config-schema.js +52 -0
  44. package/build/lib/demos/config-schema.js.map +1 -0
  45. package/build/lib/demos/config.d.ts +40 -0
  46. package/build/lib/demos/config.d.ts.map +1 -0
  47. package/build/lib/demos/config.js +180 -0
  48. package/build/lib/demos/config.js.map +1 -0
  49. package/build/lib/demos/index.d.ts +9 -0
  50. package/build/lib/demos/index.d.ts.map +1 -0
  51. package/build/lib/demos/index.js +8 -0
  52. package/build/lib/demos/index.js.map +1 -0
  53. package/build/lib/demos/ui/components.d.ts +33 -0
  54. package/build/lib/demos/ui/components.d.ts.map +1 -0
  55. package/build/lib/demos/ui/components.js +699 -0
  56. package/build/lib/demos/ui/components.js.map +1 -0
  57. package/build/lib/demos/ui/data-collector.d.ts +88 -0
  58. package/build/lib/demos/ui/data-collector.d.ts.map +1 -0
  59. package/build/lib/demos/ui/data-collector.js +174 -0
  60. package/build/lib/demos/ui/data-collector.js.map +1 -0
  61. package/build/lib/demos/ui/landing-page-cli.d.ts +3 -0
  62. package/build/lib/demos/ui/landing-page-cli.d.ts.map +1 -0
  63. package/build/lib/demos/ui/landing-page-cli.js +21 -0
  64. package/build/lib/demos/ui/landing-page-cli.js.map +1 -0
  65. package/build/lib/demos/ui/landing-page.d.ts +32 -0
  66. package/build/lib/demos/ui/landing-page.d.ts.map +1 -0
  67. package/build/lib/demos/ui/landing-page.js +83 -0
  68. package/build/lib/demos/ui/landing-page.js.map +1 -0
  69. package/build/lib/demos/ui/page-renderer.d.ts +26 -0
  70. package/build/lib/demos/ui/page-renderer.d.ts.map +1 -0
  71. package/build/lib/demos/ui/page-renderer.js +104 -0
  72. package/build/lib/demos/ui/page-renderer.js.map +1 -0
  73. package/build/lib/demos/utils.d.ts +14 -0
  74. package/build/lib/demos/utils.d.ts.map +1 -0
  75. package/build/lib/demos/utils.js +37 -0
  76. package/build/lib/demos/utils.js.map +1 -0
  77. package/build/lib/deployment/$$.d.ts +3 -0
  78. package/build/lib/deployment/$$.d.ts.map +1 -0
  79. package/build/lib/deployment/$$.js +3 -0
  80. package/build/lib/deployment/$$.js.map +1 -0
  81. package/build/lib/deployment/$.d.ts +2 -0
  82. package/build/lib/deployment/$.d.ts.map +1 -0
  83. package/build/lib/deployment/$.js +2 -0
  84. package/build/lib/deployment/$.js.map +1 -0
  85. package/build/lib/deployment/metadata.d.ts +32 -0
  86. package/build/lib/deployment/metadata.d.ts.map +1 -0
  87. package/build/lib/deployment/metadata.js +37 -0
  88. package/build/lib/deployment/metadata.js.map +1 -0
  89. package/build/lib/deployment/path-manager.d.ts +41 -0
  90. package/build/lib/deployment/path-manager.d.ts.map +1 -0
  91. package/build/lib/deployment/path-manager.js +157 -0
  92. package/build/lib/deployment/path-manager.js.map +1 -0
  93. package/build/lib/github-actions/git-controller.d.ts +50 -0
  94. package/build/lib/github-actions/git-controller.d.ts.map +1 -0
  95. package/build/lib/github-actions/git-controller.js +90 -0
  96. package/build/lib/github-actions/git-controller.js.map +1 -0
  97. package/build/lib/github-actions/github-actions.d.ts +7 -0
  98. package/build/lib/github-actions/github-actions.d.ts.map +1 -0
  99. package/build/lib/github-actions/github-actions.js +7 -0
  100. package/build/lib/github-actions/github-actions.js.map +1 -0
  101. package/build/lib/github-actions/index.d.ts +2 -0
  102. package/build/lib/github-actions/index.d.ts.map +1 -0
  103. package/build/lib/github-actions/index.js +2 -0
  104. package/build/lib/github-actions/index.js.map +1 -0
  105. package/build/lib/github-actions/lib/get-pr-deployments.d.ts +12 -0
  106. package/build/lib/github-actions/lib/get-pr-deployments.d.ts.map +1 -0
  107. package/build/lib/github-actions/lib/get-pr-deployments.js +51 -0
  108. package/build/lib/github-actions/lib/get-pr-deployments.js.map +1 -0
  109. package/build/lib/github-actions/pr-controller.d.ts +39 -0
  110. package/build/lib/github-actions/pr-controller.d.ts.map +1 -0
  111. package/build/lib/github-actions/pr-controller.js +122 -0
  112. package/build/lib/github-actions/pr-controller.js.map +1 -0
  113. package/build/lib/github-actions/run-step-cli.d.ts +9 -0
  114. package/build/lib/github-actions/run-step-cli.d.ts.map +1 -0
  115. package/build/lib/github-actions/run-step-cli.js +71 -0
  116. package/build/lib/github-actions/run-step-cli.js.map +1 -0
  117. package/build/lib/github-actions/runner.d.ts +17 -0
  118. package/build/lib/github-actions/runner.d.ts.map +1 -0
  119. package/build/lib/github-actions/runner.js +195 -0
  120. package/build/lib/github-actions/runner.js.map +1 -0
  121. package/build/lib/github-actions/schemas/context.d.ts +933 -0
  122. package/build/lib/github-actions/schemas/context.d.ts.map +1 -0
  123. package/build/lib/github-actions/schemas/context.js +407 -0
  124. package/build/lib/github-actions/schemas/context.js.map +1 -0
  125. package/build/lib/github-actions/schemas/index.d.ts +5 -0
  126. package/build/lib/github-actions/schemas/index.d.ts.map +1 -0
  127. package/build/lib/github-actions/schemas/index.js +5 -0
  128. package/build/lib/github-actions/schemas/index.js.map +1 -0
  129. package/build/lib/github-actions/search-module.d.ts +38 -0
  130. package/build/lib/github-actions/search-module.d.ts.map +1 -0
  131. package/build/lib/github-actions/search-module.js +40 -0
  132. package/build/lib/github-actions/search-module.js.map +1 -0
  133. package/build/lib/github-actions/step.d.ts +163 -0
  134. package/build/lib/github-actions/step.d.ts.map +1 -0
  135. package/build/lib/github-actions/step.js +121 -0
  136. package/build/lib/github-actions/step.js.map +1 -0
  137. package/build/lib/helpers.d.ts.map +1 -1
  138. package/build/lib/helpers.js +5 -3
  139. package/build/lib/helpers.js.map +1 -1
  140. package/build/lib/kit-temp.d.ts +54 -0
  141. package/build/lib/kit-temp.d.ts.map +1 -1
  142. package/build/lib/kit-temp.js +80 -14
  143. package/build/lib/kit-temp.js.map +1 -1
  144. package/build/lib/kit-temp.test-d.d.ts +2 -0
  145. package/build/lib/kit-temp.test-d.d.ts.map +1 -0
  146. package/build/lib/kit-temp.test-d.js +75 -0
  147. package/build/lib/kit-temp.test-d.js.map +1 -0
  148. package/build/lib/mask/$$.d.ts +3 -0
  149. package/build/lib/mask/$$.d.ts.map +1 -0
  150. package/build/lib/mask/$$.js +3 -0
  151. package/build/lib/mask/$$.js.map +1 -0
  152. package/build/lib/mask/$.d.ts +2 -0
  153. package/build/lib/mask/$.d.ts.map +1 -0
  154. package/build/lib/mask/$.js +2 -0
  155. package/build/lib/mask/$.js.map +1 -0
  156. package/build/lib/mask/apply.d.ts +86 -0
  157. package/build/lib/mask/apply.d.ts.map +1 -0
  158. package/build/lib/mask/apply.js +86 -0
  159. package/build/lib/mask/apply.js.map +1 -0
  160. package/build/lib/mask/mask.d.ts +124 -0
  161. package/build/lib/mask/mask.d.ts.map +1 -0
  162. package/build/lib/mask/mask.js +137 -0
  163. package/build/lib/mask/mask.js.map +1 -0
  164. package/build/lib/mask/mask.test-d.d.ts +2 -0
  165. package/build/lib/mask/mask.test-d.d.ts.map +1 -0
  166. package/build/lib/mask/mask.test-d.js +102 -0
  167. package/build/lib/mask/mask.test-d.js.map +1 -0
  168. package/build/lib/task/$$.d.ts +3 -0
  169. package/build/lib/task/$$.d.ts.map +1 -0
  170. package/build/lib/task/$$.js +3 -0
  171. package/build/lib/task/$$.js.map +1 -0
  172. package/build/lib/task/$.d.ts +2 -0
  173. package/build/lib/task/$.d.ts.map +1 -0
  174. package/build/lib/task/$.js +2 -0
  175. package/build/lib/task/$.js.map +1 -0
  176. package/build/lib/task/report.d.ts +28 -0
  177. package/build/lib/task/report.d.ts.map +1 -0
  178. package/build/lib/task/report.js +33 -0
  179. package/build/lib/task/report.js.map +1 -0
  180. package/build/lib/task/task.d.ts +44 -0
  181. package/build/lib/task/task.d.ts.map +1 -0
  182. package/build/lib/task/task.js +63 -0
  183. package/build/lib/task/task.js.map +1 -0
  184. package/build/lib/version-history/index.d.ts +3 -0
  185. package/build/lib/version-history/index.d.ts.map +1 -0
  186. package/build/lib/version-history/index.js +2 -0
  187. package/build/lib/version-history/index.js.map +1 -0
  188. package/build/lib/version-history/types.d.ts +64 -0
  189. package/build/lib/version-history/types.d.ts.map +1 -0
  190. package/build/lib/version-history/types.js +5 -0
  191. package/build/lib/version-history/types.js.map +1 -0
  192. package/build/lib/version-history/version-history.d.ts +85 -0
  193. package/build/lib/version-history/version-history.d.ts.map +1 -0
  194. package/build/lib/version-history/version-history.js +248 -0
  195. package/build/lib/version-history/version-history.js.map +1 -0
  196. package/build/sandbox.d.ts +2 -0
  197. package/build/sandbox.d.ts.map +1 -0
  198. package/build/sandbox.js +3 -0
  199. package/build/sandbox.js.map +1 -0
  200. package/build/template/components/Link.jsx +1 -1
  201. package/package.json +16 -9
  202. package/src/api/api.ts +1 -0
  203. package/src/api/singletons/markdown/markdown.test.ts +1 -1
  204. package/src/api/static/index.ts +1 -0
  205. package/src/api/static/manifest.test.ts +106 -0
  206. package/src/api/static/manifest.ts +16 -0
  207. package/src/api/static/rebase.test.ts +229 -0
  208. package/src/api/static/rebase.ts +140 -0
  209. package/src/api/static/static.ts +2 -0
  210. package/src/api/utils/asset-url/asset-url.test.ts +4 -4
  211. package/src/api/vite/plugins/build.ts +25 -1
  212. package/src/api/vite/plugins/core.ts +1 -1
  213. package/src/cli/commands/static/$default.ts +43 -0
  214. package/src/cli/commands/static/rebase.ts +37 -0
  215. package/src/cli/commands/static.ts +6 -0
  216. package/src/lib/demos/builder.ts +298 -0
  217. package/src/lib/demos/config-schema.ts +56 -0
  218. package/src/lib/demos/config.test.ts +193 -0
  219. package/src/lib/demos/config.ts +205 -0
  220. package/src/lib/demos/index.ts +9 -0
  221. package/src/lib/demos/ui/components.ts +739 -0
  222. package/src/lib/demos/ui/data-collector.ts +246 -0
  223. package/src/lib/demos/ui/landing-page-cli.ts +23 -0
  224. package/src/lib/demos/ui/landing-page.ts +126 -0
  225. package/src/lib/demos/ui/page-renderer.ts +124 -0
  226. package/src/lib/demos/utils.ts +43 -0
  227. package/src/lib/deployment/$$.ts +2 -0
  228. package/src/lib/deployment/$.test.ts +53 -0
  229. package/src/lib/deployment/$.ts +1 -0
  230. package/src/lib/deployment/metadata.ts +40 -0
  231. package/src/lib/deployment/path-manager.ts +186 -0
  232. package/src/lib/github-actions/git-controller.ts +151 -0
  233. package/src/lib/github-actions/github-actions.ts +6 -0
  234. package/src/lib/github-actions/index.ts +1 -0
  235. package/src/lib/github-actions/lib/get-pr-deployments.ts +76 -0
  236. package/src/lib/github-actions/pr-controller.test.ts +172 -0
  237. package/src/lib/github-actions/pr-controller.ts +183 -0
  238. package/src/lib/github-actions/run-step-cli.ts +84 -0
  239. package/src/lib/github-actions/runner.test.ts +192 -0
  240. package/src/lib/github-actions/runner.ts +226 -0
  241. package/src/lib/github-actions/schemas/context.ts +424 -0
  242. package/src/lib/github-actions/schemas/index.ts +5 -0
  243. package/src/lib/github-actions/search-module.test.ts +110 -0
  244. package/src/lib/github-actions/search-module.ts +76 -0
  245. package/src/lib/github-actions/step.test.ts +149 -0
  246. package/src/lib/github-actions/step.ts +232 -0
  247. package/src/lib/helpers.ts +4 -3
  248. package/src/lib/kit-temp.test-d.ts +115 -0
  249. package/src/lib/kit-temp.test.ts +127 -0
  250. package/src/lib/kit-temp.ts +126 -14
  251. package/src/lib/mask/$$.ts +2 -0
  252. package/src/lib/mask/$.test.ts +248 -0
  253. package/src/lib/mask/$.ts +1 -0
  254. package/src/lib/mask/apply.ts +134 -0
  255. package/src/lib/mask/mask.test-d.ts +144 -0
  256. package/src/lib/mask/mask.ts +244 -0
  257. package/src/lib/shiki/shiki.test.ts +1 -1
  258. package/src/lib/task/$$.ts +2 -0
  259. package/src/lib/task/$.test.ts +209 -0
  260. package/src/lib/task/$.ts +1 -0
  261. package/src/lib/task/report.ts +72 -0
  262. package/src/lib/task/task.ts +112 -0
  263. package/src/lib/version-history/index.test.ts +188 -0
  264. package/src/lib/version-history/index.ts +4 -0
  265. package/src/lib/version-history/types.ts +68 -0
  266. package/src/lib/version-history/version-history.ts +293 -0
  267. package/src/sandbox.ts +1 -0
  268. package/src/template/components/Link.tsx +1 -1
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Arr, Cli, Path, Str } from '@wollybeard/kit'
4
+ import $ from 'ansis'
5
+ import console from 'console'
6
+
7
+ const commandsDir = import.meta.dirname
8
+ const thisModuleName = Path.parse(import.meta.filename).name
9
+ const cliName = `polen static`
10
+
11
+ const h2 = (str: string) => {
12
+ return $.bold.black.bgWhiteBright(` ${str.toUpperCase()} `)
13
+ }
14
+
15
+ const code = (str: string) => {
16
+ if (!$.isSupported()) return `\`${str}\``
17
+ return $.magenta(str)
18
+ }
19
+
20
+ const s = Str.Builder()
21
+ const allCommands = await Cli.discoverCommandPointers(commandsDir)
22
+ const commands = allCommands.filter(_ => _.name !== thisModuleName)
23
+
24
+ s``
25
+ s`${$.bold.redBright`POLEN 🌺`} ${$.dim(`static commands`)}`
26
+ s`Manage static builds and deployments.`
27
+ s``
28
+ s``
29
+ s`${h2(`commands`)}`
30
+ s``
31
+
32
+ if (Arr.isEmpty(commands)) {
33
+ s`No commands available yet.`
34
+ } else {
35
+ commands.forEach(command => {
36
+ s`${$.dim`$ ${cliName}`} ${$.cyanBright(command.name)}`
37
+ })
38
+ }
39
+ s``
40
+ s`${$.dim`Get help for a command with ${code(`polen static <command> --help`)}`}`
41
+ s``
42
+
43
+ console.log(Str.indent(String(s)))
@@ -0,0 +1,37 @@
1
+ /* eslint-disable */
2
+ // @ts-nocheck
3
+ import { Api } from '#api/index'
4
+ import { Task } from '#lib/task'
5
+ import { Command } from '@molt/command'
6
+ import { Err, Path } from '@wollybeard/kit'
7
+ import { z } from 'zod'
8
+
9
+ const args = Command.create()
10
+ .parameter(
11
+ `source`,
12
+ z.string().describe('Path to the Polen build directory to rebase'),
13
+ )
14
+ .parameter(
15
+ `newBasePath`,
16
+ z.string().describe('New base path for the build (e.g., /new-path/)'),
17
+ )
18
+ .parameter(
19
+ `--target -t`,
20
+ z.string().optional().describe('Target directory for copy mode (if not provided, mutate in place)'),
21
+ )
22
+ .parse()
23
+
24
+ const plan: Api.Static.RebasePlan = args.target
25
+ ? {
26
+ changeMode: 'copy',
27
+ sourcePath: args.source,
28
+ targetPath: args.target,
29
+ newBasePath: args.newBasePath,
30
+ }
31
+ : {
32
+ changeMode: 'mutate',
33
+ sourcePath: args.source,
34
+ newBasePath: args.newBasePath,
35
+ }
36
+
37
+ await Task.runAndExit(Api.Static.rebase, plan)
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Cli, Path } from '@wollybeard/kit'
4
+
5
+ const commandsDir = Path.join(import.meta.dirname, 'static')
6
+ await Cli.dispatch(commandsDir)
@@ -0,0 +1,298 @@
1
+ import { promises as fs } from 'node:fs'
2
+ import * as path from 'node:path'
3
+ import { $ } from 'zx'
4
+ import { getDemoConfig } from './config.ts'
5
+ import { getDemoExamples } from './utils.ts'
6
+
7
+ // Configure zx for CI-friendly operation
8
+ $.verbose = true // Show command output
9
+ $.quiet = false // Don't suppress stdout/stderr
10
+
11
+ export interface BuildOptions {
12
+ basePath?: string
13
+ outputDir?: string
14
+ examples?: string[]
15
+ prNumber?: string
16
+ currentSha?: string
17
+ }
18
+
19
+ export interface DeployOptions {
20
+ sourceDir: string
21
+ targetDir: string
22
+ updateBasePaths?: {
23
+ from: string
24
+ to: string
25
+ }
26
+ }
27
+
28
+ export class DemoBuilder {
29
+ private config = getDemoConfig()
30
+
31
+ /**
32
+ * Build demos for a specific version/tag
33
+ */
34
+ async build(tag: string, options: BuildOptions = {}): Promise<void> {
35
+ const basePath = options.basePath || `/polen/${tag}/`
36
+ const examples = options.examples || await getDemoExamples()
37
+
38
+ for (const example of examples) {
39
+ await this.buildExample(example, `${basePath}${example}/`, options.outputDir)
40
+ }
41
+ }
42
+
43
+ // /**
44
+ // * Build demos landing page
45
+ // */
46
+ // private async buildLandingPage(options: {
47
+ // basePath: string
48
+ // outputDir?: string
49
+ // prNumber?: string
50
+ // currentSha?: string
51
+ // }): Promise<void> {
52
+ // console.log(` Building landing page...`)
53
+
54
+ // // Import and call the build-demos-home module directly
55
+ // const { buildDemosHome } = await import('./ui/landing-page.ts')
56
+
57
+ // await buildDemosHome({
58
+ // basePath: options.basePath || '/',
59
+ // prNumber: options.prNumber,
60
+ // currentSha: options.currentSha,
61
+ // prDeployments: undefined,
62
+ // trunkDeployments: undefined,
63
+ // distTags: undefined,
64
+ // })
65
+ // }
66
+
67
+ /**
68
+ * Build a single example
69
+ */
70
+ private async buildExample(
71
+ example: string,
72
+ basePath: string,
73
+ outputDir?: string,
74
+ ): Promise<void> {
75
+ const exampleDir = path.join('examples', example)
76
+
77
+ // Check if example exists
78
+ try {
79
+ await fs.access(exampleDir)
80
+ } catch {
81
+ console.warn(` ⚠️ Example ${example} not found, skipping`)
82
+ return
83
+ }
84
+
85
+ console.log(` Building ${example}...`)
86
+
87
+ // Build using Polen CLI
88
+ const args = ['build', '--base', basePath]
89
+ if (outputDir) {
90
+ args.push('--outputDir', path.join(outputDir, example))
91
+ }
92
+
93
+ // Build with a timeout to prevent hanging
94
+ try {
95
+ // Use --yes to auto-confirm any npx prompts and set CI env
96
+ await $`cd ${exampleDir} && CI=true npx --yes polen ${args}`.timeout('5m')
97
+ console.log(` ✅ Built ${example}`)
98
+ } catch (error) {
99
+ console.error(` ❌ Failed to build ${example}:`, error)
100
+ throw error
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Deploy built demos to a target directory
106
+ */
107
+ async deploy(options: DeployOptions): Promise<void> {
108
+ const { sourceDir, targetDir, updateBasePaths } = options
109
+
110
+ console.log(`🚀 Deploying demos from ${sourceDir} to ${targetDir}`)
111
+
112
+ // Ensure target directory exists and is clean
113
+ await fs.rm(targetDir, { recursive: true, force: true })
114
+ await fs.mkdir(targetDir, { recursive: true })
115
+
116
+ // Copy demos landing page if it exists
117
+ const landingPageSrc = path.join(sourceDir, 'index.html')
118
+ try {
119
+ await fs.access(landingPageSrc)
120
+ await fs.copyFile(landingPageSrc, path.join(targetDir, 'index.html'))
121
+ console.log(` Copied landing page`)
122
+ } catch {
123
+ // Check dist-demos-home directory as alternative source
124
+ try {
125
+ const altLandingPage = path.join('dist-demos-home', 'index.html')
126
+ await fs.access(altLandingPage)
127
+ await fs.copyFile(altLandingPage, path.join(targetDir, 'index.html'))
128
+ console.log(` Copied landing page from dist-demos-home`)
129
+ } catch {
130
+ console.log(` No landing page found`)
131
+ }
132
+ }
133
+
134
+ // Copy each example's build output
135
+ const examples = await getDemoExamples()
136
+ for (const example of examples) {
137
+ const buildDir = path.join('examples', example, 'build')
138
+ const destDir = path.join(targetDir, example)
139
+
140
+ try {
141
+ await fs.access(buildDir)
142
+ await fs.mkdir(destDir, { recursive: true })
143
+ await this.copyDirectory(buildDir, destDir)
144
+ console.log(` Copied ${example}`)
145
+ } catch {
146
+ console.warn(` ⚠️ No build output for ${example}, skipping`)
147
+ }
148
+ }
149
+
150
+ // Update base paths if needed
151
+ if (updateBasePaths) {
152
+ await this.updateBasePaths(targetDir, updateBasePaths.from, updateBasePaths.to)
153
+ console.log(` Updated base paths from ${updateBasePaths.from} to ${updateBasePaths.to}`)
154
+ }
155
+
156
+ console.log(`✅ Successfully deployed demos to ${targetDir}`)
157
+ }
158
+
159
+ /**
160
+ * Copy directory recursively
161
+ */
162
+ private async copyDirectory(src: string, dest: string): Promise<void> {
163
+ await $`cp -r ${src}/* ${dest}/`
164
+ }
165
+
166
+ /**
167
+ * Update base paths in deployed files
168
+ */
169
+ async updateBasePaths(
170
+ dir: string,
171
+ oldPath: string,
172
+ newPath: string,
173
+ ): Promise<void> {
174
+ // Find all HTML, JS, CSS, and JSON files and update paths
175
+ await $`find ${dir} -type f \\( -name "*.html" -o -name "*.js" -o -name "*.css" -o -name "*.json" \\) | while read file; do
176
+ perl -i -pe "s|${oldPath.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')}|${newPath}|g" "$file"
177
+ done`
178
+ }
179
+
180
+ /**
181
+ * Build and deploy demos for multiple versions
182
+ *
183
+ * This method automates the process of building demos for multiple Polen versions:
184
+ *
185
+ * 1. For each version tag (e.g., "1.2.0", "1.3.0-beta.1"):
186
+ * - Checks out that git tag to get the code at that version
187
+ * - Builds Polen itself (pnpm build)
188
+ * - Builds all demo examples using that version of Polen
189
+ * - Deploys the built demos to deployDir/[version]/
190
+ *
191
+ * 2. Handles version filtering:
192
+ * - Skips versions below the configured minimumPolenVersion
193
+ * - Skips versions where Polen build fails
194
+ *
195
+ * 3. Git branch management:
196
+ * - By default, saves and restores the original branch when done
197
+ * - This prevents leaving the repo in a detached HEAD state
198
+ * - Set restoreOriginalBranch: false to disable this behavior
199
+ *
200
+ * Example:
201
+ * ```
202
+ * // Build demos for versions 1.2.0 and 1.3.0-beta.1
203
+ * const results = await buildMultipleVersions(
204
+ * ['1.2.0', '1.3.0-beta.1'],
205
+ * 'gh-pages'
206
+ * )
207
+ * // Results: { built: ['1.2.0', '1.3.0-beta.1'], skipped: [] }
208
+ * ```
209
+ *
210
+ * Note: This method modifies the git working directory by checking out tags.
211
+ * The original branch is restored by default to prevent issues in CI environments.
212
+ */
213
+ async buildMultipleVersions(
214
+ versions: string[],
215
+ deployDir: string,
216
+ options: { restoreOriginalBranch?: boolean } = { restoreOriginalBranch: true },
217
+ ): Promise<{ built: string[]; skipped: string[] }> {
218
+ const results = {
219
+ built: [] as string[],
220
+ skipped: [] as string[],
221
+ }
222
+
223
+ // Save current branch if needed
224
+ let originalBranch: string | null = null
225
+ if (options.restoreOriginalBranch !== false) {
226
+ originalBranch = (await $`git rev-parse --abbrev-ref HEAD`).stdout.trim()
227
+ }
228
+
229
+ try {
230
+ for (const version of versions) {
231
+ // Skip if below minimum Polen version
232
+ if (!this.config.meetsMinimumPolenVersion(version)) {
233
+ console.log(
234
+ `⚠️ Skipping ${version} - below minimum Polen version ${this.config.minimumPolenVersion}`,
235
+ )
236
+ results.skipped.push(version)
237
+ continue
238
+ }
239
+
240
+ console.log(`\n📦 Building ${version}...`)
241
+
242
+ // Checkout the version
243
+ await $`git checkout ${version}`
244
+
245
+ // Build Polen
246
+ try {
247
+ await $`pnpm build`
248
+ } catch {
249
+ console.log(`⚠️ Could not build Polen for ${version}, skipping`)
250
+ results.skipped.push(version)
251
+ continue
252
+ }
253
+
254
+ // Re-install to link workspace packages
255
+ await $`pnpm install`
256
+
257
+ // Build demos
258
+ await this.build(version)
259
+
260
+ // Determine target directory
261
+ // Stable versions go to version-specific dirs (not 'latest' here)
262
+ const targetDir = path.join(deployDir, version)
263
+
264
+ await this.deploy({
265
+ sourceDir: 'dist-demos',
266
+ targetDir,
267
+ })
268
+
269
+ results.built.push(version)
270
+ }
271
+ } finally {
272
+ // Restore original branch if requested (default behavior)
273
+ if (originalBranch && options.restoreOriginalBranch !== false) {
274
+ await $`git checkout ${originalBranch}`
275
+ }
276
+ }
277
+
278
+ return results
279
+ }
280
+
281
+ /**
282
+ * Build demos for current development cycle
283
+ */
284
+ async buildCurrentCycle(deployDir: string): Promise<void> {
285
+ const { VersionHistory } = await import('../version-history/index.ts')
286
+ const cycle = await VersionHistory.getCurrentDevelopmentCycle()
287
+
288
+ if (!cycle.stable) {
289
+ throw new Error('No stable release found')
290
+ }
291
+
292
+ const versions = cycle.all.map(v => v.git.tag)
293
+ await this.buildMultipleVersions(versions, deployDir)
294
+ }
295
+ }
296
+
297
+ // Export singleton instance
298
+ export const demoBuilder = new DemoBuilder()
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Schema definitions for demo configuration
3
+ */
4
+
5
+ import { z } from 'zod'
6
+
7
+ // Schema for demo configuration
8
+ export const DemoConfigSchema = z.object({
9
+ examples: z.object({
10
+ exclude: z.array(z.string()).default([]),
11
+ order: z.array(z.string()).default([]),
12
+ minimumPolenVersion: z.string().default('0.1.0'),
13
+ }),
14
+ deployment: z.object({
15
+ basePaths: z.record(z.string()).default({}),
16
+ redirects: z.array(z.object({
17
+ from: z.string(),
18
+ to: z.string(),
19
+ })).default([]),
20
+ gc: z.object({
21
+ retainStableVersions: z.boolean().default(true),
22
+ retainCurrentCycle: z.boolean().default(true),
23
+ retainDays: z.number().default(30),
24
+ }).default({}),
25
+ }),
26
+ ui: z.object({
27
+ theme: z.object({
28
+ primaryColor: z.string().default('#000'),
29
+ backgroundColor: z.string().default('#fff'),
30
+ textColor: z.string().default('#000'),
31
+ mutedTextColor: z.string().default('#666'),
32
+ }).default({}),
33
+ branding: z.object({
34
+ title: z.string().default('Polen Demos'),
35
+ description: z.string().default('Interactive GraphQL API documentation'),
36
+ logoUrl: z.string().optional(),
37
+ }).default({}),
38
+ }),
39
+ metadata: z.object({
40
+ disabledDemos: z.record(z.object({
41
+ title: z.string(),
42
+ description: z.string(),
43
+ reason: z.string().optional(),
44
+ })).default({}),
45
+ }),
46
+ })
47
+
48
+ export type DemoConfigData = z.infer<typeof DemoConfigSchema>
49
+
50
+ // Legacy schema for backward compatibility
51
+ export const LegacyDemoConfigSchema = z.object({
52
+ excludeDemos: z.array(z.string()).optional(),
53
+ minimumVersion: z.string().optional(),
54
+ minimumPolenVersion: z.string().optional(),
55
+ order: z.array(z.string()).optional(),
56
+ })
@@ -0,0 +1,193 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
3
+ import { DemoConfig, getDemoConfig, resetDemoConfig } from './config.ts'
4
+
5
+ vi.mock('node:fs', () => ({
6
+ readFileSync: vi.fn(),
7
+ }))
8
+
9
+ // Mock the imported TypeScript config to prevent it from affecting tests
10
+ vi.mock('../../../.github/demo-config.ts', () => ({
11
+ default: null,
12
+ }))
13
+
14
+ describe('DemoConfig', () => {
15
+ const mockReadFileSync = readFileSync as unknown as ReturnType<typeof vi.fn>
16
+
17
+ beforeEach(() => {
18
+ vi.clearAllMocks()
19
+ })
20
+
21
+ afterEach(() => {
22
+ // Reset singleton
23
+ resetDemoConfig()
24
+ })
25
+
26
+ describe('constructor', () => {
27
+ it('loads config from default path', () => {
28
+ mockReadFileSync.mockReturnValue(JSON.stringify({
29
+ excludeDemos: ['test1', 'test2'],
30
+ minimumPolenVersion: '1.0.0',
31
+ order: ['demo1', 'demo2'],
32
+ }))
33
+
34
+ const config = new DemoConfig()
35
+
36
+ expect(mockReadFileSync).toHaveBeenCalledWith('.github/demo-config.json', 'utf-8')
37
+ expect(config.excludeDemos).toEqual(['test1', 'test2'])
38
+ expect(config.minimumPolenVersion).toBe('1.0.0')
39
+ expect(config.order).toEqual(['demo1', 'demo2'])
40
+ })
41
+
42
+ it('loads config from custom path', () => {
43
+ mockReadFileSync.mockReturnValue('{}')
44
+
45
+ const config = new DemoConfig('custom/path')
46
+
47
+ expect(mockReadFileSync).toHaveBeenCalledWith('custom/path.json', 'utf-8')
48
+ })
49
+
50
+ it('uses defaults when config file is missing', () => {
51
+ mockReadFileSync.mockImplementation(() => {
52
+ throw new Error('File not found')
53
+ })
54
+
55
+ const config = new DemoConfig()
56
+
57
+ expect(config.excludeDemos).toEqual([])
58
+ expect(config.minimumPolenVersion).toBe('0.1.0')
59
+ expect(config.order).toEqual([])
60
+ })
61
+
62
+ it('supports legacy minimumVersion field', () => {
63
+ mockReadFileSync.mockReturnValue(JSON.stringify({
64
+ minimumVersion: '0.8.0', // old field name
65
+ }))
66
+
67
+ const config = new DemoConfig()
68
+
69
+ expect(config.minimumPolenVersion).toBe('0.8.0')
70
+ })
71
+
72
+ it('prefers minimumPolenVersion over minimumVersion', () => {
73
+ mockReadFileSync.mockReturnValue(JSON.stringify({
74
+ minimumVersion: '0.8.0',
75
+ minimumPolenVersion: '0.9.0',
76
+ }))
77
+
78
+ const config = new DemoConfig()
79
+
80
+ expect(config.minimumPolenVersion).toBe('0.9.0')
81
+ })
82
+ })
83
+
84
+ describe('meetsMinimumPolenVersion', () => {
85
+ it('returns true when version meets minimum', () => {
86
+ mockReadFileSync.mockReturnValue(JSON.stringify({
87
+ minimumPolenVersion: '1.0.0',
88
+ }))
89
+
90
+ const config = new DemoConfig()
91
+
92
+ expect(config.meetsMinimumPolenVersion('1.0.0')).toBe(true)
93
+ expect(config.meetsMinimumPolenVersion('1.0.1')).toBe(true)
94
+ expect(config.meetsMinimumPolenVersion('2.0.0')).toBe(true)
95
+ })
96
+
97
+ it('returns false when version is below minimum', () => {
98
+ mockReadFileSync.mockReturnValue(JSON.stringify({
99
+ minimumPolenVersion: '1.0.0',
100
+ }))
101
+
102
+ const config = new DemoConfig()
103
+
104
+ expect(config.meetsMinimumPolenVersion('0.9.0')).toBe(false)
105
+ expect(config.meetsMinimumPolenVersion('0.9.9')).toBe(false)
106
+ })
107
+
108
+ it('returns false for invalid versions', () => {
109
+ mockReadFileSync.mockReturnValue(JSON.stringify({
110
+ minimumPolenVersion: '1.0.0',
111
+ }))
112
+
113
+ const config = new DemoConfig()
114
+
115
+ expect(config.meetsMinimumPolenVersion('invalid')).toBe(false)
116
+ expect(config.meetsMinimumPolenVersion('')).toBe(false)
117
+ })
118
+ })
119
+
120
+ describe('isDemoExcluded', () => {
121
+ it('returns true for excluded demos', () => {
122
+ mockReadFileSync.mockReturnValue(JSON.stringify({
123
+ excludeDemos: ['demo1', 'demo2'],
124
+ }))
125
+
126
+ const config = new DemoConfig()
127
+
128
+ expect(config.isDemoExcluded('demo1')).toBe(true)
129
+ expect(config.isDemoExcluded('demo2')).toBe(true)
130
+ })
131
+
132
+ it('returns false for non-excluded demos', () => {
133
+ mockReadFileSync.mockReturnValue(JSON.stringify({
134
+ excludeDemos: ['demo1'],
135
+ }))
136
+
137
+ const config = new DemoConfig()
138
+
139
+ expect(config.isDemoExcluded('demo3')).toBe(false)
140
+ })
141
+ })
142
+
143
+ describe('getOrderedDemos', () => {
144
+ it('orders demos according to config', () => {
145
+ mockReadFileSync.mockReturnValue(JSON.stringify({
146
+ excludeDemos: ['excluded1'],
147
+ order: ['demo3', 'demo1'],
148
+ }))
149
+
150
+ const config = new DemoConfig()
151
+ const availableDemos = ['demo1', 'demo2', 'demo3', 'demo4', 'excluded1']
152
+
153
+ const ordered = config.getOrderedDemos(availableDemos)
154
+
155
+ expect(ordered).toEqual(['demo3', 'demo1', 'demo2', 'demo4'])
156
+ })
157
+
158
+ it('handles demos in order that are not available', () => {
159
+ mockReadFileSync.mockReturnValue(JSON.stringify({
160
+ order: ['demo1', 'nonexistent', 'demo2'],
161
+ }))
162
+
163
+ const config = new DemoConfig()
164
+ const availableDemos = ['demo1', 'demo2', 'demo3']
165
+
166
+ const ordered = config.getOrderedDemos(availableDemos)
167
+
168
+ expect(ordered).toEqual(['demo1', 'demo2', 'demo3'])
169
+ })
170
+ })
171
+
172
+ describe('getDemoConfig singleton', () => {
173
+ it('returns same instance on multiple calls', () => {
174
+ mockReadFileSync.mockReturnValue('{}')
175
+
176
+ const config1 = getDemoConfig()
177
+ const config2 = getDemoConfig()
178
+
179
+ expect(config1).toBe(config2)
180
+ expect(mockReadFileSync).toHaveBeenCalledTimes(1)
181
+ })
182
+
183
+ it('creates new instance when path is provided', () => {
184
+ mockReadFileSync.mockReturnValue('{}')
185
+
186
+ const config1 = getDemoConfig()
187
+ const config2 = getDemoConfig('custom/path.json')
188
+
189
+ expect(config1).not.toBe(config2)
190
+ expect(mockReadFileSync).toHaveBeenCalledTimes(2)
191
+ })
192
+ })
193
+ })