@rm-industries/create-forge 0.4.0-beta.0 → 0.4.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -6,6 +6,7 @@ import { basename, dirname, isAbsolute, join, parse, relative, resolve } from "n
6
6
  import validateNpmPackageName from "validate-npm-package-name";
7
7
  import { tmpdir } from "node:os";
8
8
  import { fileURLToPath } from "node:url";
9
+ import { createHash } from "node:crypto";
9
10
  import { spawn } from "node:child_process";
10
11
  //#region src/prompts.ts
11
12
  const clackPrompts = {
@@ -209,6 +210,16 @@ const runCli = async (args, version, context = {}) => {
209
210
  }
210
211
  };
211
212
  //#endregion
213
+ //#region src/schedule.ts
214
+ const deriveScheduleMinutes = (packageName) => {
215
+ const digest = createHash("sha256").update(packageName).digest();
216
+ const security = 1 + digest.readUInt8(0) % 59;
217
+ return {
218
+ security,
219
+ automation: 1 + (security - 1 + 17 + digest.readUInt8(1) % 29) % 59
220
+ };
221
+ };
222
+ //#endregion
212
223
  //#region src/template-tokens.ts
213
224
  const prefix = "__RM_INDUSTRIES_FORGE_TEMPLATE_V1_";
214
225
  const templateTokens = Object.freeze({
@@ -217,7 +228,9 @@ const templateTokens = Object.freeze({
217
228
  description: `${prefix}DESCRIPTION__`,
218
229
  author: `${prefix}AUTHOR__`,
219
230
  url: `${prefix}URL__`,
220
- repository: `${prefix}REPOSITORY__`
231
+ repository: `${prefix}REPOSITORY__`,
232
+ securityScheduleMinute: `${prefix}SECURITY_SCHEDULE_MINUTE__`,
233
+ automationScheduleMinute: `${prefix}AUTOMATION_SCHEDULE_MINUTE__`
221
234
  });
222
235
  const templateTokenPrefix = prefix;
223
236
  //#endregion
@@ -287,6 +300,10 @@ const replaceToken = (source, token, value, field) => {
287
300
  if (!source.includes(quotedToken)) throw new Error(`Packaged template is missing the ${field} token.`);
288
301
  return source.replaceAll(quotedToken, quoteTypeScriptString(value));
289
302
  };
303
+ const replaceRawToken = (source, token, value, field) => {
304
+ if (!source.includes(token)) throw new Error(`Packaged template is missing the ${field} token.`);
305
+ return source.replaceAll(token, value);
306
+ };
290
307
  const customizeTemplate = async (destination, options) => {
291
308
  const packagePath = join(destination, "package.json");
292
309
  const packageLockPath = join(destination, "package-lock.json");
@@ -311,6 +328,15 @@ const customizeTemplate = async (destination, options) => {
311
328
  siteConfig = replaceToken(siteConfig, templateTokens.repository, options.repository, "repository");
312
329
  if (siteConfig.includes("__RM_INDUSTRIES_FORGE_TEMPLATE_V1_")) throw new Error("Packaged template contains unresolved tokens.");
313
330
  await writeFile(siteConfigPath, siteConfig);
331
+ const scheduleMinutes = deriveScheduleMinutes(options.packageName);
332
+ const securityWorkflowPath = join(destination, ".github", "workflows", "security.yml");
333
+ const automationWorkflowPath = join(destination, ".github", "workflows", "automation.yml");
334
+ let securityWorkflow = await readFile(securityWorkflowPath, "utf8");
335
+ let automationWorkflow = await readFile(automationWorkflowPath, "utf8");
336
+ securityWorkflow = replaceRawToken(securityWorkflow, templateTokens.securityScheduleMinute, String(scheduleMinutes.security), "security schedule minute");
337
+ automationWorkflow = replaceRawToken(automationWorkflow, templateTokens.automationScheduleMinute, String(scheduleMinutes.automation), "automation schedule minute");
338
+ await writeFile(securityWorkflowPath, securityWorkflow);
339
+ await writeFile(automationWorkflowPath, automationWorkflow);
314
340
  };
315
341
  const materializeProject = async (options, context) => {
316
342
  const cwd = context.cwd ?? process.cwd();
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/prompts.ts","../src/options.ts","../src/validation.ts","../src/resolve-options.ts","../src/cli.ts","../src/template-tokens.ts","../src/materialize.ts","../src/process.ts","../src/reporter.ts","../src/index.ts"],"sourcesContent":["import { confirm, isCancel, text } from '@clack/prompts';\n\nexport type TextPrompt = {\n message: string;\n initialValue?: string;\n placeholder?: string;\n validate?: (value: string | undefined) => string | Error | undefined;\n};\n\nexport type ConfirmPrompt = { message: string; initialValue: boolean };\n\nexport type PromptAdapter = {\n text(options: TextPrompt): Promise<string | symbol>;\n confirm(options: ConfirmPrompt): Promise<boolean | symbol>;\n isCancel(value: unknown): boolean;\n};\n\nexport const clackPrompts: PromptAdapter = {\n text: (options) =>\n text({\n message: options.message,\n ...(options.initialValue === undefined ? {} : { initialValue: options.initialValue }),\n ...(options.placeholder === undefined ? {} : { placeholder: options.placeholder }),\n ...(options.validate === undefined ? {} : { validate: options.validate }),\n }),\n confirm,\n isCancel,\n};\n","export type GeneratorOptions = {\n destination: string;\n packageName: string;\n siteName: string;\n description: string;\n author: string;\n url: string;\n repository: string;\n install: boolean;\n git: boolean;\n};\n\nexport type ProvidedOptions = {\n [Key in keyof GeneratorOptions]?: GeneratorOptions[Key] | undefined;\n} & { yes: boolean };\n\nexport const defaultDescription = 'A content-driven website built with Forge';\nexport const defaultUrl = 'https://example.com';\n\nexport const derivePackageName = (destination: string) => {\n return basename(resolve(destination)) || 'forge-site';\n};\n\nexport const humanizePackageName = (packageName: string) => {\n const unscopedName = packageName.split('/').at(-1) ?? packageName;\n return unscopedName\n .split(/[-_\\s]+/u)\n .filter(Boolean)\n .map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)\n .join(' ');\n};\nimport { basename, resolve } from 'node:path';\n","import validateNpmPackageName from 'validate-npm-package-name';\n\nexport class InputError extends Error {}\n\nconst invalid = (option: string, value: string, reason: string): never => {\n throw new InputError(`Invalid ${option} value ${JSON.stringify(value)}: ${reason}`);\n};\n\nexport const validateDestination = (value: string) => {\n const destination = value.trim();\n if (!destination) invalid('destination', value, 'provide a non-empty path');\n if (destination.includes('\\0')) invalid('destination', value, 'paths cannot contain null bytes');\n return destination;\n};\n\nexport const validatePackageName = (value: string) => {\n const packageName = value.trim();\n const result = validateNpmPackageName(packageName);\n if (!result.validForNewPackages) {\n const reason = [...(result.errors ?? []), ...(result.warnings ?? [])].join('; ');\n invalid('--name', value, reason || 'provide a valid npm package name');\n }\n return packageName;\n};\n\nexport const validatePlainText = (option: string, value: string, required = false) => {\n const text = value.trim();\n if (required && !text) invalid(option, value, 'provide non-empty text');\n if (/\\p{Cc}/u.test(text)) invalid(option, value, 'control characters are not allowed');\n return text;\n};\n\nexport const validateUrl = (value: string) => {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return invalid('--url', value, 'provide an absolute HTTP(S) URL');\n }\n if (!['http:', 'https:'].includes(url.protocol)) {\n invalid('--url', value, 'provide an absolute HTTP(S) URL');\n }\n return url.toString();\n};\n\nexport const validateRepository = (value: string) => {\n const repository = value.trim();\n if (!repository) return repository;\n if (!/^[\\w.-]+\\/[\\w.-]+$/u.test(repository)) {\n invalid('--repository', value, 'use the GitHub owner/repository format or leave it empty');\n }\n return repository;\n};\n","import {\n defaultDescription,\n defaultUrl,\n derivePackageName,\n humanizePackageName,\n type GeneratorOptions,\n type ProvidedOptions,\n} from './options';\nimport type { PromptAdapter, TextPrompt } from './prompts';\nimport {\n InputError,\n validateDestination,\n validatePackageName,\n validatePlainText,\n validateRepository,\n validateUrl,\n} from './validation';\n\nexport class CancellationError extends Error {}\n\ntype ResolutionContext = { interactive: boolean; prompts: PromptAdapter };\n\nconst textValidation = (validator: (value: string) => string) => (value: string | undefined) => {\n try {\n validator(value ?? '');\n return undefined;\n } catch (error) {\n return error instanceof Error ? error.message : 'Invalid value';\n }\n};\n\nconst promptText = async (prompts: PromptAdapter, options: TextPrompt) => {\n const value = await prompts.text(options);\n if (prompts.isCancel(value)) throw new CancellationError('Project creation cancelled.');\n return String(value);\n};\n\nconst promptConfirm = async (prompts: PromptAdapter, message: string) => {\n const value = await prompts.confirm({ message, initialValue: true });\n if (prompts.isCancel(value)) throw new CancellationError('Project creation cancelled.');\n return Boolean(value);\n};\n\nconst hasEveryExplicitValue = (options: ProvidedOptions) =>\n options.destination !== undefined &&\n options.packageName !== undefined &&\n options.siteName !== undefined &&\n options.description !== undefined &&\n options.author !== undefined &&\n options.url !== undefined &&\n options.repository !== undefined &&\n options.install !== undefined &&\n options.git !== undefined;\n\nexport const resolveOptions = async (\n provided: ProvidedOptions,\n { interactive, prompts }: ResolutionContext,\n): Promise<GeneratorOptions> => {\n const useDefaults = provided.yes;\n if (!interactive && !useDefaults && !hasEveryExplicitValue(provided)) {\n throw new InputError(\n 'Non-interactive use requires --yes or explicit values for every generator option. Run create-forge --help for details.',\n );\n }\n\n const destination = validateDestination(\n provided.destination ??\n (useDefaults\n ? '.'\n : await promptText(prompts, {\n message: 'Where should we create your project?',\n placeholder: '.',\n initialValue: '.',\n validate: textValidation(validateDestination),\n })),\n );\n const derivedPackageName = derivePackageName(destination);\n const packageName = validatePackageName(\n provided.packageName ??\n (useDefaults\n ? derivedPackageName\n : await promptText(prompts, {\n message: 'What should the npm package be named?',\n initialValue: derivedPackageName,\n validate: textValidation(validatePackageName),\n })),\n );\n const defaultSiteName = humanizePackageName(packageName);\n const siteName = validatePlainText(\n '--site-name',\n provided.siteName ??\n (useDefaults\n ? defaultSiteName\n : await promptText(prompts, {\n message: 'What should the site be called?',\n initialValue: defaultSiteName,\n validate: textValidation((value) => validatePlainText('--site-name', value, true)),\n })),\n true,\n );\n const description = validatePlainText(\n '--description',\n provided.description ??\n (useDefaults\n ? defaultDescription\n : await promptText(prompts, {\n message: 'How would you describe the site?',\n initialValue: defaultDescription,\n validate: textValidation((value) => validatePlainText('--description', value)),\n })),\n );\n const author = validatePlainText(\n '--author',\n provided.author ??\n (useDefaults\n ? ''\n : await promptText(prompts, {\n message: 'Who is the site author?',\n placeholder: 'Optional',\n validate: textValidation((value) => validatePlainText('--author', value)),\n })),\n );\n const url = validateUrl(\n provided.url ??\n (useDefaults\n ? defaultUrl\n : await promptText(prompts, {\n message: 'What is the canonical site URL?',\n initialValue: defaultUrl,\n validate: textValidation(validateUrl),\n })),\n );\n const repository = validateRepository(\n provided.repository ??\n (useDefaults\n ? ''\n : await promptText(prompts, {\n message: 'What is the GitHub repository?',\n placeholder: 'owner/repository (optional)',\n validate: textValidation(validateRepository),\n })),\n );\n const install = provided.install ?? (useDefaults ? true : await promptConfirm(prompts, 'Install dependencies?'));\n const git = provided.git ?? (useDefaults ? true : await promptConfirm(prompts, 'Initialize a Git repository?'));\n\n return { destination, packageName, siteName, description, author, url, repository, install, git };\n};\n","import { Command, CommanderError, Option } from 'commander';\n\nimport type { GeneratorOptions, ProvidedOptions } from './options';\nimport { clackPrompts, type PromptAdapter } from './prompts';\nimport { CancellationError, resolveOptions } from './resolve-options';\n\nexport type CliResult = { output: string; exitCode: number; options?: GeneratorOptions };\nexport type CliContext = { interactive?: boolean; prompts?: PromptAdapter };\n\nconst hasBoth = (args: readonly string[], positive: string, negative: string) =>\n args.includes(positive) && args.includes(negative);\n\nconst parseArguments = (args: readonly string[], version: string) => {\n let output = '';\n const program = new Command()\n .name('create-forge')\n .description('Create a content-driven Forge website')\n .argument('[destination]', 'directory where the project will be created')\n .option('--name <name>', 'npm package name (defaults to the directory basename)')\n .option('--site-name <name>', 'site name')\n .option('--description <text>', 'site description')\n .option('--author <name>', 'site author')\n .option('--url <url>', 'absolute canonical HTTP(S) URL')\n .option('--repository <owner/repository>', 'GitHub repository or an empty value')\n .addOption(new Option('--install', 'install dependencies'))\n .addOption(new Option('--no-install', 'do not install dependencies'))\n .addOption(new Option('--git', 'initialize a Git repository'))\n .addOption(new Option('--no-git', 'do not initialize a Git repository'))\n .option('-y, --yes', 'use documented defaults without prompting', false)\n .version(version, '-v, --version', 'show the installed package version')\n .helpOption('-h, --help', 'show this help message')\n .showHelpAfterError()\n .allowExcessArguments(false)\n .exitOverride()\n .configureOutput({\n writeOut: (value) => {\n output += value;\n },\n writeErr: (value) => {\n output += value;\n },\n });\n\n try {\n program.parse(args, { from: 'user' });\n } catch (error) {\n if (error instanceof CommanderError) return { output, exitCode: error.exitCode } as const;\n throw error;\n }\n\n if (hasBoth(args, '--install', '--no-install')) {\n return { output: 'error: --install cannot be used with --no-install\\n', exitCode: 1 } as const;\n }\n if (hasBoth(args, '--git', '--no-git')) {\n return { output: 'error: --git cannot be used with --no-git\\n', exitCode: 1 } as const;\n }\n\n const raw = program.opts();\n const provided: ProvidedOptions = {\n destination: program.args[0],\n packageName: raw.name,\n siteName: raw.siteName,\n description: raw.description,\n author: raw.author,\n url: raw.url,\n repository: raw.repository,\n install: raw.install,\n git: raw.git,\n yes: raw.yes,\n };\n return { provided } as const;\n};\n\nexport const runCli = async (\n args: readonly string[],\n version: string,\n context: CliContext = {},\n): Promise<CliResult> => {\n const parsed = parseArguments(args, version);\n if (!('provided' in parsed)) return parsed;\n\n try {\n const options = await resolveOptions(parsed.provided, {\n interactive: context.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY),\n prompts: context.prompts ?? clackPrompts,\n });\n return { output: '', exitCode: 0, options };\n } catch (error) {\n if (error instanceof CancellationError) return { output: `${error.message}\\n`, exitCode: 1 };\n if (error instanceof Error) return { output: `error: ${error.message}\\n`, exitCode: 1 };\n throw error;\n }\n};\n","const prefix = '__RM_INDUSTRIES_FORGE_TEMPLATE_V1_';\n\nexport const templateTokens = Object.freeze({\n packageName: `${prefix}PACKAGE_NAME__`,\n siteName: `${prefix}SITE_NAME__`,\n description: `${prefix}DESCRIPTION__`,\n author: `${prefix}AUTHOR__`,\n url: `${prefix}URL__`,\n repository: `${prefix}REPOSITORY__`,\n});\n\nexport const templateTokenPrefix = prefix;\n","import {\n chmod,\n copyFile,\n lstat,\n mkdir,\n mkdtemp,\n readdir,\n readFile,\n rm,\n rmdir,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { dirname, isAbsolute, join, parse, relative, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { GeneratorOptions } from './options';\nimport { templateTokenPrefix, templateTokens } from './template-tokens';\n\nexport class MaterializationError extends Error {}\n\ntype MaterializationContext = {\n templateDirectory: string | URL;\n cwd?: string;\n signal?: AbortSignal;\n confirmOverwrite?: (destination: string) => Promise<boolean>;\n onFileCopied?: (path: string) => void;\n};\n\ntype TemplateFile = { source: string; relativePath: string; mode: number };\n\nconst pathExists = async (path: string) => {\n try {\n await lstat(path);\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw error;\n }\n};\n\nconst getPathStat = async (path: string) => {\n try {\n return await lstat(path);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n};\n\nconst assertNotAborted = (signal?: AbortSignal) => {\n if (signal?.aborted) throw new Error('Project creation was interrupted.');\n};\n\nconst resolveTemplateDirectory = (directory: string | URL) =>\n directory instanceof URL ? fileURLToPath(directory) : resolve(directory);\n\nconst resolveDestination = (destination: string, cwd: string) => {\n const segments = destination.split(/[\\\\/]/u);\n if (segments.includes('..')) {\n throw new MaterializationError(\n `Unsafe destination ${JSON.stringify(destination)}: parent traversal is not allowed.`,\n );\n }\n const resolved = isAbsolute(destination) ? resolve(destination) : resolve(cwd, destination);\n if (resolved === parse(resolved).root) {\n throw new MaterializationError(\n `Unsafe destination ${JSON.stringify(destination)}: a filesystem root is not allowed.`,\n );\n }\n return resolved;\n};\n\nconst listTemplateFiles = async (root: string, directory = root): Promise<TemplateFile[]> => {\n const entries = await readdir(directory, { withFileTypes: true });\n const files: TemplateFile[] = [];\n for (const entry of entries) {\n const source = join(directory, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await listTemplateFiles(root, source)));\n continue;\n }\n if (!entry.isFile()) throw new MaterializationError(`Template entry ${source} is not a regular file.`);\n const relativePath = relative(root, source);\n files.push({\n source,\n relativePath: relativePath === '.gitignore.template' ? '.gitignore' : relativePath,\n mode: (await stat(source)).mode,\n });\n }\n return files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));\n};\n\nconst isDirectoryEmpty = async (directory: string) => (await readdir(directory)).length === 0;\n\nconst ensureDirectory = async (directory: string, created: Set<string>) => {\n const existing = await getPathStat(directory);\n if (existing) {\n if (existing.isSymbolicLink() || !existing.isDirectory()) {\n throw new Error(`Unsafe destination entry ${directory}: expected a regular directory.`);\n }\n return;\n }\n const parent = dirname(directory);\n if (parent !== directory) await ensureDirectory(parent, created);\n await mkdir(directory);\n created.add(directory);\n};\n\nconst quoteTypeScriptString = (value: string) =>\n `'${value\n .replaceAll('\\\\', '\\\\\\\\')\n .replaceAll(\"'\", \"\\\\'\")\n .replaceAll('\\u2028', '\\\\u2028')\n .replaceAll('\\u2029', '\\\\u2029')}'`;\n\nconst replaceToken = (source: string, token: string, value: string, field: string) => {\n const quotedToken = `'${token}'`;\n if (!source.includes(quotedToken)) throw new Error(`Packaged template is missing the ${field} token.`);\n return source.replaceAll(quotedToken, quoteTypeScriptString(value));\n};\n\nconst customizeTemplate = async (destination: string, options: GeneratorOptions) => {\n const packagePath = join(destination, 'package.json');\n const packageLockPath = join(destination, 'package-lock.json');\n const packageMetadata = JSON.parse(await readFile(packagePath, 'utf8')) as Record<string, unknown>;\n const packageLock = JSON.parse(await readFile(packageLockPath, 'utf8')) as {\n name?: string;\n packages?: Record<string, Record<string, unknown>>;\n };\n if (\n packageMetadata.name !== templateTokens.packageName ||\n packageLock.name !== templateTokens.packageName ||\n packageLock.packages?.['']?.name !== templateTokens.packageName\n ) {\n throw new Error('Packaged template contains unexpected package metadata tokens.');\n }\n packageMetadata.name = options.packageName;\n packageMetadata.version = '0.0.0';\n packageMetadata.private = true;\n packageLock.name = options.packageName;\n if (!packageLock.packages?.['']) throw new Error('Packaged template lockfile is missing its root package metadata.');\n packageLock.packages[''].name = options.packageName;\n packageLock.packages[''].version = '0.0.0';\n await writeFile(packagePath, `${JSON.stringify(packageMetadata, undefined, 2)}\\n`);\n await writeFile(packageLockPath, `${JSON.stringify(packageLock, undefined, 2)}\\n`);\n\n const siteConfigPath = join(destination, 'src', 'config', 'site.ts');\n let siteConfig = await readFile(siteConfigPath, 'utf8');\n siteConfig = replaceToken(siteConfig, templateTokens.siteName, options.siteName, 'site name');\n siteConfig = replaceToken(siteConfig, templateTokens.description, options.description, 'description');\n siteConfig = replaceToken(siteConfig, templateTokens.author, options.author, 'author');\n siteConfig = replaceToken(siteConfig, templateTokens.url, options.url, 'URL');\n siteConfig = replaceToken(siteConfig, templateTokens.repository, options.repository, 'repository');\n if (siteConfig.includes(templateTokenPrefix)) throw new Error('Packaged template contains unresolved tokens.');\n await writeFile(siteConfigPath, siteConfig);\n};\n\nexport const materializeProject = async (options: GeneratorOptions, context: MaterializationContext) => {\n const cwd = context.cwd ?? process.cwd();\n const destination = resolveDestination(options.destination, cwd);\n const templateDirectory = resolveTemplateDirectory(context.templateDirectory);\n const destinationExisted = await pathExists(destination);\n if (destinationExisted) {\n const destinationStat = await lstat(destination);\n if (destinationStat.isSymbolicLink() || !destinationStat.isDirectory()) {\n throw new MaterializationError(`Unsafe destination ${destination}: expected a regular directory.`);\n }\n }\n if (destinationExisted && !(await isDirectoryEmpty(destination))) {\n const confirmed = await context.confirmOverwrite?.(destination);\n if (!confirmed) {\n throw new MaterializationError(`Destination ${destination} is not empty. No files were changed.`);\n }\n }\n\n const files = await listTemplateFiles(templateDirectory);\n const backupDirectory = await mkdtemp(join(tmpdir(), 'create-forge-backup-'));\n const createdFiles = new Set<string>();\n const createdDirectories = new Set<string>();\n const backups = new Map<string, string>();\n\n try {\n assertNotAborted(context.signal);\n await ensureDirectory(destination, createdDirectories);\n for (const file of files) {\n assertNotAborted(context.signal);\n const target = join(destination, file.relativePath);\n const targetRelative = relative(destination, target);\n if (targetRelative.startsWith('..') || isAbsolute(targetRelative))\n throw new Error(`Unsafe template path ${file.relativePath}.`);\n await ensureDirectory(dirname(target), createdDirectories);\n const targetStat = await getPathStat(target);\n if (targetStat) {\n if (targetStat.isSymbolicLink() || !targetStat.isFile()) {\n throw new Error(`Unsafe destination entry ${target}: expected a regular file.`);\n }\n const backup = join(backupDirectory, file.relativePath);\n await mkdir(dirname(backup), { recursive: true });\n await copyFile(target, backup);\n backups.set(target, backup);\n } else {\n createdFiles.add(target);\n }\n await copyFile(file.source, target);\n await chmod(target, file.mode);\n context.onFileCopied?.(target);\n }\n assertNotAborted(context.signal);\n await customizeTemplate(destination, options);\n const tokenBytes = Buffer.from(templateTokenPrefix);\n for (const file of files) {\n assertNotAborted(context.signal);\n if ((await readFile(join(destination, file.relativePath))).includes(tokenBytes)) {\n throw new Error(`Generated file ${file.relativePath} contains an unresolved template token.`);\n }\n }\n return { destination, filesCopied: files.length };\n } catch (error) {\n const cleanupErrors: unknown[] = [];\n for (const [target, backup] of backups) {\n try {\n await copyFile(backup, target);\n } catch (cleanupError) {\n cleanupErrors.push(cleanupError);\n }\n }\n for (const file of createdFiles) {\n try {\n await rm(file, { force: true });\n } catch (cleanupError) {\n cleanupErrors.push(cleanupError);\n }\n }\n for (const directory of [...createdDirectories].sort((left, right) => right.length - left.length)) {\n try {\n await rmdir(directory);\n } catch (cleanupError) {\n if (!['ENOENT', 'ENOTEMPTY'].includes((cleanupError as NodeJS.ErrnoException).code ?? '')) {\n cleanupErrors.push(cleanupError);\n }\n }\n }\n const reason = error instanceof Error ? error.message : String(error);\n const recovery = cleanupErrors.length\n ? `Recovery: review ${destination}; some changes could not be rolled back automatically.`\n : `Recovery: changes from this invocation were rolled back; resolve the error and retry.`;\n throw new MaterializationError(`${reason} ${recovery}`, { cause: error });\n } finally {\n await rm(backupDirectory, { recursive: true, force: true });\n }\n};\n","import { spawn } from 'node:child_process';\n\nimport type { GeneratorOptions } from './options';\n\nexport class ProcessStepError extends Error {\n readonly exitCode: number;\n\n constructor(message: string, exitCode = 1, options?: ErrorOptions) {\n super(message, options);\n this.exitCode = exitCode;\n }\n}\n\nexport type Command = {\n executable: string;\n arguments: readonly string[];\n cwd: string;\n signal?: AbortSignal;\n environment?: NodeJS.ProcessEnv;\n};\n\nexport type CommandExecutor = (command: Command) => Promise<void>;\n\nconst signalExitCode = (signal: NodeJS.Signals | null) => {\n if (signal === 'SIGINT') return 130;\n if (signal === 'SIGTERM') return 143;\n return 1;\n};\n\nexport const executeCommand: CommandExecutor = ({ executable, arguments: args, cwd, signal, environment }) =>\n new Promise((resolve, reject) => {\n const child = spawn(executable, args, {\n cwd,\n stdio: 'inherit',\n ...(signal ? { signal } : {}),\n ...(environment ? { env: environment } : {}),\n });\n\n child.once('error', (error) => {\n const interrupted = signal?.aborted;\n reject(\n new ProcessStepError(\n interrupted\n ? `${executable} was interrupted. The generated project remains at ${cwd}.`\n : `Could not start ${executable}: ${error.message}. The generated project remains at ${cwd}.`,\n interrupted ? 130 : 1,\n { cause: error },\n ),\n );\n });\n child.once('close', (code, processSignal) => {\n if (code === 0) {\n resolve();\n return;\n }\n const detail = processSignal ? `signal ${processSignal}` : `exit code ${code ?? 'unknown'}`;\n reject(\n new ProcessStepError(\n `${executable} failed with ${detail}. The generated project remains at ${cwd}; review the output above and retry the command there.`,\n processSignal ? signalExitCode(processSignal) : (code ?? 1),\n ),\n );\n });\n });\n\ntype ProjectSetupContext = {\n destination: string;\n signal?: AbortSignal;\n execute?: CommandExecutor;\n environment?: NodeJS.ProcessEnv;\n};\n\nexport const runProjectSetup = async (options: GeneratorOptions, context: ProjectSetupContext) => {\n const execute = context.execute ?? executeCommand;\n const command = (executable: string, args: readonly string[]) =>\n execute({\n executable,\n arguments: args,\n cwd: context.destination,\n ...(context.signal ? { signal: context.signal } : {}),\n ...(context.environment ? { environment: context.environment } : {}),\n });\n\n if (options.install) await command('npm', ['install']);\n if (options.git) await command('git', ['init', '--initial-branch=main']);\n};\n","import { isAbsolute, relative } from 'node:path';\n\nimport type { GeneratorOptions } from './options';\n\ntype CompletionContext = {\n destination: string;\n cwd?: string;\n color?: boolean;\n};\n\nconst ansi = {\n bold: (value: string) => `\\u001B[1m${value}\\u001B[22m`,\n green: (value: string) => `\\u001B[32m${value}\\u001B[39m`,\n};\n\nconst commandPath = (path: string) => {\n if (/^[\\w./@-]+$/u.test(path)) return path;\n return `\"${path.replaceAll('\"', '\\\\\"')}\"`;\n};\n\nconst displayDestination = (destination: string, cwd: string) => {\n const path = relative(cwd, destination);\n if (!path) return '.';\n return isAbsolute(path) ? destination : path;\n};\n\nexport const supportsColor = (environment: NodeJS.ProcessEnv = process.env, terminal = Boolean(process.stdout.isTTY)) =>\n terminal && environment.NO_COLOR === undefined;\n\nexport const formatCompletion = (options: GeneratorOptions, context: CompletionContext) => {\n const cwd = context.cwd ?? process.cwd();\n const color = context.color ?? supportsColor();\n const decorate = color ? ansi : { bold: (value: string) => value, green: (value: string) => value };\n const destination = displayDestination(context.destination, cwd);\n const commands = [\n ...(destination === '.' ? [] : [`cd ${commandPath(destination)}`]),\n ...(options.install ? [] : ['npm install']),\n 'npm run dev',\n ];\n\n return `${decorate.green('✓')} Created ${options.siteName} in ${destination}\\n\\n${decorate.bold('Setup')}\\n Dependencies: ${options.install ? 'installed' : 'skipped'}\\n Git repository: ${options.git ? 'initialized' : 'skipped'}\\n\\n${decorate.bold('Next steps')}\\n${commands.map((command) => ` ${command}`).join('\\n')}\\n`;\n};\n","#!/usr/bin/env node\n\nimport { readFile } from 'node:fs/promises';\n\nimport { confirm, isCancel } from '@clack/prompts';\n\nimport { runCli } from './cli';\nimport { materializeProject } from './materialize';\nimport { ProcessStepError, runProjectSetup } from './process';\nimport { formatCompletion } from './reporter';\n\ntype PackageMetadata = {\n version: string;\n};\n\nconst packageJson = new URL('../package.json', import.meta.url);\nconst metadata = JSON.parse(await readFile(packageJson, 'utf8')) as PackageMetadata;\nconst interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);\nconst result = await runCli(process.argv.slice(2), metadata.version, { interactive });\n\nif (result.options) {\n const controller = new AbortController();\n let interruptedExitCode: number | undefined;\n const interrupt = () => {\n interruptedExitCode = 130;\n controller.abort();\n };\n const terminate = () => {\n interruptedExitCode = 143;\n controller.abort();\n };\n process.once('SIGINT', interrupt);\n process.once('SIGTERM', terminate);\n try {\n const materialized = await materializeProject(result.options, {\n templateDirectory: new URL('./template/', import.meta.url),\n signal: controller.signal,\n ...(interactive\n ? {\n confirmOverwrite: async (destination: string) => {\n const answer = await confirm({\n message: `${destination} is not empty. Continue and overwrite conflicting template files?`,\n initialValue: false,\n });\n return !isCancel(answer) && answer;\n },\n }\n : {}),\n });\n await runProjectSetup(result.options, {\n destination: materialized.destination,\n signal: controller.signal,\n });\n result.output = formatCompletion(result.options, { destination: materialized.destination });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n result.output = `error: ${message}\\n`;\n result.exitCode = interruptedExitCode ?? (error instanceof ProcessStepError ? error.exitCode : 1);\n } finally {\n process.off('SIGINT', interrupt);\n process.off('SIGTERM', terminate);\n }\n}\n\nprocess.stdout.write(result.output);\nprocess.exitCode = result.exitCode;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/prompts.ts","../src/options.ts","../src/validation.ts","../src/resolve-options.ts","../src/cli.ts","../src/schedule.ts","../src/template-tokens.ts","../src/materialize.ts","../src/process.ts","../src/reporter.ts","../src/index.ts"],"sourcesContent":["import { confirm, isCancel, text } from '@clack/prompts';\n\nexport type TextPrompt = {\n message: string;\n initialValue?: string;\n placeholder?: string;\n validate?: (value: string | undefined) => string | Error | undefined;\n};\n\nexport type ConfirmPrompt = { message: string; initialValue: boolean };\n\nexport type PromptAdapter = {\n text(options: TextPrompt): Promise<string | symbol>;\n confirm(options: ConfirmPrompt): Promise<boolean | symbol>;\n isCancel(value: unknown): boolean;\n};\n\nexport const clackPrompts: PromptAdapter = {\n text: (options) =>\n text({\n message: options.message,\n ...(options.initialValue === undefined ? {} : { initialValue: options.initialValue }),\n ...(options.placeholder === undefined ? {} : { placeholder: options.placeholder }),\n ...(options.validate === undefined ? {} : { validate: options.validate }),\n }),\n confirm,\n isCancel,\n};\n","export type GeneratorOptions = {\n destination: string;\n packageName: string;\n siteName: string;\n description: string;\n author: string;\n url: string;\n repository: string;\n install: boolean;\n git: boolean;\n};\n\nexport type ProvidedOptions = {\n [Key in keyof GeneratorOptions]?: GeneratorOptions[Key] | undefined;\n} & { yes: boolean };\n\nexport const defaultDescription = 'A content-driven website built with Forge';\nexport const defaultUrl = 'https://example.com';\n\nexport const derivePackageName = (destination: string) => {\n return basename(resolve(destination)) || 'forge-site';\n};\n\nexport const humanizePackageName = (packageName: string) => {\n const unscopedName = packageName.split('/').at(-1) ?? packageName;\n return unscopedName\n .split(/[-_\\s]+/u)\n .filter(Boolean)\n .map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)\n .join(' ');\n};\nimport { basename, resolve } from 'node:path';\n","import validateNpmPackageName from 'validate-npm-package-name';\n\nexport class InputError extends Error {}\n\nconst invalid = (option: string, value: string, reason: string): never => {\n throw new InputError(`Invalid ${option} value ${JSON.stringify(value)}: ${reason}`);\n};\n\nexport const validateDestination = (value: string) => {\n const destination = value.trim();\n if (!destination) invalid('destination', value, 'provide a non-empty path');\n if (destination.includes('\\0')) invalid('destination', value, 'paths cannot contain null bytes');\n return destination;\n};\n\nexport const validatePackageName = (value: string) => {\n const packageName = value.trim();\n const result = validateNpmPackageName(packageName);\n if (!result.validForNewPackages) {\n const reason = [...(result.errors ?? []), ...(result.warnings ?? [])].join('; ');\n invalid('--name', value, reason || 'provide a valid npm package name');\n }\n return packageName;\n};\n\nexport const validatePlainText = (option: string, value: string, required = false) => {\n const text = value.trim();\n if (required && !text) invalid(option, value, 'provide non-empty text');\n if (/\\p{Cc}/u.test(text)) invalid(option, value, 'control characters are not allowed');\n return text;\n};\n\nexport const validateUrl = (value: string) => {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return invalid('--url', value, 'provide an absolute HTTP(S) URL');\n }\n if (!['http:', 'https:'].includes(url.protocol)) {\n invalid('--url', value, 'provide an absolute HTTP(S) URL');\n }\n return url.toString();\n};\n\nexport const validateRepository = (value: string) => {\n const repository = value.trim();\n if (!repository) return repository;\n if (!/^[\\w.-]+\\/[\\w.-]+$/u.test(repository)) {\n invalid('--repository', value, 'use the GitHub owner/repository format or leave it empty');\n }\n return repository;\n};\n","import {\n defaultDescription,\n defaultUrl,\n derivePackageName,\n humanizePackageName,\n type GeneratorOptions,\n type ProvidedOptions,\n} from './options';\nimport type { PromptAdapter, TextPrompt } from './prompts';\nimport {\n InputError,\n validateDestination,\n validatePackageName,\n validatePlainText,\n validateRepository,\n validateUrl,\n} from './validation';\n\nexport class CancellationError extends Error {}\n\ntype ResolutionContext = { interactive: boolean; prompts: PromptAdapter };\n\nconst textValidation = (validator: (value: string) => string) => (value: string | undefined) => {\n try {\n validator(value ?? '');\n return undefined;\n } catch (error) {\n return error instanceof Error ? error.message : 'Invalid value';\n }\n};\n\nconst promptText = async (prompts: PromptAdapter, options: TextPrompt) => {\n const value = await prompts.text(options);\n if (prompts.isCancel(value)) throw new CancellationError('Project creation cancelled.');\n return String(value);\n};\n\nconst promptConfirm = async (prompts: PromptAdapter, message: string) => {\n const value = await prompts.confirm({ message, initialValue: true });\n if (prompts.isCancel(value)) throw new CancellationError('Project creation cancelled.');\n return Boolean(value);\n};\n\nconst hasEveryExplicitValue = (options: ProvidedOptions) =>\n options.destination !== undefined &&\n options.packageName !== undefined &&\n options.siteName !== undefined &&\n options.description !== undefined &&\n options.author !== undefined &&\n options.url !== undefined &&\n options.repository !== undefined &&\n options.install !== undefined &&\n options.git !== undefined;\n\nexport const resolveOptions = async (\n provided: ProvidedOptions,\n { interactive, prompts }: ResolutionContext,\n): Promise<GeneratorOptions> => {\n const useDefaults = provided.yes;\n if (!interactive && !useDefaults && !hasEveryExplicitValue(provided)) {\n throw new InputError(\n 'Non-interactive use requires --yes or explicit values for every generator option. Run create-forge --help for details.',\n );\n }\n\n const destination = validateDestination(\n provided.destination ??\n (useDefaults\n ? '.'\n : await promptText(prompts, {\n message: 'Where should we create your project?',\n placeholder: '.',\n initialValue: '.',\n validate: textValidation(validateDestination),\n })),\n );\n const derivedPackageName = derivePackageName(destination);\n const packageName = validatePackageName(\n provided.packageName ??\n (useDefaults\n ? derivedPackageName\n : await promptText(prompts, {\n message: 'What should the npm package be named?',\n initialValue: derivedPackageName,\n validate: textValidation(validatePackageName),\n })),\n );\n const defaultSiteName = humanizePackageName(packageName);\n const siteName = validatePlainText(\n '--site-name',\n provided.siteName ??\n (useDefaults\n ? defaultSiteName\n : await promptText(prompts, {\n message: 'What should the site be called?',\n initialValue: defaultSiteName,\n validate: textValidation((value) => validatePlainText('--site-name', value, true)),\n })),\n true,\n );\n const description = validatePlainText(\n '--description',\n provided.description ??\n (useDefaults\n ? defaultDescription\n : await promptText(prompts, {\n message: 'How would you describe the site?',\n initialValue: defaultDescription,\n validate: textValidation((value) => validatePlainText('--description', value)),\n })),\n );\n const author = validatePlainText(\n '--author',\n provided.author ??\n (useDefaults\n ? ''\n : await promptText(prompts, {\n message: 'Who is the site author?',\n placeholder: 'Optional',\n validate: textValidation((value) => validatePlainText('--author', value)),\n })),\n );\n const url = validateUrl(\n provided.url ??\n (useDefaults\n ? defaultUrl\n : await promptText(prompts, {\n message: 'What is the canonical site URL?',\n initialValue: defaultUrl,\n validate: textValidation(validateUrl),\n })),\n );\n const repository = validateRepository(\n provided.repository ??\n (useDefaults\n ? ''\n : await promptText(prompts, {\n message: 'What is the GitHub repository?',\n placeholder: 'owner/repository (optional)',\n validate: textValidation(validateRepository),\n })),\n );\n const install = provided.install ?? (useDefaults ? true : await promptConfirm(prompts, 'Install dependencies?'));\n const git = provided.git ?? (useDefaults ? true : await promptConfirm(prompts, 'Initialize a Git repository?'));\n\n return { destination, packageName, siteName, description, author, url, repository, install, git };\n};\n","import { Command, CommanderError, Option } from 'commander';\n\nimport type { GeneratorOptions, ProvidedOptions } from './options';\nimport { clackPrompts, type PromptAdapter } from './prompts';\nimport { CancellationError, resolveOptions } from './resolve-options';\n\nexport type CliResult = { output: string; exitCode: number; options?: GeneratorOptions };\nexport type CliContext = { interactive?: boolean; prompts?: PromptAdapter };\n\nconst hasBoth = (args: readonly string[], positive: string, negative: string) =>\n args.includes(positive) && args.includes(negative);\n\nconst parseArguments = (args: readonly string[], version: string) => {\n let output = '';\n const program = new Command()\n .name('create-forge')\n .description('Create a content-driven Forge website')\n .argument('[destination]', 'directory where the project will be created')\n .option('--name <name>', 'npm package name (defaults to the directory basename)')\n .option('--site-name <name>', 'site name')\n .option('--description <text>', 'site description')\n .option('--author <name>', 'site author')\n .option('--url <url>', 'absolute canonical HTTP(S) URL')\n .option('--repository <owner/repository>', 'GitHub repository or an empty value')\n .addOption(new Option('--install', 'install dependencies'))\n .addOption(new Option('--no-install', 'do not install dependencies'))\n .addOption(new Option('--git', 'initialize a Git repository'))\n .addOption(new Option('--no-git', 'do not initialize a Git repository'))\n .option('-y, --yes', 'use documented defaults without prompting', false)\n .version(version, '-v, --version', 'show the installed package version')\n .helpOption('-h, --help', 'show this help message')\n .showHelpAfterError()\n .allowExcessArguments(false)\n .exitOverride()\n .configureOutput({\n writeOut: (value) => {\n output += value;\n },\n writeErr: (value) => {\n output += value;\n },\n });\n\n try {\n program.parse(args, { from: 'user' });\n } catch (error) {\n if (error instanceof CommanderError) return { output, exitCode: error.exitCode } as const;\n throw error;\n }\n\n if (hasBoth(args, '--install', '--no-install')) {\n return { output: 'error: --install cannot be used with --no-install\\n', exitCode: 1 } as const;\n }\n if (hasBoth(args, '--git', '--no-git')) {\n return { output: 'error: --git cannot be used with --no-git\\n', exitCode: 1 } as const;\n }\n\n const raw = program.opts();\n const provided: ProvidedOptions = {\n destination: program.args[0],\n packageName: raw.name,\n siteName: raw.siteName,\n description: raw.description,\n author: raw.author,\n url: raw.url,\n repository: raw.repository,\n install: raw.install,\n git: raw.git,\n yes: raw.yes,\n };\n return { provided } as const;\n};\n\nexport const runCli = async (\n args: readonly string[],\n version: string,\n context: CliContext = {},\n): Promise<CliResult> => {\n const parsed = parseArguments(args, version);\n if (!('provided' in parsed)) return parsed;\n\n try {\n const options = await resolveOptions(parsed.provided, {\n interactive: context.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY),\n prompts: context.prompts ?? clackPrompts,\n });\n return { output: '', exitCode: 0, options };\n } catch (error) {\n if (error instanceof CancellationError) return { output: `${error.message}\\n`, exitCode: 1 };\n if (error instanceof Error) return { output: `error: ${error.message}\\n`, exitCode: 1 };\n throw error;\n }\n};\n","import { createHash } from 'node:crypto';\n\nexport const deriveScheduleMinutes = (packageName: string) => {\n const digest = createHash('sha256').update(packageName).digest();\n const security = 1 + (digest.readUInt8(0) % 59);\n const automation = 1 + ((security - 1 + 17 + (digest.readUInt8(1) % 29)) % 59);\n return { security, automation };\n};\n","const prefix = '__RM_INDUSTRIES_FORGE_TEMPLATE_V1_';\n\nexport const templateTokens = Object.freeze({\n packageName: `${prefix}PACKAGE_NAME__`,\n siteName: `${prefix}SITE_NAME__`,\n description: `${prefix}DESCRIPTION__`,\n author: `${prefix}AUTHOR__`,\n url: `${prefix}URL__`,\n repository: `${prefix}REPOSITORY__`,\n securityScheduleMinute: `${prefix}SECURITY_SCHEDULE_MINUTE__`,\n automationScheduleMinute: `${prefix}AUTOMATION_SCHEDULE_MINUTE__`,\n});\n\nexport const templateTokenPrefix = prefix;\n","import {\n chmod,\n copyFile,\n lstat,\n mkdir,\n mkdtemp,\n readdir,\n readFile,\n rm,\n rmdir,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { dirname, isAbsolute, join, parse, relative, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { GeneratorOptions } from './options';\nimport { deriveScheduleMinutes } from './schedule';\nimport { templateTokenPrefix, templateTokens } from './template-tokens';\n\nexport class MaterializationError extends Error {}\n\ntype MaterializationContext = {\n templateDirectory: string | URL;\n cwd?: string;\n signal?: AbortSignal;\n confirmOverwrite?: (destination: string) => Promise<boolean>;\n onFileCopied?: (path: string) => void;\n};\n\ntype TemplateFile = { source: string; relativePath: string; mode: number };\n\nconst pathExists = async (path: string) => {\n try {\n await lstat(path);\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw error;\n }\n};\n\nconst getPathStat = async (path: string) => {\n try {\n return await lstat(path);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n};\n\nconst assertNotAborted = (signal?: AbortSignal) => {\n if (signal?.aborted) throw new Error('Project creation was interrupted.');\n};\n\nconst resolveTemplateDirectory = (directory: string | URL) =>\n directory instanceof URL ? fileURLToPath(directory) : resolve(directory);\n\nconst resolveDestination = (destination: string, cwd: string) => {\n const segments = destination.split(/[\\\\/]/u);\n if (segments.includes('..')) {\n throw new MaterializationError(\n `Unsafe destination ${JSON.stringify(destination)}: parent traversal is not allowed.`,\n );\n }\n const resolved = isAbsolute(destination) ? resolve(destination) : resolve(cwd, destination);\n if (resolved === parse(resolved).root) {\n throw new MaterializationError(\n `Unsafe destination ${JSON.stringify(destination)}: a filesystem root is not allowed.`,\n );\n }\n return resolved;\n};\n\nconst listTemplateFiles = async (root: string, directory = root): Promise<TemplateFile[]> => {\n const entries = await readdir(directory, { withFileTypes: true });\n const files: TemplateFile[] = [];\n for (const entry of entries) {\n const source = join(directory, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await listTemplateFiles(root, source)));\n continue;\n }\n if (!entry.isFile()) throw new MaterializationError(`Template entry ${source} is not a regular file.`);\n const relativePath = relative(root, source);\n files.push({\n source,\n relativePath: relativePath === '.gitignore.template' ? '.gitignore' : relativePath,\n mode: (await stat(source)).mode,\n });\n }\n return files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));\n};\n\nconst isDirectoryEmpty = async (directory: string) => (await readdir(directory)).length === 0;\n\nconst ensureDirectory = async (directory: string, created: Set<string>) => {\n const existing = await getPathStat(directory);\n if (existing) {\n if (existing.isSymbolicLink() || !existing.isDirectory()) {\n throw new Error(`Unsafe destination entry ${directory}: expected a regular directory.`);\n }\n return;\n }\n const parent = dirname(directory);\n if (parent !== directory) await ensureDirectory(parent, created);\n await mkdir(directory);\n created.add(directory);\n};\n\nconst quoteTypeScriptString = (value: string) =>\n `'${value\n .replaceAll('\\\\', '\\\\\\\\')\n .replaceAll(\"'\", \"\\\\'\")\n .replaceAll('\\u2028', '\\\\u2028')\n .replaceAll('\\u2029', '\\\\u2029')}'`;\n\nconst replaceToken = (source: string, token: string, value: string, field: string) => {\n const quotedToken = `'${token}'`;\n if (!source.includes(quotedToken)) throw new Error(`Packaged template is missing the ${field} token.`);\n return source.replaceAll(quotedToken, quoteTypeScriptString(value));\n};\n\nconst replaceRawToken = (source: string, token: string, value: string, field: string) => {\n if (!source.includes(token)) throw new Error(`Packaged template is missing the ${field} token.`);\n return source.replaceAll(token, value);\n};\n\nconst customizeTemplate = async (destination: string, options: GeneratorOptions) => {\n const packagePath = join(destination, 'package.json');\n const packageLockPath = join(destination, 'package-lock.json');\n const packageMetadata = JSON.parse(await readFile(packagePath, 'utf8')) as Record<string, unknown>;\n const packageLock = JSON.parse(await readFile(packageLockPath, 'utf8')) as {\n name?: string;\n packages?: Record<string, Record<string, unknown>>;\n };\n if (\n packageMetadata.name !== templateTokens.packageName ||\n packageLock.name !== templateTokens.packageName ||\n packageLock.packages?.['']?.name !== templateTokens.packageName\n ) {\n throw new Error('Packaged template contains unexpected package metadata tokens.');\n }\n packageMetadata.name = options.packageName;\n packageMetadata.version = '0.0.0';\n packageMetadata.private = true;\n packageLock.name = options.packageName;\n if (!packageLock.packages?.['']) throw new Error('Packaged template lockfile is missing its root package metadata.');\n packageLock.packages[''].name = options.packageName;\n packageLock.packages[''].version = '0.0.0';\n await writeFile(packagePath, `${JSON.stringify(packageMetadata, undefined, 2)}\\n`);\n await writeFile(packageLockPath, `${JSON.stringify(packageLock, undefined, 2)}\\n`);\n\n const siteConfigPath = join(destination, 'src', 'config', 'site.ts');\n let siteConfig = await readFile(siteConfigPath, 'utf8');\n siteConfig = replaceToken(siteConfig, templateTokens.siteName, options.siteName, 'site name');\n siteConfig = replaceToken(siteConfig, templateTokens.description, options.description, 'description');\n siteConfig = replaceToken(siteConfig, templateTokens.author, options.author, 'author');\n siteConfig = replaceToken(siteConfig, templateTokens.url, options.url, 'URL');\n siteConfig = replaceToken(siteConfig, templateTokens.repository, options.repository, 'repository');\n if (siteConfig.includes(templateTokenPrefix)) throw new Error('Packaged template contains unresolved tokens.');\n await writeFile(siteConfigPath, siteConfig);\n\n const scheduleMinutes = deriveScheduleMinutes(options.packageName);\n const securityWorkflowPath = join(destination, '.github', 'workflows', 'security.yml');\n const automationWorkflowPath = join(destination, '.github', 'workflows', 'automation.yml');\n let securityWorkflow = await readFile(securityWorkflowPath, 'utf8');\n let automationWorkflow = await readFile(automationWorkflowPath, 'utf8');\n securityWorkflow = replaceRawToken(\n securityWorkflow,\n templateTokens.securityScheduleMinute,\n String(scheduleMinutes.security),\n 'security schedule minute',\n );\n automationWorkflow = replaceRawToken(\n automationWorkflow,\n templateTokens.automationScheduleMinute,\n String(scheduleMinutes.automation),\n 'automation schedule minute',\n );\n await writeFile(securityWorkflowPath, securityWorkflow);\n await writeFile(automationWorkflowPath, automationWorkflow);\n};\n\nexport const materializeProject = async (options: GeneratorOptions, context: MaterializationContext) => {\n const cwd = context.cwd ?? process.cwd();\n const destination = resolveDestination(options.destination, cwd);\n const templateDirectory = resolveTemplateDirectory(context.templateDirectory);\n const destinationExisted = await pathExists(destination);\n if (destinationExisted) {\n const destinationStat = await lstat(destination);\n if (destinationStat.isSymbolicLink() || !destinationStat.isDirectory()) {\n throw new MaterializationError(`Unsafe destination ${destination}: expected a regular directory.`);\n }\n }\n if (destinationExisted && !(await isDirectoryEmpty(destination))) {\n const confirmed = await context.confirmOverwrite?.(destination);\n if (!confirmed) {\n throw new MaterializationError(`Destination ${destination} is not empty. No files were changed.`);\n }\n }\n\n const files = await listTemplateFiles(templateDirectory);\n const backupDirectory = await mkdtemp(join(tmpdir(), 'create-forge-backup-'));\n const createdFiles = new Set<string>();\n const createdDirectories = new Set<string>();\n const backups = new Map<string, string>();\n\n try {\n assertNotAborted(context.signal);\n await ensureDirectory(destination, createdDirectories);\n for (const file of files) {\n assertNotAborted(context.signal);\n const target = join(destination, file.relativePath);\n const targetRelative = relative(destination, target);\n if (targetRelative.startsWith('..') || isAbsolute(targetRelative))\n throw new Error(`Unsafe template path ${file.relativePath}.`);\n await ensureDirectory(dirname(target), createdDirectories);\n const targetStat = await getPathStat(target);\n if (targetStat) {\n if (targetStat.isSymbolicLink() || !targetStat.isFile()) {\n throw new Error(`Unsafe destination entry ${target}: expected a regular file.`);\n }\n const backup = join(backupDirectory, file.relativePath);\n await mkdir(dirname(backup), { recursive: true });\n await copyFile(target, backup);\n backups.set(target, backup);\n } else {\n createdFiles.add(target);\n }\n await copyFile(file.source, target);\n await chmod(target, file.mode);\n context.onFileCopied?.(target);\n }\n assertNotAborted(context.signal);\n await customizeTemplate(destination, options);\n const tokenBytes = Buffer.from(templateTokenPrefix);\n for (const file of files) {\n assertNotAborted(context.signal);\n if ((await readFile(join(destination, file.relativePath))).includes(tokenBytes)) {\n throw new Error(`Generated file ${file.relativePath} contains an unresolved template token.`);\n }\n }\n return { destination, filesCopied: files.length };\n } catch (error) {\n const cleanupErrors: unknown[] = [];\n for (const [target, backup] of backups) {\n try {\n await copyFile(backup, target);\n } catch (cleanupError) {\n cleanupErrors.push(cleanupError);\n }\n }\n for (const file of createdFiles) {\n try {\n await rm(file, { force: true });\n } catch (cleanupError) {\n cleanupErrors.push(cleanupError);\n }\n }\n for (const directory of [...createdDirectories].sort((left, right) => right.length - left.length)) {\n try {\n await rmdir(directory);\n } catch (cleanupError) {\n if (!['ENOENT', 'ENOTEMPTY'].includes((cleanupError as NodeJS.ErrnoException).code ?? '')) {\n cleanupErrors.push(cleanupError);\n }\n }\n }\n const reason = error instanceof Error ? error.message : String(error);\n const recovery = cleanupErrors.length\n ? `Recovery: review ${destination}; some changes could not be rolled back automatically.`\n : `Recovery: changes from this invocation were rolled back; resolve the error and retry.`;\n throw new MaterializationError(`${reason} ${recovery}`, { cause: error });\n } finally {\n await rm(backupDirectory, { recursive: true, force: true });\n }\n};\n","import { spawn } from 'node:child_process';\n\nimport type { GeneratorOptions } from './options';\n\nexport class ProcessStepError extends Error {\n readonly exitCode: number;\n\n constructor(message: string, exitCode = 1, options?: ErrorOptions) {\n super(message, options);\n this.exitCode = exitCode;\n }\n}\n\nexport type Command = {\n executable: string;\n arguments: readonly string[];\n cwd: string;\n signal?: AbortSignal;\n environment?: NodeJS.ProcessEnv;\n};\n\nexport type CommandExecutor = (command: Command) => Promise<void>;\n\nconst signalExitCode = (signal: NodeJS.Signals | null) => {\n if (signal === 'SIGINT') return 130;\n if (signal === 'SIGTERM') return 143;\n return 1;\n};\n\nexport const executeCommand: CommandExecutor = ({ executable, arguments: args, cwd, signal, environment }) =>\n new Promise((resolve, reject) => {\n const child = spawn(executable, args, {\n cwd,\n stdio: 'inherit',\n ...(signal ? { signal } : {}),\n ...(environment ? { env: environment } : {}),\n });\n\n child.once('error', (error) => {\n const interrupted = signal?.aborted;\n reject(\n new ProcessStepError(\n interrupted\n ? `${executable} was interrupted. The generated project remains at ${cwd}.`\n : `Could not start ${executable}: ${error.message}. The generated project remains at ${cwd}.`,\n interrupted ? 130 : 1,\n { cause: error },\n ),\n );\n });\n child.once('close', (code, processSignal) => {\n if (code === 0) {\n resolve();\n return;\n }\n const detail = processSignal ? `signal ${processSignal}` : `exit code ${code ?? 'unknown'}`;\n reject(\n new ProcessStepError(\n `${executable} failed with ${detail}. The generated project remains at ${cwd}; review the output above and retry the command there.`,\n processSignal ? signalExitCode(processSignal) : (code ?? 1),\n ),\n );\n });\n });\n\ntype ProjectSetupContext = {\n destination: string;\n signal?: AbortSignal;\n execute?: CommandExecutor;\n environment?: NodeJS.ProcessEnv;\n};\n\nexport const runProjectSetup = async (options: GeneratorOptions, context: ProjectSetupContext) => {\n const execute = context.execute ?? executeCommand;\n const command = (executable: string, args: readonly string[]) =>\n execute({\n executable,\n arguments: args,\n cwd: context.destination,\n ...(context.signal ? { signal: context.signal } : {}),\n ...(context.environment ? { environment: context.environment } : {}),\n });\n\n if (options.install) await command('npm', ['install']);\n if (options.git) await command('git', ['init', '--initial-branch=main']);\n};\n","import { isAbsolute, relative } from 'node:path';\n\nimport type { GeneratorOptions } from './options';\n\ntype CompletionContext = {\n destination: string;\n cwd?: string;\n color?: boolean;\n};\n\nconst ansi = {\n bold: (value: string) => `\\u001B[1m${value}\\u001B[22m`,\n green: (value: string) => `\\u001B[32m${value}\\u001B[39m`,\n};\n\nconst commandPath = (path: string) => {\n if (/^[\\w./@-]+$/u.test(path)) return path;\n return `\"${path.replaceAll('\"', '\\\\\"')}\"`;\n};\n\nconst displayDestination = (destination: string, cwd: string) => {\n const path = relative(cwd, destination);\n if (!path) return '.';\n return isAbsolute(path) ? destination : path;\n};\n\nexport const supportsColor = (environment: NodeJS.ProcessEnv = process.env, terminal = Boolean(process.stdout.isTTY)) =>\n terminal && environment.NO_COLOR === undefined;\n\nexport const formatCompletion = (options: GeneratorOptions, context: CompletionContext) => {\n const cwd = context.cwd ?? process.cwd();\n const color = context.color ?? supportsColor();\n const decorate = color ? ansi : { bold: (value: string) => value, green: (value: string) => value };\n const destination = displayDestination(context.destination, cwd);\n const commands = [\n ...(destination === '.' ? [] : [`cd ${commandPath(destination)}`]),\n ...(options.install ? [] : ['npm install']),\n 'npm run dev',\n ];\n\n return `${decorate.green('✓')} Created ${options.siteName} in ${destination}\\n\\n${decorate.bold('Setup')}\\n Dependencies: ${options.install ? 'installed' : 'skipped'}\\n Git repository: ${options.git ? 'initialized' : 'skipped'}\\n\\n${decorate.bold('Next steps')}\\n${commands.map((command) => ` ${command}`).join('\\n')}\\n`;\n};\n","#!/usr/bin/env node\n\nimport { readFile } from 'node:fs/promises';\n\nimport { confirm, isCancel } from '@clack/prompts';\n\nimport { runCli } from './cli';\nimport { materializeProject } from './materialize';\nimport { ProcessStepError, runProjectSetup } from './process';\nimport { formatCompletion } from './reporter';\n\ntype PackageMetadata = {\n version: string;\n};\n\nconst packageJson = new URL('../package.json', import.meta.url);\nconst metadata = JSON.parse(await readFile(packageJson, 'utf8')) as PackageMetadata;\nconst interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);\nconst result = await runCli(process.argv.slice(2), metadata.version, { interactive });\n\nif (result.options) {\n const controller = new AbortController();\n let interruptedExitCode: number | undefined;\n const interrupt = () => {\n interruptedExitCode = 130;\n controller.abort();\n };\n const terminate = () => {\n interruptedExitCode = 143;\n controller.abort();\n };\n process.once('SIGINT', interrupt);\n process.once('SIGTERM', terminate);\n try {\n const materialized = await materializeProject(result.options, {\n templateDirectory: new URL('./template/', import.meta.url),\n signal: controller.signal,\n ...(interactive\n ? {\n confirmOverwrite: async (destination: string) => {\n const answer = await confirm({\n message: `${destination} is not empty. Continue and overwrite conflicting template files?`,\n initialValue: false,\n });\n return !isCancel(answer) && answer;\n },\n }\n : {}),\n });\n await runProjectSetup(result.options, {\n destination: materialized.destination,\n signal: controller.signal,\n });\n result.output = formatCompletion(result.options, { destination: materialized.destination });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n result.output = `error: ${message}\\n`;\n result.exitCode = interruptedExitCode ?? (error instanceof ProcessStepError ? error.exitCode : 1);\n } finally {\n process.off('SIGINT', interrupt);\n process.off('SIGTERM', terminate);\n }\n}\n\nprocess.stdout.write(result.output);\nprocess.exitCode = result.exitCode;\n"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: Setup project
3
+ description: Set up Node.js and install project dependencies deterministically
4
+
5
+ inputs:
6
+ node-version:
7
+ description: Node.js version to install
8
+ required: false
9
+ default: '26.x'
10
+
11
+ runs:
12
+ using: composite
13
+
14
+ steps:
15
+ - name: Setup Node.js
16
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
17
+ with:
18
+ node-version: ${{ inputs.node-version }}
19
+ cache: npm
20
+ cache-dependency-path: package-lock.json
21
+
22
+ - name: Install dependencies
23
+ shell: bash
24
+ run: npm ci
@@ -0,0 +1,45 @@
1
+ ---
2
+ version: 2
3
+
4
+ updates:
5
+ - package-ecosystem: github-actions
6
+ directory: /
7
+ schedule:
8
+ interval: weekly
9
+ day: monday
10
+ cooldown:
11
+ default-days: 7
12
+ open-pull-requests-limit: 5
13
+ commit-message:
14
+ prefix: chore
15
+ groups:
16
+ github-actions:
17
+ patterns:
18
+ - '*'
19
+
20
+ - package-ecosystem: npm
21
+ directory: /
22
+ schedule:
23
+ interval: weekly
24
+ day: monday
25
+ cooldown:
26
+ default-days: 7
27
+ semver-major-days: 21
28
+ semver-minor-days: 7
29
+ semver-patch-days: 3
30
+ open-pull-requests-limit: 5
31
+ commit-message:
32
+ prefix: chore
33
+ groups:
34
+ production-dependencies:
35
+ dependency-type: production
36
+ exclude-patterns:
37
+ - '@sveltia/cms'
38
+ update-types:
39
+ - minor
40
+ - patch
41
+ development-dependencies:
42
+ dependency-type: development
43
+ update-types:
44
+ - minor
45
+ - patch
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: Repository Automation Validation
3
+
4
+ on: # yamllint disable-line rule:truthy
5
+ pull_request:
6
+ branches:
7
+ - main
8
+ paths:
9
+ - '.github/**'
10
+ push:
11
+ branches:
12
+ - main
13
+ paths:
14
+ - '.github/**'
15
+ schedule:
16
+ - cron: '__RM_INDUSTRIES_FORGE_TEMPLATE_V1_AUTOMATION_SCHEDULE_MINUTE__ 5 * * 1'
17
+
18
+ permissions: {}
19
+
20
+ concurrency:
21
+ group: ${{ github.workflow }}-${{ github.ref }}
22
+ cancel-in-progress: true
23
+
24
+ jobs:
25
+ security:
26
+ name: Workflow security
27
+ runs-on: ubuntu-latest
28
+
29
+ permissions:
30
+ contents: read
31
+ security-events: write
32
+
33
+ steps:
34
+ - name: Checkout repository
35
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
36
+ with:
37
+ persist-credentials: false
38
+
39
+ - name: Scan workflows
40
+ uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2
41
+ with:
42
+ advanced-security: true
43
+ collect: all
44
+
45
+ syntax:
46
+ name: Workflow syntax
47
+ runs-on: ubuntu-latest
48
+
49
+ permissions:
50
+ contents: read
51
+
52
+ steps:
53
+ - name: Checkout repository
54
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
55
+ with:
56
+ persist-credentials: false
57
+
58
+ - name: Validate workflows
59
+ uses: raven-actions/actionlint@3d39aea434753780c3b3d4a1a31c854b4dbf49d7 # v2
@@ -0,0 +1,235 @@
1
+ ---
2
+ name: Project Continuous Integration
3
+
4
+ on: # yamllint disable-line rule:truthy
5
+ pull_request:
6
+ branches:
7
+ - main
8
+ push:
9
+ branches:
10
+ - main
11
+
12
+ permissions: {}
13
+
14
+ concurrency:
15
+ group: ${{ github.workflow }}-${{ github.ref }}
16
+ cancel-in-progress: true
17
+
18
+ defaults:
19
+ run:
20
+ shell: bash
21
+
22
+ jobs:
23
+ format:
24
+ name: Format
25
+ runs-on: ubuntu-latest
26
+
27
+ permissions:
28
+ contents: read
29
+
30
+ steps:
31
+ - name: Checkout repository
32
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
33
+ with:
34
+ persist-credentials: false
35
+
36
+ - name: Setup project
37
+ uses: ./.github/actions/setup-project
38
+ with:
39
+ node-version: '26.x'
40
+
41
+ - name: Check formatting
42
+ run: npm run format
43
+
44
+ spellcheck:
45
+ name: Spellcheck
46
+ runs-on: ubuntu-latest
47
+
48
+ permissions:
49
+ contents: read
50
+
51
+ steps:
52
+ - name: Checkout repository
53
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
54
+ with:
55
+ persist-credentials: false
56
+
57
+ - name: Setup project
58
+ uses: ./.github/actions/setup-project
59
+ with:
60
+ node-version: '26.x'
61
+
62
+ - name: Check spelling
63
+ run: npm run spellcheck
64
+
65
+ static-quality:
66
+ name: Static quality
67
+ runs-on: ubuntu-latest
68
+
69
+ permissions:
70
+ contents: read
71
+
72
+ steps:
73
+ - name: Checkout repository
74
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
75
+ with:
76
+ persist-credentials: false
77
+
78
+ - name: Setup project
79
+ uses: ./.github/actions/setup-project
80
+ with:
81
+ node-version: '26.x'
82
+
83
+ - name: Run remaining static quality checks
84
+ run: npm run quality:static:ci
85
+
86
+ unit-tests:
87
+ name: Unit tests and coverage
88
+ runs-on: ubuntu-latest
89
+
90
+ permissions:
91
+ contents: read
92
+
93
+ steps:
94
+ - name: Checkout repository
95
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
96
+ with:
97
+ persist-credentials: false
98
+
99
+ - name: Setup project
100
+ uses: ./.github/actions/setup-project
101
+ with:
102
+ node-version: '26.x'
103
+
104
+ - name: Run unit tests with coverage
105
+ run: npm run test:coverage
106
+
107
+ - name: Upload coverage report
108
+ if: always()
109
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
110
+ with:
111
+ name: unit-coverage
112
+ path: coverage
113
+ if-no-files-found: error
114
+ retention-days: 7
115
+
116
+ build:
117
+ name: Production build
118
+ needs:
119
+ - format
120
+ - spellcheck
121
+ - static-quality
122
+ runs-on: ubuntu-latest
123
+
124
+ permissions:
125
+ contents: read
126
+
127
+ steps:
128
+ - name: Checkout repository
129
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
130
+ with:
131
+ persist-credentials: false
132
+
133
+ - name: Setup project
134
+ uses: ./.github/actions/setup-project
135
+ with:
136
+ node-version: '26.x'
137
+
138
+ - name: Build project
139
+ run: npm run build
140
+
141
+ - name: Validate generated output
142
+ run: npm run validate:build
143
+
144
+ - name: Upload production build
145
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
146
+ with:
147
+ name: site-build
148
+ path: dist
149
+ if-no-files-found: error
150
+ retention-days: 7
151
+
152
+ browser-tests:
153
+ name: Browser and accessibility tests
154
+ runs-on: ubuntu-latest
155
+
156
+ permissions:
157
+ contents: read
158
+
159
+ steps:
160
+ - name: Checkout repository
161
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
162
+ with:
163
+ persist-credentials: false
164
+
165
+ - name: Setup project
166
+ uses: ./.github/actions/setup-project
167
+ with:
168
+ node-version: '26.x'
169
+
170
+ - name: Install Chromium
171
+ run: npx playwright install --with-deps chromium
172
+
173
+ - name: Run browser and accessibility tests
174
+ run: npm run test:e2e
175
+
176
+ - name: Upload browser failure evidence
177
+ if: failure()
178
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
179
+ with:
180
+ name: browser-test-failures
181
+ path: |
182
+ playwright-report
183
+ test-results
184
+ if-no-files-found: error
185
+ retention-days: 7
186
+
187
+ lighthouse:
188
+ name: Lighthouse budgets
189
+ runs-on: ubuntu-latest
190
+
191
+ permissions:
192
+ contents: read
193
+
194
+ steps:
195
+ - name: Checkout repository
196
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
197
+ with:
198
+ persist-credentials: false
199
+
200
+ - name: Setup project
201
+ uses: ./.github/actions/setup-project
202
+ with:
203
+ node-version: '26.x'
204
+
205
+ - name: Enforce Lighthouse budgets
206
+ run: npm run lighthouse:ci
207
+
208
+ - name: Upload Lighthouse reports
209
+ if: always()
210
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
211
+ with:
212
+ name: lighthouse-reports
213
+ path: .lighthouseci
214
+ include-hidden-files: true
215
+ if-no-files-found: error
216
+ retention-days: 7
217
+
218
+ project:
219
+ name: Project
220
+ needs:
221
+ - format
222
+ - spellcheck
223
+ - static-quality
224
+ - unit-tests
225
+ - build
226
+ - browser-tests
227
+ - lighthouse
228
+ runs-on: ubuntu-latest
229
+
230
+ permissions:
231
+ contents: read
232
+
233
+ steps:
234
+ - name: Confirm project checks
235
+ run: echo "All project checks passed."
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: Security Analysis
3
+
4
+ on: # yamllint disable-line rule:truthy
5
+ pull_request:
6
+ branches:
7
+ - main
8
+ push:
9
+ branches:
10
+ - main
11
+ schedule:
12
+ - cron: '__RM_INDUSTRIES_FORGE_TEMPLATE_V1_SECURITY_SCHEDULE_MINUTE__ 5 * * 1'
13
+
14
+ permissions: {}
15
+
16
+ concurrency:
17
+ group: ${{ github.workflow }}-${{ github.ref }}
18
+ cancel-in-progress: true
19
+
20
+ jobs:
21
+ code-analysis:
22
+ name: Analyze (${{ matrix.language }})
23
+ runs-on: ubuntu-latest
24
+
25
+ permissions:
26
+ actions: read
27
+ contents: read
28
+ security-events: write
29
+
30
+ strategy:
31
+ fail-fast: false
32
+ matrix:
33
+ language:
34
+ - actions
35
+ - javascript-typescript
36
+
37
+ steps:
38
+ - name: Checkout repository
39
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
40
+ with:
41
+ persist-credentials: false
42
+
43
+ - name: Initialize CodeQL
44
+ uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
45
+ with:
46
+ build-mode: none
47
+ languages: ${{ matrix.language }}
48
+ queries: security-extended,security-and-quality
49
+
50
+ - name: Perform CodeQL analysis
51
+ uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
52
+
53
+ dependency-review:
54
+ name: Review dependencies
55
+ if: github.event_name == 'pull_request'
56
+ runs-on: ubuntu-latest
57
+
58
+ permissions:
59
+ contents: read
60
+
61
+ steps:
62
+ - name: Checkout repository
63
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
64
+ with:
65
+ persist-credentials: false
66
+
67
+ - name: Review dependency changes
68
+ uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5
69
+ with:
70
+ fail-on-severity: high
@@ -55,6 +55,48 @@ the individual scripts when working offline. CI may execute independent stages
55
55
  in parallel, but a project generated by the packed Forge initializer must pass
56
56
  the documented `npm run quality` command as a single ordered pipeline.
57
57
 
58
+ ## Continuous integration
59
+
60
+ The generated `.github/workflows/project.yml` runs on pull requests targeting
61
+ `main` and on pushes to `main`. It separates static quality, coverage-enforced
62
+ unit tests, the validated production build, browser and accessibility tests,
63
+ and Lighthouse budgets so failures identify the affected gate directly. The
64
+ required `Project` result succeeds only after every release-blocking job passes.
65
+
66
+ The workflow installs dependencies with `npm ci`, caches npm downloads using
67
+ the lockfile, cancels superseded runs on the same Git reference, and grants only
68
+ read access to repository contents. Pull requests receive no secrets or write
69
+ permissions. Coverage, production builds, Lighthouse reports, and browser
70
+ failure evidence are retained for seven days. All third-party actions use
71
+ immutable commit pins.
72
+
73
+ Security automation is included alongside project CI. CodeQL analyzes the
74
+ project's TypeScript and GitHub Actions on pull requests, pushes to `main`, and
75
+ a weekly schedule. Dependency review rejects pull requests that introduce
76
+ known high- or critical-severity vulnerabilities. A separate scheduled
77
+ automation workflow validates workflow syntax and scans GitHub Actions with
78
+ Zizmor. Zizmor uploads its findings to GitHub code scanning for review in the
79
+ Security tab without failing the workflow solely because it found an issue.
80
+ The job receives `security-events: write` only for that upload; all other access
81
+ remains read-only. Repositories can make selected code-scanning severities
82
+ merge-blocking later through their ruleset without changing the workflow.
83
+
84
+ Dependabot checks npm and GitHub Actions weekly. Minor and patch npm updates are
85
+ grouped by production or development scope, while major updates remain separate
86
+ for deliberate review. Updates use cooldown periods to avoid adopting newly
87
+ released versions immediately. Because pre-1.0 Sveltia minor releases may be
88
+ breaking, `@sveltia/cms` updates are kept separate from generic production
89
+ dependency groups. Its pull requests must satisfy the compatibility line
90
+ declared by `@rm-industries/content-model`; moving to a later minor requires a
91
+ tested content-model release first. No version is ignored, security updates
92
+ remain enabled, and every update must pass the complete project and security
93
+ workflows before merging.
94
+
95
+ GitHub chooses the Dependabot run times, distributing update checks across its
96
+ available schedule. Forge derives distinct weekly CodeQL and workflow-validation
97
+ minutes from the generated package name. This keeps generation reproducible
98
+ while preventing every Forge project from starting scheduled work simultaneously.
99
+
58
100
  Playwright builds the site and runs Chromium against Astro's production preview,
59
101
  not the development server. Its browser coverage exercises desktop and mobile
60
102
  navigation, article listing/detail flows, article pagination, light and dark
@@ -18,5 +18,6 @@ export default defineConfig({
18
18
  'Sveltia',
19
19
  'unreviewed',
20
20
  'WCAG',
21
+ 'Zizmor',
21
22
  ],
22
23
  });
@@ -12,8 +12,8 @@
12
12
  "@astrojs/sitemap": "^3.7.3",
13
13
  "@fontsource/fira-code": "^5.3.0",
14
14
  "@fontsource/fira-sans": "^5.3.0",
15
- "@rm-industries/content-model": "0.1.0-alpha.0",
16
- "@sveltia/cms": "0.193.2",
15
+ "@rm-industries/content-model": "0.2.0-alpha.0",
16
+ "@sveltia/cms": "0.203.2",
17
17
  "astro": "^7.2.3"
18
18
  },
19
19
  "devDependencies": {
@@ -3859,18 +3859,6 @@
3859
3859
  "node": ">= 8"
3860
3860
  }
3861
3861
  },
3862
- "node_modules/@opentelemetry/api": {
3863
- "version": "1.9.1",
3864
- "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz",
3865
- "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==",
3866
- "dev": true,
3867
- "license": "Apache-2.0",
3868
- "optional": true,
3869
- "peer": true,
3870
- "engines": {
3871
- "node": ">=8.0.0"
3872
- }
3873
- },
3874
3862
  "node_modules/@oslojs/encoding": {
3875
3863
  "version": "1.1.0",
3876
3864
  "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz",
@@ -5306,16 +5294,16 @@
5306
5294
  }
5307
5295
  },
5308
5296
  "node_modules/@rm-industries/content-model": {
5309
- "version": "0.1.0-alpha.0",
5310
- "resolved": "https://registry.npmjs.org/@rm-industries/content-model/-/content-model-0.1.0-alpha.0.tgz",
5311
- "integrity": "sha512-qhrfTL0zWiqxyPXSZT5cM2gXw+B7ZrA5zz/mtsVdOB/8JwrNhAySok27KkyyTvPYacUFJLEEmDH7+d/SGpNlQw==",
5297
+ "version": "0.2.0-alpha.0",
5298
+ "resolved": "https://registry.npmjs.org/@rm-industries/content-model/-/content-model-0.2.0-alpha.0.tgz",
5299
+ "integrity": "sha512-DC5MkcUQ4iYIO40mFwNzlawkDEy2eleba8Ui48Xg9g5B6YWzEao6Mqu8FvEa923iVw127t1ZskhZEEh1+47njA==",
5312
5300
  "license": "MIT",
5313
5301
  "engines": {
5314
5302
  "node": "^22.22.2 || ^24.0.0 || ^26.0.0",
5315
5303
  "npm": "^10.9.0 || ^11.0.0"
5316
5304
  },
5317
5305
  "peerDependencies": {
5318
- "@sveltia/cms": ">=0.193.2 <0.194.0",
5306
+ "@sveltia/cms": ">=0.203.2 <0.204.0",
5319
5307
  "astro": "^7.2.3"
5320
5308
  },
5321
5309
  "peerDependenciesMeta": {
@@ -5760,9 +5748,9 @@
5760
5748
  "license": "MIT"
5761
5749
  },
5762
5750
  "node_modules/@sveltia/cms": {
5763
- "version": "0.193.2",
5764
- "resolved": "https://registry.npmjs.org/@sveltia/cms/-/cms-0.193.2.tgz",
5765
- "integrity": "sha512-k8uAy6Kb98P8VHGfGBXbM5/TxLv5YvImKdCnlb4DliwRr3QBYgcSAz/KHA7trSbSnJfXdcjNGOgYruBkrcWBxw==",
5751
+ "version": "0.203.2",
5752
+ "resolved": "https://registry.npmjs.org/@sveltia/cms/-/cms-0.203.2.tgz",
5753
+ "integrity": "sha512-uAqXTbPo52Im+ev/9wHZwe7KXE1Bt3b0DlSh+ijZ3IE+YKsjymrgtxSbvlr0pi4TWjPvrnmQDCp6okW5txCdCA==",
5766
5754
  "license": "MIT",
5767
5755
  "funding": {
5768
5756
  "type": "github",
@@ -31,15 +31,16 @@
31
31
  "test:unit": "vitest run",
32
32
  "quality": "npm run quality:core && npm run test:e2e && npm run lighthouse:ci",
33
33
  "quality:core": "npm run quality:static && npm run test:coverage && npm run build && npm run validate:build",
34
- "quality:static": "npm run format && npm run lint && npm run audit:unused && npm run typecheck && npm run astro:check && npm run audit"
34
+ "quality:static": "npm run format && npm run lint && npm run audit:unused && npm run typecheck && npm run astro:check && npm run audit",
35
+ "quality:static:ci": "npm run lint:code && npm run lint:css && npm run lint:markdown && npm run audit:unused && npm run typecheck && npm run astro:check && npm run audit"
35
36
  },
36
37
  "dependencies": {
37
38
  "@astrojs/rss": "^4.0.19",
38
39
  "@astrojs/sitemap": "^3.7.3",
39
40
  "@fontsource/fira-code": "^5.3.0",
40
41
  "@fontsource/fira-sans": "^5.3.0",
41
- "@rm-industries/content-model": "0.1.0-alpha.0",
42
- "@sveltia/cms": "0.193.2",
42
+ "@rm-industries/content-model": "0.2.0-alpha.0",
43
+ "@sveltia/cms": "0.203.2",
43
44
  "astro": "^7.2.3"
44
45
  },
45
46
  "devDependencies": {
@@ -69,5 +70,5 @@
69
70
  "node": "^22.22.2 || ^24.0.0 || ^26.0.0",
70
71
  "npm": "^10.9.0 || ^11.0.0"
71
72
  },
72
- "packageManager": "npm@11.18.0"
73
+ "packageManager": "npm@11.19.0"
73
74
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rm-industries/create-forge",
3
- "version": "0.4.0-beta.0",
3
+ "version": "0.4.0-beta.1",
4
4
  "description": "Create a content-driven Forge website",
5
5
  "keywords": [
6
6
  "astro",