@epic-web/workshop-utils 6.11.4 โ†’ 6.11.7

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.
@@ -1 +1 @@
1
- {"version":3,"file":"git.server.d.ts","sourceRoot":"","sources":["../../src/git.server.ts"],"names":[],"mappings":"AAyBA,wBAAsB,eAAe;;;;;;;;;;;;;;;GAqEpC;AAED,wBAAsB,qBAAqB;;;;;;;;;;;;;;;GAc1C;AAED,wBAAsB,eAAe;;;;;;;;;GA8CpC;AAED,wBAAsB,aAAa;;;;UAelC;AAED,wBAAsB,2BAA2B,2BAehD;AAED,wBAAsB,uBAAuB,qBAc5C"}
1
+ {"version":3,"file":"git.server.d.ts","sourceRoot":"","sources":["../../src/git.server.ts"],"names":[],"mappings":"AAuEA,wBAAsB,eAAe;;;;;;;;;;;;;;;GAqEpC;AAED,wBAAsB,qBAAqB;;;;;;;;;;;;;;;GAc1C;AAED,wBAAsB,eAAe;;;;;;;;;GAgDpC;AAED,wBAAsB,aAAa;;;;UAelC;AAED,wBAAsB,2BAA2B,2BAehD;AAED,wBAAsB,uBAAuB,qBAc5C"}
@@ -5,6 +5,43 @@ import { getWorkshopConfig } from './config.server.js';
5
5
  import { getEnv } from './env.server.js';
6
6
  import { getErrorMessage } from './utils.js';
7
7
  import { checkConnection } from './utils.server.js';
8
+ async function cleanupEmptyExerciseDirectories(cwd) {
9
+ try {
10
+ console.log('๐Ÿงน Cleaning up empty exercise directories...');
11
+ // Find all directories under exercises/* and exercises/*/*
12
+ const { stdout: allDirs } = await execaCommand('find exercises -type d 2>/dev/null || echo ""', { cwd, shell: true });
13
+ if (!allDirs.trim()) {
14
+ console.log(' No exercises directory found, skipping cleanup.');
15
+ return;
16
+ }
17
+ const directories = allDirs.trim().split('\n').filter(Boolean);
18
+ let deletedCount = 0;
19
+ for (const dir of directories) {
20
+ if (dir === 'exercises')
21
+ continue; // Skip the root exercises directory
22
+ // Check if directory has any files (excluding gitignored files)
23
+ const [trackedFiles, untrackedFiles] = await Promise.all([
24
+ execaCommand(`git ls-files "${dir}" 2>/dev/null || echo ""`, { cwd, shell: true }),
25
+ execaCommand(`git ls-files --others --exclude-standard "${dir}" 2>/dev/null || echo ""`, { cwd, shell: true })
26
+ ]);
27
+ const totalFiles = (trackedFiles.stdout.trim() + untrackedFiles.stdout.trim()).trim();
28
+ if (!totalFiles) {
29
+ console.log(` Deleting empty directory: ${dir}`);
30
+ await execaCommand(`rmdir "${dir}" 2>/dev/null || true`, { cwd, shell: true });
31
+ deletedCount++;
32
+ }
33
+ }
34
+ if (deletedCount > 0) {
35
+ console.log(` Deleted ${deletedCount} empty directories.`);
36
+ }
37
+ else {
38
+ console.log(' No empty directories found.');
39
+ }
40
+ }
41
+ catch (error) {
42
+ console.warn(' Warning: Failed to cleanup empty directories:', getErrorMessage(error));
43
+ }
44
+ }
8
45
  async function getDiffUrl(commitBefore, commitAfter) {
9
46
  const cwd = getWorkshopRoot();
10
47
  try {
@@ -109,6 +146,7 @@ export async function updateLocalRepo() {
109
146
  }
110
147
  console.log('๐Ÿ“ฆ Re-installing dependencies...');
111
148
  await execaCommand('npm install', { cwd, stdio: 'inherit' });
149
+ await cleanupEmptyExerciseDirectories(cwd);
112
150
  const postUpdateScript = getWorkshopConfig().scripts?.postupdate;
113
151
  if (postUpdateScript) {
114
152
  console.log('๐Ÿƒ Running post update script...');
@@ -1 +1 @@
1
- {"version":3,"file":"git.server.js","sourceRoot":"","sources":["../../src/git.server.ts"],"names":[],"mappings":"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","sourcesContent":["import { execa, execaCommand } from 'execa'\nimport { getWorkshopRoot } from './apps.server.js'\nimport { cachified, checkForUpdatesCache } from './cache.server.js'\nimport { getWorkshopConfig } from './config.server.js'\nimport { getEnv } from './env.server.js'\nimport { getErrorMessage } from './utils.js'\nimport { checkConnection } from './utils.server.js'\n\nasync function getDiffUrl(commitBefore: string, commitAfter: string) {\n\tconst cwd = getWorkshopRoot()\n\ttry {\n\t\tconst { stdout: remoteUrl } = await execaCommand(\n\t\t\t'git config --get remote.origin.url',\n\t\t\t{ cwd },\n\t\t)\n\t\tconst [, username, repoName] =\n\t\t\tremoteUrl.match(/(?:[^/]+\\/|:)([^/]+)\\/([^.]+)\\.git/) ?? []\n\t\tconst diffUrl = `https://github.com/${username}/${repoName}/compare/${commitBefore}...${commitAfter}`\n\t\treturn diffUrl\n\t} catch (error) {\n\t\tconsole.error('Failed to get repository info:', getErrorMessage(error))\n\t\treturn null\n\t}\n}\n\nexport async function checkForUpdates() {\n\tconst ENV = getEnv()\n\tif (ENV.EPICSHOP_DEPLOYED) {\n\t\treturn { updatesAvailable: false } as const\n\t}\n\n\tconst cwd = getWorkshopRoot()\n\tconst online = await checkConnection()\n\tif (!online) return { updatesAvailable: false } as const\n\n\tconst isInRepo = await execaCommand('git rev-parse --is-inside-work-tree', {\n\t\tcwd,\n\t}).then(\n\t\t() => true,\n\t\t() => false,\n\t)\n\tif (!isInRepo) {\n\t\treturn { updatesAvailable: false } as const\n\t}\n\n\tconst { stdout: remote } = await execaCommand('git remote', { cwd })\n\tif (!remote) {\n\t\treturn { updatesAvailable: false } as const\n\t}\n\n\tlet localCommit, remoteCommit\n\ttry {\n\t\tconst currentBranch = (\n\t\t\tawait execaCommand('git rev-parse --abbrev-ref HEAD', { cwd })\n\t\t).stdout.trim()\n\n\t\tlocalCommit = (\n\t\t\tawait execaCommand('git rev-parse --short HEAD', { cwd })\n\t\t).stdout.trim()\n\n\t\tawait execaCommand('git fetch --all', { cwd })\n\n\t\tremoteCommit = (\n\t\t\tawait execaCommand(`git rev-parse --short origin/${currentBranch}`, {\n\t\t\t\tcwd,\n\t\t\t})\n\t\t).stdout.trim()\n\n\t\tconst { stdout } = await execa(\n\t\t\t'git',\n\t\t\t['rev-list', '--count', '--left-right', 'HEAD...@{upstream}'],\n\t\t\t{ cwd },\n\t\t)\n\t\tconst [, behind = 0] = stdout.trim().split(/\\s+/).map(Number)\n\t\tconst updatesAvailable = behind > 0\n\n\t\treturn {\n\t\t\tupdatesAvailable,\n\t\t\tlocalCommit,\n\t\t\tremoteCommit,\n\t\t\tdiffLink: await getDiffUrl(localCommit, remoteCommit),\n\t\t} as const\n\t} catch (error) {\n\t\tconsole.error('Unable to check for updates', getErrorMessage(error))\n\t\treturn {\n\t\t\tupdatesAvailable: false,\n\t\t\tlocalCommit,\n\t\t\tremoteCommit,\n\t\t\tdiffLink:\n\t\t\t\tlocalCommit && remoteCommit\n\t\t\t\t\t? await getDiffUrl(localCommit, remoteCommit)\n\t\t\t\t\t: null,\n\t\t} as const\n\t}\n}\n\nexport async function checkForUpdatesCached() {\n\tconst ENV = getEnv()\n\tif (ENV.EPICSHOP_DEPLOYED) {\n\t\treturn { updatesAvailable: false } as const\n\t}\n\n\tconst key = 'checkForUpdates'\n\treturn cachified({\n\t\tttl: 1000 * 60,\n\t\tswr: 1000 * 60 * 60 * 24,\n\t\tkey,\n\t\tgetFreshValue: checkForUpdates,\n\t\tcache: checkForUpdatesCache,\n\t})\n}\n\nexport async function updateLocalRepo() {\n\tconst ENV = getEnv()\n\tif (ENV.EPICSHOP_DEPLOYED) {\n\t\treturn {\n\t\t\tstatus: 'error',\n\t\t\tmessage: 'Updates are not available in deployed environments.',\n\t\t} as const\n\t}\n\n\tconst cwd = getWorkshopRoot()\n\ttry {\n\t\tconst updates = await checkForUpdates()\n\t\tif (!updates.updatesAvailable) {\n\t\t\treturn { status: 'success', message: 'No updates available.' } as const\n\t\t}\n\n\t\tconst uncommittedChanges =\n\t\t\t(await execaCommand('git status --porcelain', { cwd })).stdout.trim()\n\t\t\t\t.length > 0\n\n\t\tif (uncommittedChanges) {\n\t\t\tconsole.log('๐Ÿ‘œ Stashing uncommitted changes...')\n\t\t\tawait execaCommand('git stash --include-untracked', { cwd })\n\t\t}\n\n\t\tconsole.log('โฌ‡๏ธ Pulling latest changes...')\n\t\tawait execaCommand('git pull origin HEAD', { cwd })\n\n\t\tif (uncommittedChanges) {\n\t\t\tconsole.log('๐Ÿ‘œ re-applying stashed changes...')\n\t\t\tawait execaCommand('git stash pop', { cwd })\n\t\t}\n\n\t\tconsole.log('๐Ÿ“ฆ Re-installing dependencies...')\n\t\tawait execaCommand('npm install', { cwd, stdio: 'inherit' })\n\n\t\tconst postUpdateScript = getWorkshopConfig().scripts?.postupdate\n\t\tif (postUpdateScript) {\n\t\t\tconsole.log('๐Ÿƒ Running post update script...')\n\t\t\tawait execaCommand(postUpdateScript, { cwd, stdio: 'inherit' })\n\t\t}\n\n\t\treturn { status: 'success', message: 'Updated successfully.' } as const\n\t} catch (error) {\n\t\treturn { status: 'error', message: getErrorMessage(error) } as const\n\t}\n}\n\nexport async function getCommitInfo() {\n\tconst cwd = getWorkshopRoot()\n\ttry {\n\t\tconst { stdout: hash } = await execaCommand('git rev-parse HEAD', { cwd })\n\t\tconst { stdout: message } = await execaCommand('git log -1 --pretty=%B', {\n\t\t\tcwd,\n\t\t})\n\t\tconst { stdout: date } = await execaCommand('git log -1 --format=%cI', {\n\t\t\tcwd,\n\t\t})\n\t\treturn { hash: hash.trim(), message: message.trim(), date: date.trim() }\n\t} catch (error) {\n\t\tconsole.error('Failed to get commit info:', getErrorMessage(error))\n\t\treturn null\n\t}\n}\n\nexport async function getLatestWorkshopAppVersion() {\n\tconst cwd = getWorkshopRoot()\n\ttry {\n\t\tconst { stdout } = await execaCommand(\n\t\t\t'npm view @epic-web/workshop-app version',\n\t\t\t{ cwd },\n\t\t)\n\t\treturn stdout.trim()\n\t} catch (error) {\n\t\tconsole.error(\n\t\t\t'Failed to get latest workshop app version:',\n\t\t\tgetErrorMessage(error),\n\t\t)\n\t\treturn null\n\t}\n}\n\nexport async function checkForExerciseChanges() {\n\tconst cwd = getWorkshopRoot()\n\ttry {\n\t\tconst { stdout } = await execaCommand('git status --porcelain exercises/', {\n\t\t\tcwd,\n\t\t})\n\t\treturn stdout.trim().length > 0\n\t} catch (error) {\n\t\tconsole.error(\n\t\t\t'Failed to check for exercise changes:',\n\t\t\tgetErrorMessage(error),\n\t\t)\n\t\treturn false\n\t}\n}\n"]}
1
+ {"version":3,"file":"git.server.js","sourceRoot":"","sources":["../../src/git.server.ts"],"names":[],"mappings":"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","sourcesContent":["import { execa, execaCommand } from 'execa'\nimport { getWorkshopRoot } from './apps.server.js'\nimport { cachified, checkForUpdatesCache } from './cache.server.js'\nimport { getWorkshopConfig } from './config.server.js'\nimport { getEnv } from './env.server.js'\nimport { getErrorMessage } from './utils.js'\nimport { checkConnection } from './utils.server.js'\n\nasync function cleanupEmptyExerciseDirectories(cwd: string) {\n\ttry {\n\t\tconsole.log('๐Ÿงน Cleaning up empty exercise directories...')\n\t\t\n\t\t// Find all directories under exercises/* and exercises/*/*\n\t\tconst { stdout: allDirs } = await execaCommand(\n\t\t\t'find exercises -type d 2>/dev/null || echo \"\"',\n\t\t\t{ cwd, shell: true }\n\t\t)\n\t\t\n\t\tif (!allDirs.trim()) {\n\t\t\tconsole.log(' No exercises directory found, skipping cleanup.')\n\t\t\treturn\n\t\t}\n\t\t\n\t\tconst directories = allDirs.trim().split('\\n').filter(Boolean)\n\t\tlet deletedCount = 0\n\t\t\n\t\tfor (const dir of directories) {\n\t\t\tif (dir === 'exercises') continue // Skip the root exercises directory\n\t\t\t\n\t\t\t// Check if directory has any files (excluding gitignored files)\n\t\t\tconst [trackedFiles, untrackedFiles] = await Promise.all([\n\t\t\t\texecaCommand(`git ls-files \"${dir}\" 2>/dev/null || echo \"\"`, { cwd, shell: true }),\n\t\t\t\texecaCommand(`git ls-files --others --exclude-standard \"${dir}\" 2>/dev/null || echo \"\"`, { cwd, shell: true })\n\t\t\t])\n\t\t\t\n\t\t\tconst totalFiles = (trackedFiles.stdout.trim() + untrackedFiles.stdout.trim()).trim()\n\t\t\t\n\t\t\tif (!totalFiles) {\n\t\t\t\tconsole.log(` Deleting empty directory: ${dir}`)\n\t\t\t\tawait execaCommand(`rmdir \"${dir}\" 2>/dev/null || true`, { cwd, shell: true })\n\t\t\t\tdeletedCount++\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (deletedCount > 0) {\n\t\t\tconsole.log(` Deleted ${deletedCount} empty directories.`)\n\t\t} else {\n\t\t\tconsole.log(' No empty directories found.')\n\t\t}\n\t} catch (error) {\n\t\tconsole.warn(' Warning: Failed to cleanup empty directories:', getErrorMessage(error))\n\t}\n}\n\nasync function getDiffUrl(commitBefore: string, commitAfter: string) {\n\tconst cwd = getWorkshopRoot()\n\ttry {\n\t\tconst { stdout: remoteUrl } = await execaCommand(\n\t\t\t'git config --get remote.origin.url',\n\t\t\t{ cwd },\n\t\t)\n\t\tconst [, username, repoName] =\n\t\t\tremoteUrl.match(/(?:[^/]+\\/|:)([^/]+)\\/([^.]+)\\.git/) ?? []\n\t\tconst diffUrl = `https://github.com/${username}/${repoName}/compare/${commitBefore}...${commitAfter}`\n\t\treturn diffUrl\n\t} catch (error) {\n\t\tconsole.error('Failed to get repository info:', getErrorMessage(error))\n\t\treturn null\n\t}\n}\n\nexport async function checkForUpdates() {\n\tconst ENV = getEnv()\n\tif (ENV.EPICSHOP_DEPLOYED) {\n\t\treturn { updatesAvailable: false } as const\n\t}\n\n\tconst cwd = getWorkshopRoot()\n\tconst online = await checkConnection()\n\tif (!online) return { updatesAvailable: false } as const\n\n\tconst isInRepo = await execaCommand('git rev-parse --is-inside-work-tree', {\n\t\tcwd,\n\t}).then(\n\t\t() => true,\n\t\t() => false,\n\t)\n\tif (!isInRepo) {\n\t\treturn { updatesAvailable: false } as const\n\t}\n\n\tconst { stdout: remote } = await execaCommand('git remote', { cwd })\n\tif (!remote) {\n\t\treturn { updatesAvailable: false } as const\n\t}\n\n\tlet localCommit, remoteCommit\n\ttry {\n\t\tconst currentBranch = (\n\t\t\tawait execaCommand('git rev-parse --abbrev-ref HEAD', { cwd })\n\t\t).stdout.trim()\n\n\t\tlocalCommit = (\n\t\t\tawait execaCommand('git rev-parse --short HEAD', { cwd })\n\t\t).stdout.trim()\n\n\t\tawait execaCommand('git fetch --all', { cwd })\n\n\t\tremoteCommit = (\n\t\t\tawait execaCommand(`git rev-parse --short origin/${currentBranch}`, {\n\t\t\t\tcwd,\n\t\t\t})\n\t\t).stdout.trim()\n\n\t\tconst { stdout } = await execa(\n\t\t\t'git',\n\t\t\t['rev-list', '--count', '--left-right', 'HEAD...@{upstream}'],\n\t\t\t{ cwd },\n\t\t)\n\t\tconst [, behind = 0] = stdout.trim().split(/\\s+/).map(Number)\n\t\tconst updatesAvailable = behind > 0\n\n\t\treturn {\n\t\t\tupdatesAvailable,\n\t\t\tlocalCommit,\n\t\t\tremoteCommit,\n\t\t\tdiffLink: await getDiffUrl(localCommit, remoteCommit),\n\t\t} as const\n\t} catch (error) {\n\t\tconsole.error('Unable to check for updates', getErrorMessage(error))\n\t\treturn {\n\t\t\tupdatesAvailable: false,\n\t\t\tlocalCommit,\n\t\t\tremoteCommit,\n\t\t\tdiffLink:\n\t\t\t\tlocalCommit && remoteCommit\n\t\t\t\t\t? await getDiffUrl(localCommit, remoteCommit)\n\t\t\t\t\t: null,\n\t\t} as const\n\t}\n}\n\nexport async function checkForUpdatesCached() {\n\tconst ENV = getEnv()\n\tif (ENV.EPICSHOP_DEPLOYED) {\n\t\treturn { updatesAvailable: false } as const\n\t}\n\n\tconst key = 'checkForUpdates'\n\treturn cachified({\n\t\tttl: 1000 * 60,\n\t\tswr: 1000 * 60 * 60 * 24,\n\t\tkey,\n\t\tgetFreshValue: checkForUpdates,\n\t\tcache: checkForUpdatesCache,\n\t})\n}\n\nexport async function updateLocalRepo() {\n\tconst ENV = getEnv()\n\tif (ENV.EPICSHOP_DEPLOYED) {\n\t\treturn {\n\t\t\tstatus: 'error',\n\t\t\tmessage: 'Updates are not available in deployed environments.',\n\t\t} as const\n\t}\n\n\tconst cwd = getWorkshopRoot()\n\ttry {\n\t\tconst updates = await checkForUpdates()\n\t\tif (!updates.updatesAvailable) {\n\t\t\treturn { status: 'success', message: 'No updates available.' } as const\n\t\t}\n\n\t\tconst uncommittedChanges =\n\t\t\t(await execaCommand('git status --porcelain', { cwd })).stdout.trim()\n\t\t\t\t.length > 0\n\n\t\tif (uncommittedChanges) {\n\t\t\tconsole.log('๐Ÿ‘œ Stashing uncommitted changes...')\n\t\t\tawait execaCommand('git stash --include-untracked', { cwd })\n\t\t}\n\n\t\tconsole.log('โฌ‡๏ธ Pulling latest changes...')\n\t\tawait execaCommand('git pull origin HEAD', { cwd })\n\n\t\tif (uncommittedChanges) {\n\t\t\tconsole.log('๐Ÿ‘œ re-applying stashed changes...')\n\t\t\tawait execaCommand('git stash pop', { cwd })\n\t\t}\n\n\t\tconsole.log('๐Ÿ“ฆ Re-installing dependencies...')\n\t\tawait execaCommand('npm install', { cwd, stdio: 'inherit' })\n\n\t\tawait cleanupEmptyExerciseDirectories(cwd)\n\n\t\tconst postUpdateScript = getWorkshopConfig().scripts?.postupdate\n\t\tif (postUpdateScript) {\n\t\t\tconsole.log('๐Ÿƒ Running post update script...')\n\t\t\tawait execaCommand(postUpdateScript, { cwd, stdio: 'inherit' })\n\t\t}\n\n\t\treturn { status: 'success', message: 'Updated successfully.' } as const\n\t} catch (error) {\n\t\treturn { status: 'error', message: getErrorMessage(error) } as const\n\t}\n}\n\nexport async function getCommitInfo() {\n\tconst cwd = getWorkshopRoot()\n\ttry {\n\t\tconst { stdout: hash } = await execaCommand('git rev-parse HEAD', { cwd })\n\t\tconst { stdout: message } = await execaCommand('git log -1 --pretty=%B', {\n\t\t\tcwd,\n\t\t})\n\t\tconst { stdout: date } = await execaCommand('git log -1 --format=%cI', {\n\t\t\tcwd,\n\t\t})\n\t\treturn { hash: hash.trim(), message: message.trim(), date: date.trim() }\n\t} catch (error) {\n\t\tconsole.error('Failed to get commit info:', getErrorMessage(error))\n\t\treturn null\n\t}\n}\n\nexport async function getLatestWorkshopAppVersion() {\n\tconst cwd = getWorkshopRoot()\n\ttry {\n\t\tconst { stdout } = await execaCommand(\n\t\t\t'npm view @epic-web/workshop-app version',\n\t\t\t{ cwd },\n\t\t)\n\t\treturn stdout.trim()\n\t} catch (error) {\n\t\tconsole.error(\n\t\t\t'Failed to get latest workshop app version:',\n\t\t\tgetErrorMessage(error),\n\t\t)\n\t\treturn null\n\t}\n}\n\nexport async function checkForExerciseChanges() {\n\tconst cwd = getWorkshopRoot()\n\ttry {\n\t\tconst { stdout } = await execaCommand('git status --porcelain exercises/', {\n\t\t\tcwd,\n\t\t})\n\t\treturn stdout.trim().length > 0\n\t} catch (error) {\n\t\tconsole.error(\n\t\t\t'Failed to check for exercise changes:',\n\t\t\tgetErrorMessage(error),\n\t\t)\n\t\treturn false\n\t}\n}\n"]}
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@epic-web/workshop-utils","version":"6.11.4","publishConfig":{"access":"public"},"type":"module","tshy":{"project":"./tsconfig.build.json","dialects":["esm"],"exports":{"./package.json":"./package.json","./apps.server":"./src/apps.server.ts","./diff.server":"./src/diff.server.ts","./env.server":"./src/env.server.ts","./epic-api.server":"./src/epic-api.server.ts","./user.server":"./src/user.server.ts","./cache.server":"./src/cache.server.ts","./config.server":"./src/config.server.ts","./db.server":"./src/db.server.ts","./timing.server":"./src/timing.server.ts","./modified-time.server":"./src/modified-time.server.ts","./compile-mdx.server":"./src/compile-mdx.server.ts","./git.server":"./src/git.server.ts","./iframe-sync":"./src/iframe-sync.ts","./playwright.server":"./src/playwright.server.ts","./notifications.server":"./src/notifications.server.ts","./process-manager.server":"./src/process-manager.server.ts","./test":"./src/test.ts","./utils.server":"./src/utils.server.ts","./utils":"./src/utils.ts"}},"exports":{"./package.json":"./package.json","./apps.server":{"import":{"types":"./dist/esm/apps.server.d.ts","default":"./dist/esm/apps.server.js"}},"./diff.server":{"import":{"types":"./dist/esm/diff.server.d.ts","default":"./dist/esm/diff.server.js"}},"./env.server":{"import":{"types":"./dist/esm/env.server.d.ts","default":"./dist/esm/env.server.js"}},"./epic-api.server":{"import":{"types":"./dist/esm/epic-api.server.d.ts","default":"./dist/esm/epic-api.server.js"}},"./user.server":{"import":{"types":"./dist/esm/user.server.d.ts","default":"./dist/esm/user.server.js"}},"./cache.server":{"import":{"types":"./dist/esm/cache.server.d.ts","default":"./dist/esm/cache.server.js"}},"./config.server":{"import":{"types":"./dist/esm/config.server.d.ts","default":"./dist/esm/config.server.js"}},"./db.server":{"import":{"types":"./dist/esm/db.server.d.ts","default":"./dist/esm/db.server.js"}},"./timing.server":{"import":{"types":"./dist/esm/timing.server.d.ts","default":"./dist/esm/timing.server.js"}},"./modified-time.server":{"import":{"types":"./dist/esm/modified-time.server.d.ts","default":"./dist/esm/modified-time.server.js"}},"./compile-mdx.server":{"import":{"types":"./dist/esm/compile-mdx.server.d.ts","default":"./dist/esm/compile-mdx.server.js"}},"./git.server":{"import":{"types":"./dist/esm/git.server.d.ts","default":"./dist/esm/git.server.js"}},"./iframe-sync":{"import":{"types":"./dist/esm/iframe-sync.d.ts","default":"./dist/esm/iframe-sync.js"}},"./playwright.server":{"import":{"types":"./dist/esm/playwright.server.d.ts","default":"./dist/esm/playwright.server.js"}},"./notifications.server":{"import":{"types":"./dist/esm/notifications.server.d.ts","default":"./dist/esm/notifications.server.js"}},"./process-manager.server":{"import":{"types":"./dist/esm/process-manager.server.d.ts","default":"./dist/esm/process-manager.server.js"}},"./test":{"import":{"types":"./dist/esm/test.d.ts","default":"./dist/esm/test.js"}},"./utils.server":{"import":{"types":"./dist/esm/utils.server.d.ts","default":"./dist/esm/utils.server.js"}},"./utils":{"import":{"types":"./dist/esm/utils.d.ts","default":"./dist/esm/utils.js"}}},"files":["dist"],"scripts":{"typecheck":"tsc --noEmit","build":"tshy","build:watch":"nx watch --projects=@epic-web/workshop-utils -- nx run \\$NX_PROJECT_NAME:build"},"dependencies":{"@epic-web/cachified":"^5.6.0","@epic-web/invariant":"^1.0.0","@epic-web/remember":"^1.1.0","@kentcdodds/md-temp":"^10.0.1","@mdx-js/mdx":"^3.1.0","@paralleldrive/cuid2":"^2.2.2","@playwright/test":"^1.53.2","@react-router/node":"^7.6.3","@testing-library/dom":"^10.4.0","@testing-library/jest-dom":"^6.6.3","@total-typescript/ts-reset":"^0.6.1","@types/chai":"^5.2.2","@types/chai-dom":"^1.11.3","@vitest/expect":"^3.2.4","chai":"^5.2.0","chai-dom":"^1.12.1","chalk":"^5.4.1","chokidar":"^4.0.3","close-with-grace":"^2.2.0","cookie":"^1.0.2","cross-spawn":"^7.0.6","dayjs":"^1.11.13","esbuild":"^0.25.5","execa":"^9.6.0","find-process":"^1.4.10","fkill":"^9.0.0","fs-extra":"^11.3.0","globby":"^14.1.0","ignore":"^7.0.5","json5":"^2.2.3","lru-cache":"^11.1.0","lz-string":"^1.5.0","md5-hex":"^5.0.0","mdast-util-mdx-jsx":"^3.2.0","mdx-bundler":"^10.1.1","p-queue":"^8.1.0","parse-git-diff":"^0.0.19","rehype":"^13.0.2","rehype-autolink-headings":"^7.1.0","remark":"^15.0.1","remark-emoji":"^5.0.1","remark-gfm":"^4.0.1","shiki":"^3.7.0","unified":"^11.0.5","unist-util-remove-position":"^5.0.0","unist-util-visit":"^5.0.0","zod":"^3.25.71"},"devDependencies":{"@types/hast":"^3.0.4","@types/mdast":"^4.0.4","@types/node":"^24.0.10","tshy":"^3.0.2"},"repository":{"type":"git","url":"https://github.com/epicweb-dev/epicshop.git","directory":"packages/workshop-utils"}}
1
+ {"name":"@epic-web/workshop-utils","version":"6.11.7","publishConfig":{"access":"public"},"type":"module","tshy":{"project":"./tsconfig.build.json","dialects":["esm"],"exports":{"./package.json":"./package.json","./apps.server":"./src/apps.server.ts","./diff.server":"./src/diff.server.ts","./env.server":"./src/env.server.ts","./epic-api.server":"./src/epic-api.server.ts","./user.server":"./src/user.server.ts","./cache.server":"./src/cache.server.ts","./config.server":"./src/config.server.ts","./db.server":"./src/db.server.ts","./timing.server":"./src/timing.server.ts","./modified-time.server":"./src/modified-time.server.ts","./compile-mdx.server":"./src/compile-mdx.server.ts","./git.server":"./src/git.server.ts","./iframe-sync":"./src/iframe-sync.ts","./playwright.server":"./src/playwright.server.ts","./notifications.server":"./src/notifications.server.ts","./process-manager.server":"./src/process-manager.server.ts","./test":"./src/test.ts","./utils.server":"./src/utils.server.ts","./utils":"./src/utils.ts"}},"exports":{"./package.json":"./package.json","./apps.server":{"import":{"types":"./dist/esm/apps.server.d.ts","default":"./dist/esm/apps.server.js"}},"./diff.server":{"import":{"types":"./dist/esm/diff.server.d.ts","default":"./dist/esm/diff.server.js"}},"./env.server":{"import":{"types":"./dist/esm/env.server.d.ts","default":"./dist/esm/env.server.js"}},"./epic-api.server":{"import":{"types":"./dist/esm/epic-api.server.d.ts","default":"./dist/esm/epic-api.server.js"}},"./user.server":{"import":{"types":"./dist/esm/user.server.d.ts","default":"./dist/esm/user.server.js"}},"./cache.server":{"import":{"types":"./dist/esm/cache.server.d.ts","default":"./dist/esm/cache.server.js"}},"./config.server":{"import":{"types":"./dist/esm/config.server.d.ts","default":"./dist/esm/config.server.js"}},"./db.server":{"import":{"types":"./dist/esm/db.server.d.ts","default":"./dist/esm/db.server.js"}},"./timing.server":{"import":{"types":"./dist/esm/timing.server.d.ts","default":"./dist/esm/timing.server.js"}},"./modified-time.server":{"import":{"types":"./dist/esm/modified-time.server.d.ts","default":"./dist/esm/modified-time.server.js"}},"./compile-mdx.server":{"import":{"types":"./dist/esm/compile-mdx.server.d.ts","default":"./dist/esm/compile-mdx.server.js"}},"./git.server":{"import":{"types":"./dist/esm/git.server.d.ts","default":"./dist/esm/git.server.js"}},"./iframe-sync":{"import":{"types":"./dist/esm/iframe-sync.d.ts","default":"./dist/esm/iframe-sync.js"}},"./playwright.server":{"import":{"types":"./dist/esm/playwright.server.d.ts","default":"./dist/esm/playwright.server.js"}},"./notifications.server":{"import":{"types":"./dist/esm/notifications.server.d.ts","default":"./dist/esm/notifications.server.js"}},"./process-manager.server":{"import":{"types":"./dist/esm/process-manager.server.d.ts","default":"./dist/esm/process-manager.server.js"}},"./test":{"import":{"types":"./dist/esm/test.d.ts","default":"./dist/esm/test.js"}},"./utils.server":{"import":{"types":"./dist/esm/utils.server.d.ts","default":"./dist/esm/utils.server.js"}},"./utils":{"import":{"types":"./dist/esm/utils.d.ts","default":"./dist/esm/utils.js"}}},"files":["dist"],"scripts":{"typecheck":"tsc --noEmit","build":"tshy","build:watch":"nx watch --projects=@epic-web/workshop-utils -- nx run \\$NX_PROJECT_NAME:build"},"dependencies":{"@epic-web/cachified":"^5.6.0","@epic-web/invariant":"^1.0.0","@epic-web/remember":"^1.1.0","@kentcdodds/md-temp":"^10.0.1","@mdx-js/mdx":"^3.1.0","@paralleldrive/cuid2":"^2.2.2","@playwright/test":"^1.53.2","@react-router/node":"^7.6.3","@testing-library/dom":"^10.4.0","@testing-library/jest-dom":"^6.6.3","@total-typescript/ts-reset":"^0.6.1","@types/chai":"^5.2.2","@types/chai-dom":"^1.11.3","@vitest/expect":"^3.2.4","chai":"^5.2.0","chai-dom":"^1.12.1","chalk":"^5.4.1","chokidar":"^4.0.3","close-with-grace":"^2.2.0","cookie":"^1.0.2","cross-spawn":"^7.0.6","dayjs":"^1.11.13","esbuild":"^0.25.5","execa":"^9.6.0","find-process":"^1.4.10","fkill":"^9.0.0","fs-extra":"^11.3.0","globby":"^14.1.0","ignore":"^7.0.5","json5":"^2.2.3","lru-cache":"^11.1.0","lz-string":"^1.5.0","md5-hex":"^5.0.0","mdast-util-mdx-jsx":"^3.2.0","mdx-bundler":"^10.1.1","p-queue":"^8.1.0","parse-git-diff":"^0.0.19","rehype":"^13.0.2","rehype-autolink-headings":"^7.1.0","remark":"^15.0.1","remark-emoji":"^5.0.1","remark-gfm":"^4.0.1","shiki":"^3.7.0","unified":"^11.0.5","unist-util-remove-position":"^5.0.0","unist-util-visit":"^5.0.0","zod":"^3.25.71"},"devDependencies":{"@types/hast":"^3.0.4","@types/mdast":"^4.0.4","@types/node":"^24.0.10","tshy":"^3.0.2"},"repository":{"type":"git","url":"https://github.com/epicweb-dev/epicshop.git","directory":"packages/workshop-utils"}}