create-m5kdev 0.31.0 → 0.31.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,17 +24,17 @@ function walkPackageJsonFiles(directory, out = []) {
24
24
  function readJson(filePath) {
25
25
  return JSON.parse(node_fs.default.readFileSync(filePath, "utf8"));
26
26
  }
27
- function collectConsumerDependencyNames(packageFiles) {
27
+ function collectConsumerDependencyNamesFromManifests(manifests) {
28
28
  const names = /* @__PURE__ */ new Set();
29
- for (const filePath of packageFiles) {
30
- const manifest = readJson(filePath);
31
- for (const field of DEPENDENCY_FIELDS) {
32
- const dependencies = manifest[field];
33
- for (const [name, specifier] of Object.entries(dependencies ?? {})) if (specifier.startsWith("catalog:") || name.startsWith("@m5kdev/") && specifier.startsWith("workspace:")) names.add(name);
34
- }
29
+ for (const manifest of manifests) for (const field of DEPENDENCY_FIELDS) {
30
+ const dependencies = manifest[field];
31
+ for (const [name, specifier] of Object.entries(dependencies ?? {})) if (specifier.startsWith("catalog:") || name.startsWith("@m5kdev/") && specifier.startsWith("workspace:")) names.add(name);
35
32
  }
36
33
  return [...names].sort((left, right) => left.localeCompare(right));
37
34
  }
35
+ function collectConsumerDependencyNames(packageFiles) {
36
+ return collectConsumerDependencyNamesFromManifests(packageFiles.map(readJson));
37
+ }
38
38
  function buildConsumerCatalog(options) {
39
39
  const workspacePath = node_path.default.join(options.repoRoot, "pnpm-workspace.yaml");
40
40
  const sourceCatalog = (0, yaml.parse)(node_fs.default.readFileSync(workspacePath, "utf8")).catalog ?? {};
@@ -131,6 +131,7 @@ function mergeManagedCatalog(options) {
131
131
  exports.assertCatalogKeys = assertCatalogKeys;
132
132
  exports.buildConsumerCatalog = buildConsumerCatalog;
133
133
  exports.collectConsumerDependencyNames = collectConsumerDependencyNames;
134
+ exports.collectConsumerDependencyNamesFromManifests = collectConsumerDependencyNamesFromManifests;
134
135
  exports.mergeManagedCatalog = mergeManagedCatalog;
135
136
  exports.readCatalog = readCatalog;
136
137
  exports.renderConsumerWorkspace = renderConsumerWorkspace;
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.cjs","names":["fs","path"],"sources":["../../src/catalog.ts"],"sourcesContent":["import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { parse, parseDocument, stringify } from \"yaml\";\r\n\r\nconst DEPENDENCY_FIELDS = [\r\n \"dependencies\",\r\n \"devDependencies\",\r\n \"peerDependencies\",\r\n \"optionalDependencies\",\r\n] as const;\r\n\r\nexport type ConsumerCatalog = Record<string, string>;\r\n\r\nexport function walkPackageJsonFiles(directory: string, out: string[] = []): string[] {\r\n for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {\r\n if (entry.name === \"node_modules\" || entry.name === \".git\") continue;\r\n const entryPath = path.join(directory, entry.name);\r\n if (entry.isDirectory()) {\r\n walkPackageJsonFiles(entryPath, out);\r\n } else if (entry.isFile() && entry.name === \"package.json\") {\r\n out.push(entryPath);\r\n }\r\n }\r\n return out;\r\n}\r\n\r\nfunction readJson(filePath: string): Record<string, unknown> {\r\n return JSON.parse(fs.readFileSync(filePath, \"utf8\")) as Record<string, unknown>;\r\n}\r\n\r\nexport function collectConsumerDependencyNames(packageFiles: readonly string[]): string[] {\r\n const names = new Set<string>();\r\n\r\n for (const filePath of packageFiles) {\r\n const manifest = readJson(filePath);\r\n for (const field of DEPENDENCY_FIELDS) {\r\n const dependencies = manifest[field] as Record<string, string> | undefined;\r\n for (const [name, specifier] of Object.entries(dependencies ?? {})) {\r\n if (\r\n specifier.startsWith(\"catalog:\") ||\r\n (name.startsWith(\"@m5kdev/\") && specifier.startsWith(\"workspace:\"))\r\n ) {\r\n names.add(name);\r\n }\r\n }\r\n }\r\n }\r\n\r\n return [...names].sort((left, right) => left.localeCompare(right));\r\n}\r\n\r\nexport function buildConsumerCatalog(options: {\r\n repoRoot: string;\r\n starterDirectory: string;\r\n rootTemplatesDirectory: string;\r\n}): ConsumerCatalog {\r\n const workspacePath = path.join(options.repoRoot, \"pnpm-workspace.yaml\");\r\n const workspace = parse(fs.readFileSync(workspacePath, \"utf8\")) as {\r\n catalog?: Record<string, string | number>;\r\n };\r\n const sourceCatalog = workspace.catalog ?? {};\r\n const packageFiles = [\r\n ...walkPackageJsonFiles(options.starterDirectory),\r\n path.join(options.rootTemplatesDirectory, \"package.json.tpl\"),\r\n ];\r\n const names = collectConsumerDependencyNames(packageFiles);\r\n const catalog: ConsumerCatalog = {};\r\n const missing: string[] = [];\r\n\r\n for (const name of names) {\r\n if (name.startsWith(\"@m5kdev/\")) {\r\n const packageName = name.slice(\"@m5kdev/\".length);\r\n const packagePath = path.join(options.repoRoot, \"packages\", packageName, \"package.json\");\r\n if (!fs.existsSync(packagePath)) {\r\n missing.push(name);\r\n continue;\r\n }\r\n const packageManifest = readJson(packagePath);\r\n if (typeof packageManifest.version !== \"string\") {\r\n missing.push(name);\r\n continue;\r\n }\r\n catalog[name] = packageManifest.version;\r\n continue;\r\n }\r\n\r\n const version = sourceCatalog[name];\r\n if (version === undefined) {\r\n missing.push(name);\r\n continue;\r\n }\r\n catalog[name] = String(version);\r\n }\r\n\r\n if (missing.length > 0) {\r\n throw new Error(`Missing consumer catalog versions for: ${missing.join(\", \")}`);\r\n }\r\n\r\n return catalog;\r\n}\r\n\r\nexport function renderConsumerWorkspace(source: string, catalog: ConsumerCatalog): string {\r\n const workspace = parse(source) as Record<string, unknown>;\r\n workspace.catalog = Object.fromEntries(\r\n Object.entries(catalog).sort(([left], [right]) => left.localeCompare(right))\r\n );\r\n return stringify(workspace, { lineWidth: 0 });\r\n}\r\n\r\nexport function readCatalog(source: string): ConsumerCatalog {\r\n const workspace = parse(source) as { catalog?: Record<string, string | number> };\r\n return Object.fromEntries(\r\n Object.entries(workspace.catalog ?? {}).map(([name, version]) => [name, String(version)])\r\n );\r\n}\r\n\r\nexport function assertCatalogKeys(\r\n catalog: ConsumerCatalog,\r\n expectedNames: readonly string[],\r\n label = \"consumer catalog\"\r\n): void {\r\n const expected = new Set(expectedNames);\r\n const missing = expectedNames.filter((name) => !(name in catalog));\r\n const obsolete = Object.keys(catalog).filter((name) => !expected.has(name));\r\n if (missing.length > 0 || obsolete.length > 0) {\r\n const details = [\r\n missing.length > 0 ? `missing: ${missing.join(\", \")}` : undefined,\r\n obsolete.length > 0 ? `obsolete: ${obsolete.join(\", \")}` : undefined,\r\n ].filter(Boolean);\r\n throw new Error(`Invalid ${label} (${details.join(\"; \")})`);\r\n }\r\n}\r\n\r\nexport interface CatalogMergeConflict {\r\n name: string;\r\n base?: string;\r\n local?: string;\r\n target?: string;\r\n}\r\n\r\nexport function mergeManagedCatalog(options: {\r\n source: string;\r\n base: ConsumerCatalog;\r\n target: ConsumerCatalog;\r\n}): { source: string; changed: boolean; conflicts: CatalogMergeConflict[] } {\r\n const document = parseDocument(options.source);\r\n const local = readCatalog(options.source);\r\n const conflicts: CatalogMergeConflict[] = [];\r\n const names = new Set([...Object.keys(options.base), ...Object.keys(options.target)]);\r\n\r\n for (const name of [...names].sort((left, right) => left.localeCompare(right))) {\r\n const base = options.base[name];\r\n const current = local[name];\r\n const target = options.target[name];\r\n\r\n if (base === undefined) {\r\n if (current === undefined) document.setIn([\"catalog\", name], target);\r\n else if (current !== target) conflicts.push({ name, local: current, target });\r\n continue;\r\n }\r\n if (target === undefined) {\r\n if (current === base) document.deleteIn([\"catalog\", name]);\r\n else if (current !== undefined) conflicts.push({ name, base, local: current });\r\n continue;\r\n }\r\n if (current === base) document.setIn([\"catalog\", name], target);\r\n else if (current !== target && target !== base) {\r\n conflicts.push({ name, base, local: current, target });\r\n }\r\n }\r\n\r\n const merged = document.toString({ lineWidth: 0 });\r\n return { source: merged, changed: merged !== options.source, conflicts };\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"catalog.cjs","names":["fs","path"],"sources":["../../src/catalog.ts"],"sourcesContent":["import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { parse, parseDocument, stringify } from \"yaml\";\r\n\r\nconst DEPENDENCY_FIELDS = [\r\n \"dependencies\",\r\n \"devDependencies\",\r\n \"peerDependencies\",\r\n \"optionalDependencies\",\r\n] as const;\r\n\r\nexport type ConsumerCatalog = Record<string, string>;\r\n\r\nexport function walkPackageJsonFiles(directory: string, out: string[] = []): string[] {\r\n for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {\r\n if (entry.name === \"node_modules\" || entry.name === \".git\") continue;\r\n const entryPath = path.join(directory, entry.name);\r\n if (entry.isDirectory()) {\r\n walkPackageJsonFiles(entryPath, out);\r\n } else if (entry.isFile() && entry.name === \"package.json\") {\r\n out.push(entryPath);\r\n }\r\n }\r\n return out;\r\n}\r\n\r\nfunction readJson(filePath: string): Record<string, unknown> {\r\n return JSON.parse(fs.readFileSync(filePath, \"utf8\")) as Record<string, unknown>;\r\n}\r\n\r\nexport function collectConsumerDependencyNamesFromManifests(\r\n manifests: readonly Record<string, unknown>[]\r\n): string[] {\r\n const names = new Set<string>();\r\n\r\n for (const manifest of manifests) {\r\n for (const field of DEPENDENCY_FIELDS) {\r\n const dependencies = manifest[field] as Record<string, string> | undefined;\r\n for (const [name, specifier] of Object.entries(dependencies ?? {})) {\r\n if (\r\n specifier.startsWith(\"catalog:\") ||\r\n (name.startsWith(\"@m5kdev/\") && specifier.startsWith(\"workspace:\"))\r\n ) {\r\n names.add(name);\r\n }\r\n }\r\n }\r\n }\r\n\r\n return [...names].sort((left, right) => left.localeCompare(right));\r\n}\r\n\r\nexport function collectConsumerDependencyNames(packageFiles: readonly string[]): string[] {\r\n return collectConsumerDependencyNamesFromManifests(packageFiles.map(readJson));\r\n}\r\n\r\nexport function buildConsumerCatalog(options: {\r\n repoRoot: string;\r\n starterDirectory: string;\r\n rootTemplatesDirectory: string;\r\n}): ConsumerCatalog {\r\n const workspacePath = path.join(options.repoRoot, \"pnpm-workspace.yaml\");\r\n const workspace = parse(fs.readFileSync(workspacePath, \"utf8\")) as {\r\n catalog?: Record<string, string | number>;\r\n };\r\n const sourceCatalog = workspace.catalog ?? {};\r\n const packageFiles = [\r\n ...walkPackageJsonFiles(options.starterDirectory),\r\n path.join(options.rootTemplatesDirectory, \"package.json.tpl\"),\r\n ];\r\n const names = collectConsumerDependencyNames(packageFiles);\r\n const catalog: ConsumerCatalog = {};\r\n const missing: string[] = [];\r\n\r\n for (const name of names) {\r\n if (name.startsWith(\"@m5kdev/\")) {\r\n const packageName = name.slice(\"@m5kdev/\".length);\r\n const packagePath = path.join(options.repoRoot, \"packages\", packageName, \"package.json\");\r\n if (!fs.existsSync(packagePath)) {\r\n missing.push(name);\r\n continue;\r\n }\r\n const packageManifest = readJson(packagePath);\r\n if (typeof packageManifest.version !== \"string\") {\r\n missing.push(name);\r\n continue;\r\n }\r\n catalog[name] = packageManifest.version;\r\n continue;\r\n }\r\n\r\n const version = sourceCatalog[name];\r\n if (version === undefined) {\r\n missing.push(name);\r\n continue;\r\n }\r\n catalog[name] = String(version);\r\n }\r\n\r\n if (missing.length > 0) {\r\n throw new Error(`Missing consumer catalog versions for: ${missing.join(\", \")}`);\r\n }\r\n\r\n return catalog;\r\n}\r\n\r\nexport function renderConsumerWorkspace(source: string, catalog: ConsumerCatalog): string {\r\n const workspace = parse(source) as Record<string, unknown>;\r\n workspace.catalog = Object.fromEntries(\r\n Object.entries(catalog).sort(([left], [right]) => left.localeCompare(right))\r\n );\r\n return stringify(workspace, { lineWidth: 0 });\r\n}\r\n\r\nexport function readCatalog(source: string): ConsumerCatalog {\r\n const workspace = parse(source) as { catalog?: Record<string, string | number> };\r\n return Object.fromEntries(\r\n Object.entries(workspace.catalog ?? {}).map(([name, version]) => [name, String(version)])\r\n );\r\n}\r\n\r\nexport function assertCatalogKeys(\r\n catalog: ConsumerCatalog,\r\n expectedNames: readonly string[],\r\n label = \"consumer catalog\"\r\n): void {\r\n const expected = new Set(expectedNames);\r\n const missing = expectedNames.filter((name) => !(name in catalog));\r\n const obsolete = Object.keys(catalog).filter((name) => !expected.has(name));\r\n if (missing.length > 0 || obsolete.length > 0) {\r\n const details = [\r\n missing.length > 0 ? `missing: ${missing.join(\", \")}` : undefined,\r\n obsolete.length > 0 ? `obsolete: ${obsolete.join(\", \")}` : undefined,\r\n ].filter(Boolean);\r\n throw new Error(`Invalid ${label} (${details.join(\"; \")})`);\r\n }\r\n}\r\n\r\nexport interface CatalogMergeConflict {\r\n name: string;\r\n base?: string;\r\n local?: string;\r\n target?: string;\r\n}\r\n\r\nexport function mergeManagedCatalog(options: {\r\n source: string;\r\n base: ConsumerCatalog;\r\n target: ConsumerCatalog;\r\n}): { source: string; changed: boolean; conflicts: CatalogMergeConflict[] } {\r\n const document = parseDocument(options.source);\r\n const local = readCatalog(options.source);\r\n const conflicts: CatalogMergeConflict[] = [];\r\n const names = new Set([...Object.keys(options.base), ...Object.keys(options.target)]);\r\n\r\n for (const name of [...names].sort((left, right) => left.localeCompare(right))) {\r\n const base = options.base[name];\r\n const current = local[name];\r\n const target = options.target[name];\r\n\r\n if (base === undefined) {\r\n if (current === undefined) document.setIn([\"catalog\", name], target);\r\n else if (current !== target) conflicts.push({ name, local: current, target });\r\n continue;\r\n }\r\n if (target === undefined) {\r\n if (current === base) document.deleteIn([\"catalog\", name]);\r\n else if (current !== undefined) conflicts.push({ name, base, local: current });\r\n continue;\r\n }\r\n if (current === base) document.setIn([\"catalog\", name], target);\r\n else if (current !== target && target !== base) {\r\n conflicts.push({ name, base, local: current, target });\r\n }\r\n }\r\n\r\n const merged = document.toString({ lineWidth: 0 });\r\n return { source: merged, changed: merged !== options.source, conflicts };\r\n}\r\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  //#region src/catalog.d.ts
2
2
  type ConsumerCatalog = Record<string, string>;
3
3
  declare function walkPackageJsonFiles(directory: string, out?: string[]): string[];
4
+ declare function collectConsumerDependencyNamesFromManifests(manifests: readonly Record<string, unknown>[]): string[];
4
5
  declare function collectConsumerDependencyNames(packageFiles: readonly string[]): string[];
5
6
  declare function buildConsumerCatalog(options: {
6
7
  repoRoot: string;
@@ -26,5 +27,5 @@ declare function mergeManagedCatalog(options: {
26
27
  conflicts: CatalogMergeConflict[];
27
28
  };
28
29
  //#endregion
29
- export { CatalogMergeConflict, ConsumerCatalog, assertCatalogKeys, buildConsumerCatalog, collectConsumerDependencyNames, mergeManagedCatalog, readCatalog, renderConsumerWorkspace, walkPackageJsonFiles };
30
+ export { CatalogMergeConflict, ConsumerCatalog, assertCatalogKeys, buildConsumerCatalog, collectConsumerDependencyNames, collectConsumerDependencyNamesFromManifests, mergeManagedCatalog, readCatalog, renderConsumerWorkspace, walkPackageJsonFiles };
30
31
  //# sourceMappingURL=catalog.d.cts.map
@@ -121,7 +121,8 @@ async function diagnoseManagedRepo(options) {
121
121
  code: "CATALOG_VERSION_MISMATCH",
122
122
  severity: "error",
123
123
  message: `${name} is ${actualCatalog[name]}, expected managed version ${expected}.`,
124
- path: "pnpm-workspace.yaml"
124
+ path: "pnpm-workspace.yaml",
125
+ suggestion: `Set ${name} to ${expected} in pnpm-workspace.yaml.`
125
126
  });
126
127
  }
127
128
  const appsDirectory = node_path.default.join(options.repoRoot, "apps");
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.cjs","names":["execFile","fs","readManagedState","createDiagnosticReport","getCliVersion","loadTemplateManifest","getTemplateRoot","findRepositorySymlink","path","readCatalog","walkPackageJsonFiles","collectConsumerDependencyNames","sha256","getPendingMigrations","runMigrationValidators","ChangeSet"],"sources":["../../src/doctor.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\r\nimport fs from \"node:fs/promises\";\r\nimport path from \"node:path\";\r\nimport { promisify } from \"node:util\";\r\nimport semver from \"semver\";\r\nimport { collectConsumerDependencyNames, readCatalog, walkPackageJsonFiles } from \"./catalog\";\r\nimport { ChangeSet, findRepositorySymlink } from \"./changes\";\r\nimport { TEMPLATE_NAME } from \"./constants\";\r\nimport { createDiagnosticReport, type Diagnostic, type DiagnosticReport } from \"./diagnostics\";\r\nimport { getPendingMigrations, runMigrationValidators } from \"./migrations/registry\";\r\nimport { getTemplateRoot } from \"./paths\";\r\nimport { getCliVersion, type ManagedState, readManagedState, sha256 } from \"./state\";\r\nimport { loadTemplateManifest } from \"./template\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\nconst FULL_COMMAND_TIMEOUT_MS = 10 * 60 * 1000;\r\n\r\nexport interface DoctorOptions {\r\n repoRoot: string;\r\n full?: boolean;\r\n state?: ManagedState;\r\n}\r\n\r\nasync function exists(filePath: string): Promise<boolean> {\r\n return fs\r\n .access(filePath)\r\n .then(() => true)\r\n .catch(() => false);\r\n}\r\n\r\nexport async function diagnoseManagedRepo(options: DoctorOptions): Promise<DiagnosticReport> {\r\n const diagnostics: Diagnostic[] = [];\r\n let state = options.state;\r\n if (!state) {\r\n try {\r\n state = await readManagedState(options.repoRoot);\r\n } catch (error) {\r\n diagnostics.push({\r\n code: \"STATE_INVALID\",\r\n severity: \"error\",\r\n message: error instanceof Error ? error.message : \"Unable to read .m5kdev.json.\",\r\n path: \".m5kdev.json\",\r\n suggestion: \"Run m5kdev init to establish a managed baseline.\",\r\n });\r\n return createDiagnosticReport(diagnostics);\r\n }\r\n }\r\n\r\n const cliVersion = getCliVersion();\r\n if (state.template.name !== TEMPLATE_NAME) {\r\n diagnostics.push({\r\n code: \"STATE_TEMPLATE_UNKNOWN\",\r\n severity: \"error\",\r\n message: `Unsupported template name: ${state.template.name}`,\r\n path: \".m5kdev.json\",\r\n });\r\n }\r\n if (!semver.valid(state.template.version)) {\r\n diagnostics.push({\r\n code: \"STATE_VERSION_INVALID\",\r\n severity: \"error\",\r\n message: `Invalid template version: ${state.template.version}`,\r\n path: \".m5kdev.json\",\r\n });\r\n } else if (semver.gt(state.template.version, cliVersion)) {\r\n diagnostics.push({\r\n code: \"CLI_TOO_OLD\",\r\n severity: \"error\",\r\n message: `This repository is managed by ${state.template.version}, but the running CLI is ${cliVersion}.`,\r\n suggestion: `Run pnpm dlx create-m5kdev@${state.template.version} doctor.`,\r\n });\r\n } else if (semver.lt(state.template.version, cliVersion)) {\r\n diagnostics.push({\r\n code: \"UPDATE_AVAILABLE\",\r\n severity: \"warning\",\r\n message: `Template ${cliVersion} is available; this repository is at ${state.template.version}.`,\r\n suggestion: `Preview it with pnpm dlx create-m5kdev@${cliVersion} update --dry-run.`,\r\n });\r\n }\r\n\r\n const manifest = loadTemplateManifest(getTemplateRoot());\r\n const seenFeatures = new Set<string>();\r\n for (const feature of state.template.features) {\r\n if (seenFeatures.has(feature)) {\r\n diagnostics.push({\r\n code: \"FEATURE_DUPLICATE\",\r\n severity: \"error\",\r\n message: `Feature ${feature} is listed more than once.`,\r\n path: \".m5kdev.json\",\r\n });\r\n } else if (!(feature in manifest.features)) {\r\n diagnostics.push({\r\n code: \"FEATURE_UNKNOWN\",\r\n severity: \"error\",\r\n message: `Unknown managed feature: ${feature}`,\r\n path: \".m5kdev.json\",\r\n });\r\n }\r\n seenFeatures.add(feature);\r\n }\r\n const requiredPaths = new Set(manifest.requiredPaths);\r\n if (state.template.features.includes(\"webapp\")) requiredPaths.add(\"apps/webapp/package.json\");\r\n if (state.template.features.includes(\"expo\")) requiredPaths.add(\"apps/expo/package.json\");\r\n if (state.template.features.includes(\"test-harness\")) requiredPaths.add(\"apps/e2e/package.json\");\r\n for (const relativePath of requiredPaths) {\r\n const symlink = await findRepositorySymlink(options.repoRoot, relativePath);\r\n if (symlink) {\r\n diagnostics.push({\r\n code: \"MANAGED_PATH_SYMLINK\",\r\n severity: \"error\",\r\n message: `Required managed path contains symbolic link ${symlink}.`,\r\n path: relativePath,\r\n });\r\n } else if (!(await exists(path.join(options.repoRoot, relativePath)))) {\r\n diagnostics.push({\r\n code: \"REQUIRED_PATH_MISSING\",\r\n severity: \"error\",\r\n message: \"Required managed path is missing.\",\r\n path: relativePath,\r\n suggestion:\r\n \"Restore the path or correct the enabled features before initializing/updating.\",\r\n });\r\n }\r\n }\r\n\r\n const workspaceRelativePath = \"pnpm-workspace.yaml\";\r\n const workspacePath = path.join(options.repoRoot, workspaceRelativePath);\r\n const workspaceSymlink = await findRepositorySymlink(options.repoRoot, workspaceRelativePath);\r\n if (!workspaceSymlink && (await exists(workspacePath))) {\r\n try {\r\n const actualCatalog = readCatalog(await fs.readFile(workspacePath, \"utf8\"));\r\n for (const [name, expected] of Object.entries(state.catalog)) {\r\n if (!semver.validRange(expected)) {\r\n diagnostics.push({\r\n code: \"CATALOG_VERSION_INVALID\",\r\n severity: \"error\",\r\n message: `${name} has an invalid managed SemVer range: ${expected}.`,\r\n path: \".m5kdev.json\",\r\n });\r\n }\r\n if (!(name in actualCatalog)) {\r\n diagnostics.push({\r\n code: \"CATALOG_ENTRY_MISSING\",\r\n severity: \"error\",\r\n message: `Managed catalog entry ${name} is missing.`,\r\n path: \"pnpm-workspace.yaml\",\r\n suggestion: `Restore ${name}: ${expected}.`,\r\n });\r\n } else if (actualCatalog[name] !== expected) {\r\n diagnostics.push({\r\n code: \"CATALOG_VERSION_MISMATCH\",\r\n severity: \"error\",\r\n message: `${name} is ${actualCatalog[name]}, expected managed version ${expected}.`,\r\n path: \"pnpm-workspace.yaml\",\r\n });\r\n }\r\n }\r\n\r\n const appsDirectory = path.join(options.repoRoot, \"apps\");\r\n const packageFiles = [path.join(options.repoRoot, \"package.json\")];\r\n if (await exists(appsDirectory)) packageFiles.push(...walkPackageJsonFiles(appsDirectory));\r\n for (const dependency of collectConsumerDependencyNames(packageFiles)) {\r\n if (!(dependency in actualCatalog)) {\r\n diagnostics.push({\r\n code: \"CATALOG_REFERENCE_MISSING\",\r\n severity: \"error\",\r\n message: `${dependency} uses catalog: but has no catalog entry.`,\r\n path: \"pnpm-workspace.yaml\",\r\n });\r\n }\r\n }\r\n } catch (error) {\r\n diagnostics.push({\r\n code: \"CATALOG_INVALID\",\r\n severity: \"error\",\r\n message: error instanceof Error ? error.message : \"Unable to parse the consumer catalog.\",\r\n path: \"pnpm-workspace.yaml\",\r\n });\r\n }\r\n }\r\n\r\n for (const [relativePath, baseline] of Object.entries(state.files)) {\r\n if (baseline.policy === \"ignore\") continue;\r\n const filePath = path.join(options.repoRoot, relativePath);\r\n const symlink = await findRepositorySymlink(options.repoRoot, relativePath);\r\n if (symlink) {\r\n diagnostics.push({\r\n code: \"MANAGED_PATH_SYMLINK\",\r\n severity: \"error\",\r\n message: `Managed path contains symbolic link ${symlink}.`,\r\n path: relativePath,\r\n });\r\n continue;\r\n }\r\n const stat = await fs.lstat(filePath).catch(() => null);\r\n if (!stat) {\r\n if (baseline.policy === \"ensure\") {\r\n diagnostics.push({\r\n code: \"ENSURED_PATH_MISSING\",\r\n severity: \"error\",\r\n message: \"An ensured template path is missing.\",\r\n path: relativePath,\r\n });\r\n }\r\n continue;\r\n }\r\n if (!stat.isFile()) continue;\r\n const content = await fs.readFile(filePath);\r\n if (baseline.sha256 && sha256(content) !== baseline.sha256) {\r\n diagnostics.push({\r\n code: \"TEMPLATE_CUSTOMIZED\",\r\n severity: \"info\",\r\n message: \"Managed template content has local customizations.\",\r\n path: relativePath,\r\n });\r\n }\r\n if (baseline.kind === \"text\") {\r\n const source = content.toString(\"utf8\");\r\n if (\r\n /\\{\\{(?:APP_NAME|APP_DESCRIPTION|APP_SLUG|PACKAGE_SCOPE|BETTER_AUTH_SECRET)\\}\\}/.test(\r\n source\r\n )\r\n ) {\r\n diagnostics.push({\r\n code: \"UNRESOLVED_TEMPLATE_TOKEN\",\r\n severity: \"error\",\r\n message: \"Unresolved template token found.\",\r\n path: relativePath,\r\n });\r\n }\r\n if (/^(<{7}|={7}|>{7})(?: |$)/m.test(source)) {\r\n diagnostics.push({\r\n code: \"MERGE_CONFLICT_MARKER\",\r\n severity: \"error\",\r\n message: \"Unresolved merge conflict marker found.\",\r\n path: relativePath,\r\n });\r\n }\r\n }\r\n }\r\n\r\n const pending = getPendingMigrations(state, cliVersion);\r\n for (const migration of pending) {\r\n diagnostics.push({\r\n code: \"MIGRATION_PENDING\",\r\n severity: \"error\",\r\n message: migration.description,\r\n migrationId: migration.id,\r\n guide: migration.guide,\r\n suggestion: `Run pnpm dlx create-m5kdev@${cliVersion} update.`,\r\n });\r\n }\r\n diagnostics.push(\r\n ...(await runMigrationValidators({\r\n repoRoot: options.repoRoot,\r\n state,\r\n changes: new ChangeSet(options.repoRoot),\r\n }))\r\n );\r\n\r\n if (options.full) {\r\n const packagePath = path.join(options.repoRoot, \"package.json\");\r\n let packageManifest: { scripts?: Record<string, string> } | undefined;\r\n try {\r\n packageManifest = JSON.parse(await fs.readFile(packagePath, \"utf8\")) as {\r\n scripts?: Record<string, string>;\r\n };\r\n } catch (error) {\r\n diagnostics.push({\r\n code: \"PACKAGE_JSON_UNREADABLE\",\r\n severity: \"error\",\r\n message:\r\n error instanceof Error\r\n ? `Unable to read package.json: ${error.message}`\r\n : \"Unable to read package.json.\",\r\n path: \"package.json\",\r\n });\r\n }\r\n if (packageManifest) {\r\n for (const script of [\"check-types\", \"lint\", \"build\"] as const) {\r\n if (!packageManifest.scripts?.[script]) continue;\r\n try {\r\n await execFileAsync(\"pnpm\", [script], {\r\n cwd: options.repoRoot,\r\n env: process.env,\r\n timeout: FULL_COMMAND_TIMEOUT_MS,\r\n });\r\n } catch (error) {\r\n diagnostics.push({\r\n code: `COMMAND_${script.replace(\"-\", \"_\").toUpperCase()}_FAILED`,\r\n severity: \"error\",\r\n message: `${script} failed: ${error instanceof Error ? error.message : String(error)}`,\r\n path: \"package.json\",\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n return createDiagnosticReport(diagnostics);\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAcA,MAAM,iBAAA,GAAA,UAAA,WAA0BA,mBAAAA,SAAS;AACzC,MAAM,0BAA0B,MAAU;AAQ1C,eAAe,OAAO,UAAoC;AACxD,QAAOC,iBAAAA,QACJ,OAAO,SAAS,CAChB,WAAW,KAAK,CAChB,YAAY,MAAM;;AAGvB,eAAsB,oBAAoB,SAAmD;CAC3F,MAAM,cAA4B,EAAE;CACpC,IAAI,QAAQ,QAAQ;AACpB,KAAI,CAAC,MACH,KAAI;AACF,UAAQ,MAAMC,kBAAAA,iBAAiB,QAAQ,SAAS;UACzC,OAAO;AACd,cAAY,KAAK;GACf,MAAM;GACN,UAAU;GACV,SAAS,iBAAiB,QAAQ,MAAM,UAAU;GAClD,MAAM;GACN,YAAY;GACb,CAAC;AACF,SAAOC,wBAAAA,uBAAuB,YAAY;;CAI9C,MAAM,aAAaC,kBAAAA,eAAe;AAClC,KAAI,MAAM,SAAS,SAAA,cACjB,aAAY,KAAK;EACf,MAAM;EACN,UAAU;EACV,SAAS,8BAA8B,MAAM,SAAS;EACtD,MAAM;EACP,CAAC;AAEJ,KAAI,CAAC,OAAA,QAAO,MAAM,MAAM,SAAS,QAAQ,CACvC,aAAY,KAAK;EACf,MAAM;EACN,UAAU;EACV,SAAS,6BAA6B,MAAM,SAAS;EACrD,MAAM;EACP,CAAC;UACO,OAAA,QAAO,GAAG,MAAM,SAAS,SAAS,WAAW,CACtD,aAAY,KAAK;EACf,MAAM;EACN,UAAU;EACV,SAAS,iCAAiC,MAAM,SAAS,QAAQ,2BAA2B,WAAW;EACvG,YAAY,8BAA8B,MAAM,SAAS,QAAQ;EAClE,CAAC;UACO,OAAA,QAAO,GAAG,MAAM,SAAS,SAAS,WAAW,CACtD,aAAY,KAAK;EACf,MAAM;EACN,UAAU;EACV,SAAS,YAAY,WAAW,uCAAuC,MAAM,SAAS,QAAQ;EAC9F,YAAY,0CAA0C,WAAW;EAClE,CAAC;CAGJ,MAAM,WAAWC,qBAAAA,qBAAqBC,kBAAAA,iBAAiB,CAAC;CACxD,MAAM,+BAAe,IAAI,KAAa;AACtC,MAAK,MAAM,WAAW,MAAM,SAAS,UAAU;AAC7C,MAAI,aAAa,IAAI,QAAQ,CAC3B,aAAY,KAAK;GACf,MAAM;GACN,UAAU;GACV,SAAS,WAAW,QAAQ;GAC5B,MAAM;GACP,CAAC;WACO,EAAE,WAAW,SAAS,UAC/B,aAAY,KAAK;GACf,MAAM;GACN,UAAU;GACV,SAAS,4BAA4B;GACrC,MAAM;GACP,CAAC;AAEJ,eAAa,IAAI,QAAQ;;CAE3B,MAAM,gBAAgB,IAAI,IAAI,SAAS,cAAc;AACrD,KAAI,MAAM,SAAS,SAAS,SAAS,SAAS,CAAE,eAAc,IAAI,2BAA2B;AAC7F,KAAI,MAAM,SAAS,SAAS,SAAS,OAAO,CAAE,eAAc,IAAI,yBAAyB;AACzF,KAAI,MAAM,SAAS,SAAS,SAAS,eAAe,CAAE,eAAc,IAAI,wBAAwB;AAChG,MAAK,MAAM,gBAAgB,eAAe;EACxC,MAAM,UAAU,MAAMC,oBAAAA,sBAAsB,QAAQ,UAAU,aAAa;AAC3E,MAAI,QACF,aAAY,KAAK;GACf,MAAM;GACN,UAAU;GACV,SAAS,gDAAgD,QAAQ;GACjE,MAAM;GACP,CAAC;WACO,CAAE,MAAM,OAAOC,UAAAA,QAAK,KAAK,QAAQ,UAAU,aAAa,CAAC,CAClE,aAAY,KAAK;GACf,MAAM;GACN,UAAU;GACV,SAAS;GACT,MAAM;GACN,YACE;GACH,CAAC;;CAIN,MAAM,wBAAwB;CAC9B,MAAM,gBAAgBA,UAAAA,QAAK,KAAK,QAAQ,UAAU,sBAAsB;AAExE,KAAI,CADqB,MAAMD,oBAAAA,sBAAsB,QAAQ,UAAU,sBAAsB,IACnE,MAAM,OAAO,cAAc,CACnD,KAAI;EACF,MAAM,gBAAgBE,oBAAAA,YAAY,MAAMR,iBAAAA,QAAG,SAAS,eAAe,OAAO,CAAC;AAC3E,OAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,MAAM,QAAQ,EAAE;AAC5D,OAAI,CAAC,OAAA,QAAO,WAAW,SAAS,CAC9B,aAAY,KAAK;IACf,MAAM;IACN,UAAU;IACV,SAAS,GAAG,KAAK,wCAAwC,SAAS;IAClE,MAAM;IACP,CAAC;AAEJ,OAAI,EAAE,QAAQ,eACZ,aAAY,KAAK;IACf,MAAM;IACN,UAAU;IACV,SAAS,yBAAyB,KAAK;IACvC,MAAM;IACN,YAAY,WAAW,KAAK,IAAI,SAAS;IAC1C,CAAC;YACO,cAAc,UAAU,SACjC,aAAY,KAAK;IACf,MAAM;IACN,UAAU;IACV,SAAS,GAAG,KAAK,MAAM,cAAc,MAAM,6BAA6B,SAAS;IACjF,MAAM;IACP,CAAC;;EAIN,MAAM,gBAAgBO,UAAAA,QAAK,KAAK,QAAQ,UAAU,OAAO;EACzD,MAAM,eAAe,CAACA,UAAAA,QAAK,KAAK,QAAQ,UAAU,eAAe,CAAC;AAClE,MAAI,MAAM,OAAO,cAAc,CAAE,cAAa,KAAK,GAAGE,oBAAAA,qBAAqB,cAAc,CAAC;AAC1F,OAAK,MAAM,cAAcC,oBAAAA,+BAA+B,aAAa,CACnE,KAAI,EAAE,cAAc,eAClB,aAAY,KAAK;GACf,MAAM;GACN,UAAU;GACV,SAAS,GAAG,WAAW;GACvB,MAAM;GACP,CAAC;UAGC,OAAO;AACd,cAAY,KAAK;GACf,MAAM;GACN,UAAU;GACV,SAAS,iBAAiB,QAAQ,MAAM,UAAU;GAClD,MAAM;GACP,CAAC;;AAIN,MAAK,MAAM,CAAC,cAAc,aAAa,OAAO,QAAQ,MAAM,MAAM,EAAE;AAClE,MAAI,SAAS,WAAW,SAAU;EAClC,MAAM,WAAWH,UAAAA,QAAK,KAAK,QAAQ,UAAU,aAAa;EAC1D,MAAM,UAAU,MAAMD,oBAAAA,sBAAsB,QAAQ,UAAU,aAAa;AAC3E,MAAI,SAAS;AACX,eAAY,KAAK;IACf,MAAM;IACN,UAAU;IACV,SAAS,uCAAuC,QAAQ;IACxD,MAAM;IACP,CAAC;AACF;;EAEF,MAAM,OAAO,MAAMN,iBAAAA,QAAG,MAAM,SAAS,CAAC,YAAY,KAAK;AACvD,MAAI,CAAC,MAAM;AACT,OAAI,SAAS,WAAW,SACtB,aAAY,KAAK;IACf,MAAM;IACN,UAAU;IACV,SAAS;IACT,MAAM;IACP,CAAC;AAEJ;;AAEF,MAAI,CAAC,KAAK,QAAQ,CAAE;EACpB,MAAM,UAAU,MAAMA,iBAAAA,QAAG,SAAS,SAAS;AAC3C,MAAI,SAAS,UAAUW,kBAAAA,OAAO,QAAQ,KAAK,SAAS,OAClD,aAAY,KAAK;GACf,MAAM;GACN,UAAU;GACV,SAAS;GACT,MAAM;GACP,CAAC;AAEJ,MAAI,SAAS,SAAS,QAAQ;GAC5B,MAAM,SAAS,QAAQ,SAAS,OAAO;AACvC,OACE,iFAAiF,KAC/E,OACD,CAED,aAAY,KAAK;IACf,MAAM;IACN,UAAU;IACV,SAAS;IACT,MAAM;IACP,CAAC;AAEJ,OAAI,4BAA4B,KAAK,OAAO,CAC1C,aAAY,KAAK;IACf,MAAM;IACN,UAAU;IACV,SAAS;IACT,MAAM;IACP,CAAC;;;CAKR,MAAM,UAAUC,gCAAAA,qBAAqB,OAAO,WAAW;AACvD,MAAK,MAAM,aAAa,QACtB,aAAY,KAAK;EACf,MAAM;EACN,UAAU;EACV,SAAS,UAAU;EACnB,aAAa,UAAU;EACvB,OAAO,UAAU;EACjB,YAAY,8BAA8B,WAAW;EACtD,CAAC;AAEJ,aAAY,KACV,GAAI,MAAMC,gCAAAA,uBAAuB;EAC/B,UAAU,QAAQ;EAClB;EACA,SAAS,IAAIC,oBAAAA,UAAU,QAAQ,SAAS;EACzC,CAAC,CACH;AAED,KAAI,QAAQ,MAAM;EAChB,MAAM,cAAcP,UAAAA,QAAK,KAAK,QAAQ,UAAU,eAAe;EAC/D,IAAI;AACJ,MAAI;AACF,qBAAkB,KAAK,MAAM,MAAMP,iBAAAA,QAAG,SAAS,aAAa,OAAO,CAAC;WAG7D,OAAO;AACd,eAAY,KAAK;IACf,MAAM;IACN,UAAU;IACV,SACE,iBAAiB,QACb,gCAAgC,MAAM,YACtC;IACN,MAAM;IACP,CAAC;;AAEJ,MAAI,gBACF,MAAK,MAAM,UAAU;GAAC;GAAe;GAAQ;GAAQ,EAAW;AAC9D,OAAI,CAAC,gBAAgB,UAAU,QAAS;AACxC,OAAI;AACF,UAAM,cAAc,QAAQ,CAAC,OAAO,EAAE;KACpC,KAAK,QAAQ;KACb,KAAK,QAAQ;KACb,SAAS;KACV,CAAC;YACK,OAAO;AACd,gBAAY,KAAK;KACf,MAAM,WAAW,OAAO,QAAQ,KAAK,IAAI,CAAC,aAAa,CAAC;KACxD,UAAU;KACV,SAAS,GAAG,OAAO,WAAW,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;KACpF,MAAM;KACP,CAAC;;;;AAMV,QAAOE,wBAAAA,uBAAuB,YAAY"}
1
+ {"version":3,"file":"doctor.cjs","names":["execFile","fs","readManagedState","createDiagnosticReport","getCliVersion","loadTemplateManifest","getTemplateRoot","findRepositorySymlink","path","readCatalog","walkPackageJsonFiles","collectConsumerDependencyNames","sha256","getPendingMigrations","runMigrationValidators","ChangeSet"],"sources":["../../src/doctor.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\r\nimport fs from \"node:fs/promises\";\r\nimport path from \"node:path\";\r\nimport { promisify } from \"node:util\";\r\nimport semver from \"semver\";\r\nimport { collectConsumerDependencyNames, readCatalog, walkPackageJsonFiles } from \"./catalog\";\r\nimport { ChangeSet, findRepositorySymlink } from \"./changes\";\r\nimport { TEMPLATE_NAME } from \"./constants\";\r\nimport { createDiagnosticReport, type Diagnostic, type DiagnosticReport } from \"./diagnostics\";\r\nimport { getPendingMigrations, runMigrationValidators } from \"./migrations/registry\";\r\nimport { getTemplateRoot } from \"./paths\";\r\nimport { getCliVersion, type ManagedState, readManagedState, sha256 } from \"./state\";\r\nimport { loadTemplateManifest } from \"./template\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\nconst FULL_COMMAND_TIMEOUT_MS = 10 * 60 * 1000;\r\n\r\nexport interface DoctorOptions {\r\n repoRoot: string;\r\n full?: boolean;\r\n state?: ManagedState;\r\n}\r\n\r\nasync function exists(filePath: string): Promise<boolean> {\r\n return fs\r\n .access(filePath)\r\n .then(() => true)\r\n .catch(() => false);\r\n}\r\n\r\nexport async function diagnoseManagedRepo(options: DoctorOptions): Promise<DiagnosticReport> {\r\n const diagnostics: Diagnostic[] = [];\r\n let state = options.state;\r\n if (!state) {\r\n try {\r\n state = await readManagedState(options.repoRoot);\r\n } catch (error) {\r\n diagnostics.push({\r\n code: \"STATE_INVALID\",\r\n severity: \"error\",\r\n message: error instanceof Error ? error.message : \"Unable to read .m5kdev.json.\",\r\n path: \".m5kdev.json\",\r\n suggestion: \"Run m5kdev init to establish a managed baseline.\",\r\n });\r\n return createDiagnosticReport(diagnostics);\r\n }\r\n }\r\n\r\n const cliVersion = getCliVersion();\r\n if (state.template.name !== TEMPLATE_NAME) {\r\n diagnostics.push({\r\n code: \"STATE_TEMPLATE_UNKNOWN\",\r\n severity: \"error\",\r\n message: `Unsupported template name: ${state.template.name}`,\r\n path: \".m5kdev.json\",\r\n });\r\n }\r\n if (!semver.valid(state.template.version)) {\r\n diagnostics.push({\r\n code: \"STATE_VERSION_INVALID\",\r\n severity: \"error\",\r\n message: `Invalid template version: ${state.template.version}`,\r\n path: \".m5kdev.json\",\r\n });\r\n } else if (semver.gt(state.template.version, cliVersion)) {\r\n diagnostics.push({\r\n code: \"CLI_TOO_OLD\",\r\n severity: \"error\",\r\n message: `This repository is managed by ${state.template.version}, but the running CLI is ${cliVersion}.`,\r\n suggestion: `Run pnpm dlx create-m5kdev@${state.template.version} doctor.`,\r\n });\r\n } else if (semver.lt(state.template.version, cliVersion)) {\r\n diagnostics.push({\r\n code: \"UPDATE_AVAILABLE\",\r\n severity: \"warning\",\r\n message: `Template ${cliVersion} is available; this repository is at ${state.template.version}.`,\r\n suggestion: `Preview it with pnpm dlx create-m5kdev@${cliVersion} update --dry-run.`,\r\n });\r\n }\r\n\r\n const manifest = loadTemplateManifest(getTemplateRoot());\r\n const seenFeatures = new Set<string>();\r\n for (const feature of state.template.features) {\r\n if (seenFeatures.has(feature)) {\r\n diagnostics.push({\r\n code: \"FEATURE_DUPLICATE\",\r\n severity: \"error\",\r\n message: `Feature ${feature} is listed more than once.`,\r\n path: \".m5kdev.json\",\r\n });\r\n } else if (!(feature in manifest.features)) {\r\n diagnostics.push({\r\n code: \"FEATURE_UNKNOWN\",\r\n severity: \"error\",\r\n message: `Unknown managed feature: ${feature}`,\r\n path: \".m5kdev.json\",\r\n });\r\n }\r\n seenFeatures.add(feature);\r\n }\r\n const requiredPaths = new Set(manifest.requiredPaths);\r\n if (state.template.features.includes(\"webapp\")) requiredPaths.add(\"apps/webapp/package.json\");\r\n if (state.template.features.includes(\"expo\")) requiredPaths.add(\"apps/expo/package.json\");\r\n if (state.template.features.includes(\"test-harness\")) requiredPaths.add(\"apps/e2e/package.json\");\r\n for (const relativePath of requiredPaths) {\r\n const symlink = await findRepositorySymlink(options.repoRoot, relativePath);\r\n if (symlink) {\r\n diagnostics.push({\r\n code: \"MANAGED_PATH_SYMLINK\",\r\n severity: \"error\",\r\n message: `Required managed path contains symbolic link ${symlink}.`,\r\n path: relativePath,\r\n });\r\n } else if (!(await exists(path.join(options.repoRoot, relativePath)))) {\r\n diagnostics.push({\r\n code: \"REQUIRED_PATH_MISSING\",\r\n severity: \"error\",\r\n message: \"Required managed path is missing.\",\r\n path: relativePath,\r\n suggestion:\r\n \"Restore the path or correct the enabled features before initializing/updating.\",\r\n });\r\n }\r\n }\r\n\r\n const workspaceRelativePath = \"pnpm-workspace.yaml\";\r\n const workspacePath = path.join(options.repoRoot, workspaceRelativePath);\r\n const workspaceSymlink = await findRepositorySymlink(options.repoRoot, workspaceRelativePath);\r\n if (!workspaceSymlink && (await exists(workspacePath))) {\r\n try {\r\n const actualCatalog = readCatalog(await fs.readFile(workspacePath, \"utf8\"));\r\n for (const [name, expected] of Object.entries(state.catalog)) {\r\n if (!semver.validRange(expected)) {\r\n diagnostics.push({\r\n code: \"CATALOG_VERSION_INVALID\",\r\n severity: \"error\",\r\n message: `${name} has an invalid managed SemVer range: ${expected}.`,\r\n path: \".m5kdev.json\",\r\n });\r\n }\r\n if (!(name in actualCatalog)) {\r\n diagnostics.push({\r\n code: \"CATALOG_ENTRY_MISSING\",\r\n severity: \"error\",\r\n message: `Managed catalog entry ${name} is missing.`,\r\n path: \"pnpm-workspace.yaml\",\r\n suggestion: `Restore ${name}: ${expected}.`,\r\n });\r\n } else if (actualCatalog[name] !== expected) {\r\n diagnostics.push({\r\n code: \"CATALOG_VERSION_MISMATCH\",\r\n severity: \"error\",\r\n message: `${name} is ${actualCatalog[name]}, expected managed version ${expected}.`,\r\n path: \"pnpm-workspace.yaml\",\r\n suggestion: `Set ${name} to ${expected} in pnpm-workspace.yaml.`,\r\n });\r\n }\r\n }\r\n\r\n const appsDirectory = path.join(options.repoRoot, \"apps\");\r\n const packageFiles = [path.join(options.repoRoot, \"package.json\")];\r\n if (await exists(appsDirectory)) packageFiles.push(...walkPackageJsonFiles(appsDirectory));\r\n for (const dependency of collectConsumerDependencyNames(packageFiles)) {\r\n if (!(dependency in actualCatalog)) {\r\n diagnostics.push({\r\n code: \"CATALOG_REFERENCE_MISSING\",\r\n severity: \"error\",\r\n message: `${dependency} uses catalog: but has no catalog entry.`,\r\n path: \"pnpm-workspace.yaml\",\r\n });\r\n }\r\n }\r\n } catch (error) {\r\n diagnostics.push({\r\n code: \"CATALOG_INVALID\",\r\n severity: \"error\",\r\n message: error instanceof Error ? error.message : \"Unable to parse the consumer catalog.\",\r\n path: \"pnpm-workspace.yaml\",\r\n });\r\n }\r\n }\r\n\r\n for (const [relativePath, baseline] of Object.entries(state.files)) {\r\n if (baseline.policy === \"ignore\") continue;\r\n const filePath = path.join(options.repoRoot, relativePath);\r\n const symlink = await findRepositorySymlink(options.repoRoot, relativePath);\r\n if (symlink) {\r\n diagnostics.push({\r\n code: \"MANAGED_PATH_SYMLINK\",\r\n severity: \"error\",\r\n message: `Managed path contains symbolic link ${symlink}.`,\r\n path: relativePath,\r\n });\r\n continue;\r\n }\r\n const stat = await fs.lstat(filePath).catch(() => null);\r\n if (!stat) {\r\n if (baseline.policy === \"ensure\") {\r\n diagnostics.push({\r\n code: \"ENSURED_PATH_MISSING\",\r\n severity: \"error\",\r\n message: \"An ensured template path is missing.\",\r\n path: relativePath,\r\n });\r\n }\r\n continue;\r\n }\r\n if (!stat.isFile()) continue;\r\n const content = await fs.readFile(filePath);\r\n if (baseline.sha256 && sha256(content) !== baseline.sha256) {\r\n diagnostics.push({\r\n code: \"TEMPLATE_CUSTOMIZED\",\r\n severity: \"info\",\r\n message: \"Managed template content has local customizations.\",\r\n path: relativePath,\r\n });\r\n }\r\n if (baseline.kind === \"text\") {\r\n const source = content.toString(\"utf8\");\r\n if (\r\n /\\{\\{(?:APP_NAME|APP_DESCRIPTION|APP_SLUG|PACKAGE_SCOPE|BETTER_AUTH_SECRET)\\}\\}/.test(\r\n source\r\n )\r\n ) {\r\n diagnostics.push({\r\n code: \"UNRESOLVED_TEMPLATE_TOKEN\",\r\n severity: \"error\",\r\n message: \"Unresolved template token found.\",\r\n path: relativePath,\r\n });\r\n }\r\n if (/^(<{7}|={7}|>{7})(?: |$)/m.test(source)) {\r\n diagnostics.push({\r\n code: \"MERGE_CONFLICT_MARKER\",\r\n severity: \"error\",\r\n message: \"Unresolved merge conflict marker found.\",\r\n path: relativePath,\r\n });\r\n }\r\n }\r\n }\r\n\r\n const pending = getPendingMigrations(state, cliVersion);\r\n for (const migration of pending) {\r\n diagnostics.push({\r\n code: \"MIGRATION_PENDING\",\r\n severity: \"error\",\r\n message: migration.description,\r\n migrationId: migration.id,\r\n guide: migration.guide,\r\n suggestion: `Run pnpm dlx create-m5kdev@${cliVersion} update.`,\r\n });\r\n }\r\n diagnostics.push(\r\n ...(await runMigrationValidators({\r\n repoRoot: options.repoRoot,\r\n state,\r\n changes: new ChangeSet(options.repoRoot),\r\n }))\r\n );\r\n\r\n if (options.full) {\r\n const packagePath = path.join(options.repoRoot, \"package.json\");\r\n let packageManifest: { scripts?: Record<string, string> } | undefined;\r\n try {\r\n packageManifest = JSON.parse(await fs.readFile(packagePath, \"utf8\")) as {\r\n scripts?: Record<string, string>;\r\n };\r\n } catch (error) {\r\n diagnostics.push({\r\n code: \"PACKAGE_JSON_UNREADABLE\",\r\n severity: \"error\",\r\n message:\r\n error instanceof Error\r\n ? `Unable to read package.json: ${error.message}`\r\n : \"Unable to read package.json.\",\r\n path: \"package.json\",\r\n });\r\n }\r\n if (packageManifest) {\r\n for (const script of [\"check-types\", \"lint\", \"build\"] as const) {\r\n if (!packageManifest.scripts?.[script]) continue;\r\n try {\r\n await execFileAsync(\"pnpm\", [script], {\r\n cwd: options.repoRoot,\r\n env: process.env,\r\n timeout: FULL_COMMAND_TIMEOUT_MS,\r\n });\r\n } catch (error) {\r\n diagnostics.push({\r\n code: `COMMAND_${script.replace(\"-\", \"_\").toUpperCase()}_FAILED`,\r\n severity: \"error\",\r\n message: `${script} failed: ${error instanceof Error ? error.message : String(error)}`,\r\n path: \"package.json\",\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n return createDiagnosticReport(diagnostics);\r\n}\r\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile.cjs","names":["execFile","fs","path","os","loadTemplateManifest","collectTemplateFiles","getExcludedFeaturePaths","getTemplateFilePolicy","ChangeSet","createManagedState","findRepositorySymlink","mergeManagedCatalog","sha256"],"sources":["../../src/reconcile.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\r\nimport fs from \"node:fs/promises\";\r\nimport os from \"node:os\";\r\nimport path from \"node:path\";\r\nimport { promisify } from \"node:util\";\r\nimport { mergeManagedCatalog } from \"./catalog\";\r\nimport { ChangeSet, findRepositorySymlink } from \"./changes\";\r\nimport { collectTemplateFiles } from \"./fs\";\r\nimport { createManagedState, type ManagedState, sha256 } from \"./state\";\r\nimport { getExcludedFeaturePaths, getTemplateFilePolicy, loadTemplateManifest } from \"./template\";\r\nimport type { RenderedTemplateFile, TemplateContext } from \"./types\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\n\r\nexport interface BaseTemplateProvider {\r\n getTemplateRoot(version: string): Promise<{ root: string; cleanup: () => Promise<void> }>;\r\n}\r\n\r\nexport const npmBaseTemplateProvider: BaseTemplateProvider = {\r\n async getTemplateRoot(version) {\r\n const directory = await fs.mkdtemp(path.join(os.tmpdir(), \"m5kdev-template-\"));\r\n try {\r\n const { default: pacote } = await import(\"pacote\");\r\n await pacote.extract(`create-m5kdev@${version}`, directory);\r\n const root = path.join(directory, \"templates\", \"minimal-app\");\r\n await fs.access(path.join(root, \"template.manifest.json\"));\r\n return { root, cleanup: () => fs.rm(directory, { recursive: true, force: true }) };\r\n } catch (error) {\r\n await fs.rm(directory, { recursive: true, force: true });\r\n throw error;\r\n }\r\n },\r\n};\r\n\r\nfunction toTemplateContext(state: ManagedState): TemplateContext {\r\n return { ...state.template.context, betterAuthSecret: \"ignored-by-managed-state\" };\r\n}\r\n\r\nasync function renderForState(\r\n templateRoot: string,\r\n state: ManagedState\r\n): Promise<{\r\n files: RenderedTemplateFile[];\r\n manifest: ReturnType<typeof loadTemplateManifest>;\r\n}> {\r\n const manifest = loadTemplateManifest(templateRoot);\r\n const enabledFeatures = new Set(state.template.features);\r\n return {\r\n manifest,\r\n files: await collectTemplateFiles(templateRoot, toTemplateContext(state), {\r\n enabledFeatures,\r\n excludePrefixes: getExcludedFeaturePaths(manifest, enabledFeatures),\r\n ignoreContentFor: (relativePath) =>\r\n getTemplateFilePolicy(manifest, relativePath) === \"ignore\",\r\n }),\r\n };\r\n}\r\n\r\nasync function mergeText(base: Buffer, local: Buffer, target: Buffer): Promise<string | undefined> {\r\n const directory = await fs.mkdtemp(path.join(os.tmpdir(), \"m5kdev-merge-\"));\r\n const localPath = path.join(directory, \"local\");\r\n const basePath = path.join(directory, \"base\");\r\n const targetPath = path.join(directory, \"target\");\r\n try {\r\n await Promise.all([\r\n fs.writeFile(localPath, local),\r\n fs.writeFile(basePath, base),\r\n fs.writeFile(targetPath, target),\r\n ]);\r\n try {\r\n const result = await execFileAsync(\r\n \"git\",\r\n [\"merge-file\", \"-p\", \"--diff3\", localPath, basePath, targetPath],\r\n {\r\n encoding: \"utf8\",\r\n }\r\n );\r\n return result.stdout;\r\n } catch (error) {\r\n const mergeError = error as Error & { code?: number };\r\n if (\r\n typeof mergeError.code === \"number\" &&\r\n mergeError.code >= 1 &&\r\n mergeError.code <= 127\r\n ) {\r\n return undefined;\r\n }\r\n throw error;\r\n }\r\n } finally {\r\n await fs.rm(directory, { recursive: true, force: true });\r\n }\r\n}\r\n\r\nexport interface ReconcileResult {\r\n changes: ChangeSet;\r\n targetState: ManagedState;\r\n dependenciesChanged: boolean;\r\n}\r\n\r\nexport async function reconcileTemplates(options: {\r\n repoRoot: string;\r\n state: ManagedState;\r\n targetTemplateRoot: string;\r\n targetVersion: string;\r\n baseProvider?: BaseTemplateProvider;\r\n}): Promise<ReconcileResult> {\r\n const changes = new ChangeSet(options.repoRoot);\r\n const target = await renderForState(options.targetTemplateRoot, options.state);\r\n const enabledFeatures = new Set(options.state.template.features);\r\n const targetState = createManagedState({\r\n templateVersion: options.targetVersion,\r\n enabledFeatures,\r\n context: toTemplateContext(options.state),\r\n renderedFiles: target.files,\r\n manifest: target.manifest,\r\n appliedMigrations: options.state.appliedMigrations,\r\n });\r\n const targetFiles = new Map(target.files.map((file) => [file.relativePath, file]));\r\n const mergeCandidates: Array<{\r\n path: string;\r\n basePath: string;\r\n local: Buffer;\r\n target: RenderedTemplateFile;\r\n deletePath?: string;\r\n }> = [];\r\n let dependenciesChanged = false;\r\n\r\n const workspacePath = \"pnpm-workspace.yaml\";\r\n const workspaceSymlink = await findRepositorySymlink(options.repoRoot, workspacePath);\r\n if (workspaceSymlink) {\r\n changes.addConflict({\r\n path: workspacePath,\r\n reason: \"The managed workspace file is a symbolic link.\",\r\n });\r\n }\r\n const workspace = workspaceSymlink ? undefined : await changes.read(workspacePath);\r\n if (workspace) {\r\n const catalogMerge = mergeManagedCatalog({\r\n source: workspace.toString(\"utf8\"),\r\n base: options.state.catalog,\r\n target: targetState.catalog,\r\n });\r\n for (const conflict of catalogMerge.conflicts) {\r\n changes.addConflict({\r\n path: workspacePath,\r\n reason: `Catalog entry ${conflict.name} changed both locally and in the target template.`,\r\n });\r\n }\r\n if (catalogMerge.changed) {\r\n changes.addChange({\r\n kind: \"modify\",\r\n path: workspacePath,\r\n content: Buffer.from(catalogMerge.source),\r\n reason: \"Reconcile managed consumer catalog\",\r\n });\r\n dependenciesChanged = true;\r\n }\r\n }\r\n\r\n const paths = new Set([...Object.keys(options.state.files), ...targetFiles.keys()]);\r\n paths.delete(workspacePath);\r\n\r\n for (const rename of target.manifest.sync.renames ?? []) {\r\n const baseline = options.state.files[rename.from];\r\n const targetFile = targetFiles.get(rename.to);\r\n if (!baseline || !targetFile) {\r\n changes.addConflict({\r\n path: rename.from,\r\n reason: `Invalid template rename ${rename.from} -> ${rename.to}.`,\r\n });\r\n continue;\r\n }\r\n paths.delete(rename.from);\r\n paths.delete(rename.to);\r\n const sourcePath = path.join(options.repoRoot, rename.from);\r\n const destinationPath = path.join(options.repoRoot, rename.to);\r\n const sourceSymlink = await findRepositorySymlink(options.repoRoot, rename.from);\r\n const destinationSymlink = await findRepositorySymlink(options.repoRoot, rename.to);\r\n if (sourceSymlink || destinationSymlink) {\r\n changes.addConflict({\r\n path: rename.from,\r\n reason: \"A renamed managed path contains a symbolic link.\",\r\n });\r\n continue;\r\n }\r\n const sourceStat = await fs.lstat(sourcePath).catch(() => null);\r\n const destinationStat = await fs.lstat(destinationPath).catch(() => null);\r\n if (destinationStat && rename.from !== rename.to) {\r\n changes.addConflict({\r\n path: rename.to,\r\n reason: \"A template rename targets an existing app-owned path.\",\r\n });\r\n continue;\r\n }\r\n const local = sourceStat?.isFile() ? await fs.readFile(sourcePath) : undefined;\r\n if (!local) {\r\n if (targetState.files[rename.to]?.policy === \"ensure\") {\r\n changes.addChange({\r\n kind: \"add\",\r\n path: rename.to,\r\n content: targetFile.content,\r\n reason: `Restore renamed ensured path from ${rename.from}`,\r\n });\r\n }\r\n continue;\r\n }\r\n const localHash = sha256(local);\r\n const targetHash = sha256(targetFile.content);\r\n if (localHash === baseline.sha256) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: rename.from,\r\n reason: `Rename template path to ${rename.to}`,\r\n });\r\n changes.addChange({\r\n kind: \"add\",\r\n path: rename.to,\r\n content: targetFile.content,\r\n reason: `Rename template path from ${rename.from}`,\r\n });\r\n } else if (localHash === targetHash) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: rename.from,\r\n reason: `Rename template path to ${rename.to}`,\r\n });\r\n changes.addChange({\r\n kind: \"add\",\r\n path: rename.to,\r\n content: local,\r\n reason: `Rename template path from ${rename.from}`,\r\n });\r\n } else if (baseline.kind === \"binary\" || targetFile.kind === \"binary\") {\r\n changes.addConflict({\r\n path: rename.from,\r\n reason: \"Renamed binary content changed locally and in the target template.\",\r\n });\r\n } else {\r\n mergeCandidates.push({\r\n path: rename.to,\r\n basePath: rename.from,\r\n local,\r\n target: targetFile,\r\n deletePath: rename.from,\r\n });\r\n }\r\n }\r\n\r\n for (const relativePath of [...paths].sort((left, right) => left.localeCompare(right))) {\r\n const baseline = options.state.files[relativePath];\r\n const targetFile = targetFiles.get(relativePath);\r\n const policy = targetState.files[relativePath]?.policy ?? baseline?.policy ?? \"merge\";\r\n if (policy === \"ignore\") continue;\r\n\r\n const localPath = path.join(options.repoRoot, relativePath);\r\n const symlink = await findRepositorySymlink(options.repoRoot, relativePath);\r\n if (symlink) {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: `Managed path contains symbolic link ${symlink}.`,\r\n });\r\n continue;\r\n }\r\n const localStat = await fs.lstat(localPath).catch(() => null);\r\n const local = localStat?.isFile() ? await fs.readFile(localPath) : undefined;\r\n const localHash = local ? sha256(local) : undefined;\r\n const baseHash = baseline?.sha256;\r\n const targetHash = targetFile ? sha256(targetFile.content) : undefined;\r\n\r\n if (!baseline && targetFile) {\r\n if (!local) {\r\n changes.addChange({\r\n kind: \"add\",\r\n path: relativePath,\r\n content: targetFile.content,\r\n reason: \"Add template path\",\r\n });\r\n } else if (localHash !== targetHash && policy !== \"ensure\") {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: \"The target template adds a path already owned by the app.\",\r\n });\r\n }\r\n continue;\r\n }\r\n if (baseline && !targetFile) {\r\n if (local && localHash === baseHash) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: relativePath,\r\n reason: \"Remove obsolete template path\",\r\n });\r\n } else if (local) {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: \"The template removed a locally customized path.\",\r\n });\r\n }\r\n continue;\r\n }\r\n if (!baseline || !targetFile) continue;\r\n\r\n if (!local) {\r\n if (policy === \"ensure\") {\r\n changes.addChange({\r\n kind: \"add\",\r\n path: relativePath,\r\n content: targetFile.content,\r\n reason: \"Restore ensured path\",\r\n });\r\n } else if (targetHash !== baseHash) {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: \"A locally deleted path changed in the target template.\",\r\n });\r\n }\r\n continue;\r\n }\r\n if (localHash === targetHash || (localHash !== baseHash && targetHash === baseHash)) continue;\r\n if (localHash === baseHash) {\r\n changes.addChange({\r\n kind: \"modify\",\r\n path: relativePath,\r\n content: targetFile.content,\r\n reason: \"Update unchanged template path\",\r\n });\r\n if (relativePath === \"package.json\" || relativePath.endsWith(\"/package.json\"))\r\n dependenciesChanged = true;\r\n continue;\r\n }\r\n if (baseline.kind === \"binary\" || targetFile.kind === \"binary\") {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: \"Binary content changed locally and in the target template.\",\r\n });\r\n continue;\r\n }\r\n mergeCandidates.push({ path: relativePath, basePath: relativePath, local, target: targetFile });\r\n }\r\n\r\n if (mergeCandidates.length > 0) {\r\n let baseRoot: Awaited<ReturnType<BaseTemplateProvider[\"getTemplateRoot\"]>> | undefined;\r\n try {\r\n baseRoot = await (options.baseProvider ?? npmBaseTemplateProvider).getTemplateRoot(\r\n options.state.template.version\r\n );\r\n const base = await renderForState(baseRoot.root, options.state);\r\n const baseFiles = new Map(base.files.map((file) => [file.relativePath, file]));\r\n for (const candidate of mergeCandidates) {\r\n const baseFile = baseFiles.get(candidate.basePath);\r\n if (\r\n !baseFile ||\r\n sha256(baseFile.content) !== options.state.files[candidate.basePath]?.sha256\r\n ) {\r\n changes.addConflict({\r\n path: candidate.path,\r\n reason: \"Historical template content does not match the stored baseline hash.\",\r\n });\r\n continue;\r\n }\r\n const merged = await mergeText(baseFile.content, candidate.local, candidate.target.content);\r\n if (merged === undefined) {\r\n changes.addConflict({\r\n path: candidate.path,\r\n reason: \"Three-way merge produced conflicts.\",\r\n });\r\n } else if (merged !== candidate.local.toString(\"utf8\")) {\r\n if (candidate.deletePath) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: candidate.deletePath,\r\n reason: `Rename template path to ${candidate.path}`,\r\n });\r\n }\r\n changes.addChange({\r\n kind: candidate.deletePath ? \"add\" : \"modify\",\r\n path: candidate.path,\r\n content: Buffer.from(merged),\r\n reason: \"Three-way merge local and template changes\",\r\n });\r\n if (candidate.path === \"package.json\" || candidate.path.endsWith(\"/package.json\")) {\r\n dependenciesChanged = true;\r\n }\r\n } else if (candidate.deletePath) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: candidate.deletePath,\r\n reason: `Rename template path to ${candidate.path}`,\r\n });\r\n changes.addChange({\r\n kind: \"add\",\r\n path: candidate.path,\r\n content: candidate.local,\r\n reason: `Rename template path from ${candidate.deletePath}`,\r\n });\r\n }\r\n }\r\n } catch (error) {\r\n for (const candidate of mergeCandidates) {\r\n changes.addConflict({\r\n path: candidate.path,\r\n reason: `Unable to load historical template ${options.state.template.version}: ${error instanceof Error ? error.message : String(error)}`,\r\n });\r\n }\r\n } finally {\r\n await baseRoot?.cleanup();\r\n }\r\n }\r\n\r\n return { changes, targetState, dependenciesChanged };\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"reconcile.cjs","names":["execFile","fs","path","os","loadTemplateManifest","collectTemplateFiles","getExcludedFeaturePaths","getTemplateFilePolicy","ChangeSet","createManagedState","findRepositorySymlink","mergeManagedCatalog","sha256"],"sources":["../../src/reconcile.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\r\nimport fs from \"node:fs/promises\";\r\nimport os from \"node:os\";\r\nimport path from \"node:path\";\r\nimport { promisify } from \"node:util\";\r\nimport { mergeManagedCatalog } from \"./catalog\";\r\nimport { ChangeSet, findRepositorySymlink } from \"./changes\";\r\nimport { collectTemplateFiles } from \"./fs\";\r\nimport { createManagedState, type ManagedState, sha256 } from \"./state\";\r\nimport { getExcludedFeaturePaths, getTemplateFilePolicy, loadTemplateManifest } from \"./template\";\r\nimport type { RenderedTemplateFile, TemplateContext } from \"./types\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\n\r\nexport interface BaseTemplateProvider {\r\n getTemplateRoot(version: string): Promise<{ root: string; cleanup: () => Promise<void> }>;\r\n}\r\n\r\nexport const npmBaseTemplateProvider: BaseTemplateProvider = {\r\n async getTemplateRoot(version) {\r\n const directory = await fs.mkdtemp(path.join(os.tmpdir(), \"m5kdev-template-\"));\r\n try {\r\n const { default: pacote } = await import(\"pacote\");\r\n await pacote.extract(`create-m5kdev@${version}`, directory);\r\n const root = path.join(directory, \"templates\", \"minimal-app\");\r\n await fs.access(path.join(root, \"template.manifest.json\"));\r\n return { root, cleanup: () => fs.rm(directory, { recursive: true, force: true }) };\r\n } catch (error) {\r\n await fs.rm(directory, { recursive: true, force: true });\r\n throw error;\r\n }\r\n },\r\n};\r\n\r\nfunction toTemplateContext(state: ManagedState): TemplateContext {\r\n return { ...state.template.context, betterAuthSecret: \"ignored-by-managed-state\" };\r\n}\r\n\r\nasync function renderForState(\r\n templateRoot: string,\r\n state: ManagedState\r\n): Promise<{\r\n files: RenderedTemplateFile[];\r\n manifest: ReturnType<typeof loadTemplateManifest>;\r\n}> {\r\n const manifest = loadTemplateManifest(templateRoot);\r\n const enabledFeatures = new Set(state.template.features);\r\n return {\r\n manifest,\r\n files: await collectTemplateFiles(templateRoot, toTemplateContext(state), {\r\n enabledFeatures,\r\n excludePrefixes: getExcludedFeaturePaths(manifest, enabledFeatures),\r\n ignoreContentFor: (relativePath) =>\r\n getTemplateFilePolicy(manifest, relativePath) === \"ignore\",\r\n }),\r\n };\r\n}\r\n\r\nasync function mergeText(base: Buffer, local: Buffer, target: Buffer): Promise<string | undefined> {\r\n const directory = await fs.mkdtemp(path.join(os.tmpdir(), \"m5kdev-merge-\"));\r\n const localPath = path.join(directory, \"local\");\r\n const basePath = path.join(directory, \"base\");\r\n const targetPath = path.join(directory, \"target\");\r\n try {\r\n await Promise.all([\r\n fs.writeFile(localPath, local),\r\n fs.writeFile(basePath, base),\r\n fs.writeFile(targetPath, target),\r\n ]);\r\n try {\r\n const result = await execFileAsync(\r\n \"git\",\r\n [\"merge-file\", \"-p\", \"--diff3\", localPath, basePath, targetPath],\r\n {\r\n encoding: \"utf8\",\r\n }\r\n );\r\n return result.stdout;\r\n } catch (error) {\r\n const mergeError = error as Error & { code?: number };\r\n if (typeof mergeError.code === \"number\" && mergeError.code >= 1 && mergeError.code <= 127) {\r\n return undefined;\r\n }\r\n throw error;\r\n }\r\n } finally {\r\n await fs.rm(directory, { recursive: true, force: true });\r\n }\r\n}\r\n\r\nexport interface ReconcileResult {\r\n changes: ChangeSet;\r\n targetState: ManagedState;\r\n dependenciesChanged: boolean;\r\n}\r\n\r\nexport async function reconcileTemplates(options: {\r\n repoRoot: string;\r\n state: ManagedState;\r\n targetTemplateRoot: string;\r\n targetVersion: string;\r\n baseProvider?: BaseTemplateProvider;\r\n}): Promise<ReconcileResult> {\r\n const changes = new ChangeSet(options.repoRoot);\r\n const target = await renderForState(options.targetTemplateRoot, options.state);\r\n const enabledFeatures = new Set(options.state.template.features);\r\n const targetState = createManagedState({\r\n templateVersion: options.targetVersion,\r\n enabledFeatures,\r\n context: toTemplateContext(options.state),\r\n renderedFiles: target.files,\r\n manifest: target.manifest,\r\n appliedMigrations: options.state.appliedMigrations,\r\n });\r\n const targetFiles = new Map(target.files.map((file) => [file.relativePath, file]));\r\n const mergeCandidates: Array<{\r\n path: string;\r\n basePath: string;\r\n local: Buffer;\r\n target: RenderedTemplateFile;\r\n deletePath?: string;\r\n }> = [];\r\n let dependenciesChanged = false;\r\n\r\n const workspacePath = \"pnpm-workspace.yaml\";\r\n const workspaceSymlink = await findRepositorySymlink(options.repoRoot, workspacePath);\r\n if (workspaceSymlink) {\r\n changes.addConflict({\r\n path: workspacePath,\r\n reason: \"The managed workspace file is a symbolic link.\",\r\n });\r\n }\r\n const workspace = workspaceSymlink ? undefined : await changes.read(workspacePath);\r\n if (workspace) {\r\n const catalogMerge = mergeManagedCatalog({\r\n source: workspace.toString(\"utf8\"),\r\n base: options.state.catalog,\r\n target: targetState.catalog,\r\n });\r\n for (const conflict of catalogMerge.conflicts) {\r\n changes.addConflict({\r\n path: workspacePath,\r\n reason: `Catalog entry ${conflict.name} changed both locally and in the target template.`,\r\n });\r\n }\r\n if (catalogMerge.changed) {\r\n changes.addChange({\r\n kind: \"modify\",\r\n path: workspacePath,\r\n content: Buffer.from(catalogMerge.source),\r\n reason: \"Reconcile managed consumer catalog\",\r\n });\r\n dependenciesChanged = true;\r\n }\r\n }\r\n\r\n const paths = new Set([...Object.keys(options.state.files), ...targetFiles.keys()]);\r\n paths.delete(workspacePath);\r\n\r\n for (const rename of target.manifest.sync.renames ?? []) {\r\n const baseline = options.state.files[rename.from];\r\n const targetFile = targetFiles.get(rename.to);\r\n if (!baseline || !targetFile) {\r\n changes.addConflict({\r\n path: rename.from,\r\n reason: `Invalid template rename ${rename.from} -> ${rename.to}.`,\r\n });\r\n continue;\r\n }\r\n paths.delete(rename.from);\r\n paths.delete(rename.to);\r\n const sourcePath = path.join(options.repoRoot, rename.from);\r\n const destinationPath = path.join(options.repoRoot, rename.to);\r\n const sourceSymlink = await findRepositorySymlink(options.repoRoot, rename.from);\r\n const destinationSymlink = await findRepositorySymlink(options.repoRoot, rename.to);\r\n if (sourceSymlink || destinationSymlink) {\r\n changes.addConflict({\r\n path: rename.from,\r\n reason: \"A renamed managed path contains a symbolic link.\",\r\n });\r\n continue;\r\n }\r\n const sourceStat = await fs.lstat(sourcePath).catch(() => null);\r\n const destinationStat = await fs.lstat(destinationPath).catch(() => null);\r\n if (destinationStat && rename.from !== rename.to) {\r\n changes.addConflict({\r\n path: rename.to,\r\n reason: \"A template rename targets an existing app-owned path.\",\r\n });\r\n continue;\r\n }\r\n const local = sourceStat?.isFile() ? await fs.readFile(sourcePath) : undefined;\r\n if (!local) {\r\n if (targetState.files[rename.to]?.policy === \"ensure\") {\r\n changes.addChange({\r\n kind: \"add\",\r\n path: rename.to,\r\n content: targetFile.content,\r\n reason: `Restore renamed ensured path from ${rename.from}`,\r\n });\r\n }\r\n continue;\r\n }\r\n const localHash = sha256(local);\r\n const targetHash = sha256(targetFile.content);\r\n if (localHash === baseline.sha256) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: rename.from,\r\n reason: `Rename template path to ${rename.to}`,\r\n });\r\n changes.addChange({\r\n kind: \"add\",\r\n path: rename.to,\r\n content: targetFile.content,\r\n reason: `Rename template path from ${rename.from}`,\r\n });\r\n } else if (localHash === targetHash) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: rename.from,\r\n reason: `Rename template path to ${rename.to}`,\r\n });\r\n changes.addChange({\r\n kind: \"add\",\r\n path: rename.to,\r\n content: local,\r\n reason: `Rename template path from ${rename.from}`,\r\n });\r\n } else if (baseline.kind === \"binary\" || targetFile.kind === \"binary\") {\r\n changes.addConflict({\r\n path: rename.from,\r\n reason: \"Renamed binary content changed locally and in the target template.\",\r\n });\r\n } else {\r\n mergeCandidates.push({\r\n path: rename.to,\r\n basePath: rename.from,\r\n local,\r\n target: targetFile,\r\n deletePath: rename.from,\r\n });\r\n }\r\n }\r\n\r\n for (const relativePath of [...paths].sort((left, right) => left.localeCompare(right))) {\r\n const baseline = options.state.files[relativePath];\r\n const targetFile = targetFiles.get(relativePath);\r\n const policy = targetState.files[relativePath]?.policy ?? baseline?.policy ?? \"merge\";\r\n if (policy === \"ignore\") continue;\r\n\r\n const localPath = path.join(options.repoRoot, relativePath);\r\n const symlink = await findRepositorySymlink(options.repoRoot, relativePath);\r\n if (symlink) {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: `Managed path contains symbolic link ${symlink}.`,\r\n });\r\n continue;\r\n }\r\n const localStat = await fs.lstat(localPath).catch(() => null);\r\n const local = localStat?.isFile() ? await fs.readFile(localPath) : undefined;\r\n const localHash = local ? sha256(local) : undefined;\r\n const baseHash = baseline?.sha256;\r\n const targetHash = targetFile ? sha256(targetFile.content) : undefined;\r\n\r\n if (!baseline && targetFile) {\r\n if (!local) {\r\n changes.addChange({\r\n kind: \"add\",\r\n path: relativePath,\r\n content: targetFile.content,\r\n reason: \"Add template path\",\r\n });\r\n } else if (localHash !== targetHash && policy !== \"ensure\") {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: \"The target template adds a path already owned by the app.\",\r\n });\r\n }\r\n continue;\r\n }\r\n if (baseline && !targetFile) {\r\n if (local && localHash === baseHash) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: relativePath,\r\n reason: \"Remove obsolete template path\",\r\n });\r\n } else if (local) {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: \"The template removed a locally customized path.\",\r\n });\r\n }\r\n continue;\r\n }\r\n if (!baseline || !targetFile) continue;\r\n\r\n if (!local) {\r\n if (policy === \"ensure\") {\r\n changes.addChange({\r\n kind: \"add\",\r\n path: relativePath,\r\n content: targetFile.content,\r\n reason: \"Restore ensured path\",\r\n });\r\n } else if (targetHash !== baseHash) {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: \"A locally deleted path changed in the target template.\",\r\n });\r\n }\r\n continue;\r\n }\r\n if (localHash === targetHash || (localHash !== baseHash && targetHash === baseHash)) continue;\r\n if (localHash === baseHash) {\r\n changes.addChange({\r\n kind: \"modify\",\r\n path: relativePath,\r\n content: targetFile.content,\r\n reason: \"Update unchanged template path\",\r\n });\r\n if (relativePath === \"package.json\" || relativePath.endsWith(\"/package.json\"))\r\n dependenciesChanged = true;\r\n continue;\r\n }\r\n if (baseline.kind === \"binary\" || targetFile.kind === \"binary\") {\r\n changes.addConflict({\r\n path: relativePath,\r\n reason: \"Binary content changed locally and in the target template.\",\r\n });\r\n continue;\r\n }\r\n mergeCandidates.push({ path: relativePath, basePath: relativePath, local, target: targetFile });\r\n }\r\n\r\n if (mergeCandidates.length > 0) {\r\n let baseRoot: Awaited<ReturnType<BaseTemplateProvider[\"getTemplateRoot\"]>> | undefined;\r\n try {\r\n baseRoot = await (options.baseProvider ?? npmBaseTemplateProvider).getTemplateRoot(\r\n options.state.template.version\r\n );\r\n const base = await renderForState(baseRoot.root, options.state);\r\n const baseFiles = new Map(base.files.map((file) => [file.relativePath, file]));\r\n for (const candidate of mergeCandidates) {\r\n const baseFile = baseFiles.get(candidate.basePath);\r\n if (\r\n !baseFile ||\r\n sha256(baseFile.content) !== options.state.files[candidate.basePath]?.sha256\r\n ) {\r\n changes.addConflict({\r\n path: candidate.path,\r\n reason: \"Historical template content does not match the stored baseline hash.\",\r\n });\r\n continue;\r\n }\r\n const merged = await mergeText(baseFile.content, candidate.local, candidate.target.content);\r\n if (merged === undefined) {\r\n changes.addConflict({\r\n path: candidate.path,\r\n reason: \"Three-way merge produced conflicts.\",\r\n });\r\n } else if (merged !== candidate.local.toString(\"utf8\")) {\r\n if (candidate.deletePath) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: candidate.deletePath,\r\n reason: `Rename template path to ${candidate.path}`,\r\n });\r\n }\r\n changes.addChange({\r\n kind: candidate.deletePath ? \"add\" : \"modify\",\r\n path: candidate.path,\r\n content: Buffer.from(merged),\r\n reason: \"Three-way merge local and template changes\",\r\n });\r\n if (candidate.path === \"package.json\" || candidate.path.endsWith(\"/package.json\")) {\r\n dependenciesChanged = true;\r\n }\r\n } else if (candidate.deletePath) {\r\n changes.addChange({\r\n kind: \"delete\",\r\n path: candidate.deletePath,\r\n reason: `Rename template path to ${candidate.path}`,\r\n });\r\n changes.addChange({\r\n kind: \"add\",\r\n path: candidate.path,\r\n content: candidate.local,\r\n reason: `Rename template path from ${candidate.deletePath}`,\r\n });\r\n }\r\n }\r\n } catch (error) {\r\n for (const candidate of mergeCandidates) {\r\n changes.addConflict({\r\n path: candidate.path,\r\n reason: `Unable to load historical template ${options.state.template.version}: ${error instanceof Error ? error.message : String(error)}`,\r\n });\r\n }\r\n } finally {\r\n await baseRoot?.cleanup();\r\n }\r\n }\r\n\r\n return { changes, targetState, dependenciesChanged };\r\n}\r\n"],"mappings":"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"}
@@ -65,6 +65,9 @@ function createManagedState(options) {
65
65
  };
66
66
  if (file.relativePath === "pnpm-workspace.yaml") catalog = require_src_catalog.readCatalog(file.content.toString("utf8"));
67
67
  }
68
+ const renderedPackageManifests = options.renderedFiles.filter((file) => file.relativePath === "package.json" || file.relativePath.endsWith("/package.json")).map((file) => JSON.parse(file.content.toString("utf8")));
69
+ const referencedCatalogNames = new Set(require_src_catalog.collectConsumerDependencyNamesFromManifests(renderedPackageManifests));
70
+ catalog = Object.fromEntries(Object.entries(catalog).filter(([name]) => referencedCatalogNames.has(name)));
68
71
  return {
69
72
  schemaVersion: 1,
70
73
  template: {
@@ -1 +1 @@
1
- {"version":3,"file":"state.cjs","names":["z","path","fs","getTemplateFilePolicy","readCatalog","fsp"],"sources":["../../src/state.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\r\nimport fs from \"node:fs\";\r\nimport fsp from \"node:fs/promises\";\r\nimport path from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\nimport { z } from \"zod\";\r\nimport { type ConsumerCatalog, readCatalog } from \"./catalog\";\r\nimport { getTemplateFilePolicy } from \"./template\";\r\nimport type {\r\n RenderedTemplateFile,\r\n TemplateContext,\r\n TemplateFeatureManifest,\r\n TemplateFilePolicy,\r\n} from \"./types\";\r\n\r\nexport const STATE_FILE_NAME = \".m5kdev.json\";\r\nexport const STATE_SCHEMA_VERSION = 1;\r\n\r\nconst stateFileSchema = z.object({\r\n kind: z.enum([\"text\", \"binary\"]),\r\n policy: z.enum([\"merge\", \"ensure\", \"ignore\"]),\r\n sha256: z\r\n .string()\r\n .regex(/^[a-f0-9]{64}$/)\r\n .optional(),\r\n});\r\n\r\nexport const managedStateSchema = z.object({\r\n schemaVersion: z.literal(STATE_SCHEMA_VERSION),\r\n template: z.object({\r\n name: z.string().min(1),\r\n version: z.string().min(1),\r\n features: z.array(z.string()),\r\n context: z.object({\r\n appName: z.string().min(1),\r\n appDescription: z.string(),\r\n appSlug: z.string().min(1),\r\n packageScope: z.string().min(1),\r\n }),\r\n }),\r\n catalog: z.record(z.string(), z.string()),\r\n files: z.record(z.string(), stateFileSchema),\r\n appliedMigrations: z.array(z.string()),\r\n});\r\n\r\nexport type ManagedState = z.infer<typeof managedStateSchema>;\r\n\r\nexport function sha256(content: Uint8Array | string): string {\r\n return createHash(\"sha256\").update(content).digest(\"hex\");\r\n}\r\n\r\nexport function getCliVersion(): string {\r\n const moduleDirectory = path.dirname(fileURLToPath(import.meta.url));\r\n const candidates = [\r\n path.resolve(moduleDirectory, \"../package.json\"),\r\n path.resolve(moduleDirectory, \"../../package.json\"),\r\n ];\r\n for (const candidate of candidates) {\r\n if (!fs.existsSync(candidate)) continue;\r\n const manifest = JSON.parse(fs.readFileSync(candidate, \"utf8\")) as { version?: string };\r\n if (manifest.version) return manifest.version;\r\n }\r\n throw new Error(\"Unable to resolve the create-m5kdev package version.\");\r\n}\r\n\r\nexport function createManagedState(options: {\r\n templateVersion: string;\r\n enabledFeatures: ReadonlySet<string>;\r\n context: TemplateContext;\r\n renderedFiles: readonly RenderedTemplateFile[];\r\n manifest: TemplateFeatureManifest;\r\n appliedMigrations?: readonly string[];\r\n}): ManagedState {\r\n const files: ManagedState[\"files\"] = {};\r\n let catalog: ConsumerCatalog = {};\r\n\r\n for (const file of options.renderedFiles) {\r\n const policy: TemplateFilePolicy = getTemplateFilePolicy(options.manifest, file.relativePath);\r\n files[file.relativePath] = {\r\n kind: file.kind,\r\n policy,\r\n ...(policy === \"ignore\" ? {} : { sha256: sha256(file.content) }),\r\n };\r\n if (file.relativePath === \"pnpm-workspace.yaml\") {\r\n catalog = readCatalog(file.content.toString(\"utf8\"));\r\n }\r\n }\r\n\r\n return {\r\n schemaVersion: STATE_SCHEMA_VERSION,\r\n template: {\r\n name: \"minimal-app\",\r\n version: options.templateVersion,\r\n features: [...options.enabledFeatures].sort(),\r\n context: {\r\n appName: options.context.appName,\r\n appDescription: options.context.appDescription,\r\n appSlug: options.context.appSlug,\r\n packageScope: options.context.packageScope,\r\n },\r\n },\r\n catalog,\r\n files,\r\n appliedMigrations: [...(options.appliedMigrations ?? [])],\r\n };\r\n}\r\n\r\nexport async function writeManagedState(repoRoot: string, state: ManagedState): Promise<void> {\r\n await fsp.writeFile(\r\n path.join(repoRoot, STATE_FILE_NAME),\r\n `${JSON.stringify(state, null, 2)}\\n`,\r\n \"utf8\"\r\n );\r\n}\r\n\r\nexport async function readManagedState(repoRoot: string): Promise<ManagedState> {\r\n const statePath = path.join(repoRoot, STATE_FILE_NAME);\r\n const parsed = JSON.parse(await fsp.readFile(statePath, \"utf8\")) as unknown;\r\n return managedStateSchema.parse(parsed);\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"state.cjs","names":["z","path","fs","getTemplateFilePolicy","readCatalog","collectConsumerDependencyNamesFromManifests","fsp"],"sources":["../../src/state.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\r\nimport fs from \"node:fs\";\r\nimport fsp from \"node:fs/promises\";\r\nimport path from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\nimport { z } from \"zod\";\r\nimport {\r\n type ConsumerCatalog,\r\n collectConsumerDependencyNamesFromManifests,\r\n readCatalog,\r\n} from \"./catalog\";\r\nimport { getTemplateFilePolicy } from \"./template\";\r\nimport type {\r\n RenderedTemplateFile,\r\n TemplateContext,\r\n TemplateFeatureManifest,\r\n TemplateFilePolicy,\r\n} from \"./types\";\r\n\r\nexport const STATE_FILE_NAME = \".m5kdev.json\";\r\nexport const STATE_SCHEMA_VERSION = 1;\r\n\r\nconst stateFileSchema = z.object({\r\n kind: z.enum([\"text\", \"binary\"]),\r\n policy: z.enum([\"merge\", \"ensure\", \"ignore\"]),\r\n sha256: z\r\n .string()\r\n .regex(/^[a-f0-9]{64}$/)\r\n .optional(),\r\n});\r\n\r\nexport const managedStateSchema = z.object({\r\n schemaVersion: z.literal(STATE_SCHEMA_VERSION),\r\n template: z.object({\r\n name: z.string().min(1),\r\n version: z.string().min(1),\r\n features: z.array(z.string()),\r\n context: z.object({\r\n appName: z.string().min(1),\r\n appDescription: z.string(),\r\n appSlug: z.string().min(1),\r\n packageScope: z.string().min(1),\r\n }),\r\n }),\r\n catalog: z.record(z.string(), z.string()),\r\n files: z.record(z.string(), stateFileSchema),\r\n appliedMigrations: z.array(z.string()),\r\n});\r\n\r\nexport type ManagedState = z.infer<typeof managedStateSchema>;\r\n\r\nexport function sha256(content: Uint8Array | string): string {\r\n return createHash(\"sha256\").update(content).digest(\"hex\");\r\n}\r\n\r\nexport function getCliVersion(): string {\r\n const moduleDirectory = path.dirname(fileURLToPath(import.meta.url));\r\n const candidates = [\r\n path.resolve(moduleDirectory, \"../package.json\"),\r\n path.resolve(moduleDirectory, \"../../package.json\"),\r\n ];\r\n for (const candidate of candidates) {\r\n if (!fs.existsSync(candidate)) continue;\r\n const manifest = JSON.parse(fs.readFileSync(candidate, \"utf8\")) as { version?: string };\r\n if (manifest.version) return manifest.version;\r\n }\r\n throw new Error(\"Unable to resolve the create-m5kdev package version.\");\r\n}\r\n\r\nexport function createManagedState(options: {\r\n templateVersion: string;\r\n enabledFeatures: ReadonlySet<string>;\r\n context: TemplateContext;\r\n renderedFiles: readonly RenderedTemplateFile[];\r\n manifest: TemplateFeatureManifest;\r\n appliedMigrations?: readonly string[];\r\n}): ManagedState {\r\n const files: ManagedState[\"files\"] = {};\r\n let catalog: ConsumerCatalog = {};\r\n\r\n for (const file of options.renderedFiles) {\r\n const policy: TemplateFilePolicy = getTemplateFilePolicy(options.manifest, file.relativePath);\r\n files[file.relativePath] = {\r\n kind: file.kind,\r\n policy,\r\n ...(policy === \"ignore\" ? {} : { sha256: sha256(file.content) }),\r\n };\r\n if (file.relativePath === \"pnpm-workspace.yaml\") {\r\n catalog = readCatalog(file.content.toString(\"utf8\"));\r\n }\r\n }\r\n\r\n const renderedPackageManifests = options.renderedFiles\r\n .filter(\r\n (file) => file.relativePath === \"package.json\" || file.relativePath.endsWith(\"/package.json\")\r\n )\r\n .map((file) => JSON.parse(file.content.toString(\"utf8\")) as Record<string, unknown>);\r\n const referencedCatalogNames = new Set(\r\n collectConsumerDependencyNamesFromManifests(renderedPackageManifests)\r\n );\r\n catalog = Object.fromEntries(\r\n Object.entries(catalog).filter(([name]) => referencedCatalogNames.has(name))\r\n );\r\n\r\n return {\r\n schemaVersion: STATE_SCHEMA_VERSION,\r\n template: {\r\n name: \"minimal-app\",\r\n version: options.templateVersion,\r\n features: [...options.enabledFeatures].sort(),\r\n context: {\r\n appName: options.context.appName,\r\n appDescription: options.context.appDescription,\r\n appSlug: options.context.appSlug,\r\n packageScope: options.context.packageScope,\r\n },\r\n },\r\n catalog,\r\n files,\r\n appliedMigrations: [...(options.appliedMigrations ?? [])],\r\n };\r\n}\r\n\r\nexport async function writeManagedState(repoRoot: string, state: ManagedState): Promise<void> {\r\n await fsp.writeFile(\r\n path.join(repoRoot, STATE_FILE_NAME),\r\n `${JSON.stringify(state, null, 2)}\\n`,\r\n \"utf8\"\r\n );\r\n}\r\n\r\nexport async function readManagedState(repoRoot: string): Promise<ManagedState> {\r\n const statePath = path.join(repoRoot, STATE_FILE_NAME);\r\n const parsed = JSON.parse(await fsp.readFile(statePath, \"utf8\")) as unknown;\r\n return managedStateSchema.parse(parsed);\r\n}\r\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-m5kdev",
3
- "version": "0.31.0",
3
+ "version": "0.31.2",
4
4
  "license": "GPL-3.0-only",
5
5
  "repository": {
6
6
  "type": "git",
@@ -5,12 +5,12 @@ catalog:
5
5
  "@heroui/react": 3.0.3
6
6
  "@heroui/styles": 3.0.3
7
7
  "@libsql/client": 0.17.4
8
- "@m5kdev/backend": 0.31.0
9
- "@m5kdev/commons": 0.31.0
10
- "@m5kdev/config": 0.31.0
11
- "@m5kdev/email": 0.31.0
12
- "@m5kdev/frontend": 0.31.0
13
- "@m5kdev/web-ui": 0.31.0
8
+ "@m5kdev/backend": 0.31.2
9
+ "@m5kdev/commons": 0.31.2
10
+ "@m5kdev/config": 0.31.2
11
+ "@m5kdev/email": 0.31.2
12
+ "@m5kdev/frontend": 0.31.2
13
+ "@m5kdev/web-ui": 0.31.2
14
14
  "@playwright/test": 1.57.0
15
15
  "@react-email/components": 1.0.1
16
16
  "@tailwindcss/vite": 4.1.11