Merge branch 'feature/pull-project' of git.digitaltelepresence.com:rob/gadget into feature/pull-project

This commit is contained in:
Rob Colbert 2026-05-15 19:38:36 -04:00
commit 4df5e6dec7
7 changed files with 517 additions and 6 deletions

View File

@ -246,6 +246,8 @@ export const projectApi = {
get: (id: string) => api.get<Project>(`/api/v1/projects/${id}`), get: (id: string) => api.get<Project>(`/api/v1/projects/${id}`),
create: (data: { name: string; slug: string; gitUrl?: string }) => create: (data: { name: string; slug: string; gitUrl?: string }) =>
api.post<Project>("/api/v1/projects", data), api.post<Project>("/api/v1/projects", data),
pull: (gitUrl: string) =>
api.post<Project>("/api/v1/projects/pull", { gitUrl }),
update: ( update: (
id: string, id: string,
data: Partial<{ data: Partial<{

View File

@ -116,6 +116,83 @@ function NewProjectForm({
); );
} }
function PullProjectForm({
onCancel,
onSuccess,
}: {
onCancel: () => void;
onSuccess: (project: Project) => void;
}) {
const [gitUrl, setGitUrl] = useState("");
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState("");
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!gitUrl.trim()) {
setError("Git Repository URL is required");
return;
}
setSubmitting(true);
setError("");
try {
const project = await projectApi.pull(gitUrl.trim());
onSuccess(project);
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to pull project");
} finally {
setSubmitting(false);
}
};
return (
<div className="max-w-lg">
<h2 className="text-xl font-semibold mb-2">Pull New Project</h2>
<p className="text-sm text-text-muted mb-6">
Import an existing project from a git repository. The project name and
slug will be derived automatically from the repository URL.
</p>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm text-text-secondary mb-1">
Git Repository URL *
</label>
<input
type="text"
value={gitUrl}
onChange={(e) => setGitUrl(e.target.value)}
className="w-full px-3 py-2 bg-bg-tertiary border border-border-default rounded text-text-primary focus:border-brand focus:outline-none"
placeholder="https://github.com/user/repo.git"
autoFocus
/>
<p className="text-xs text-text-muted mt-1">
HTTPS or SSH format (e.g., git@github.com:user/repo.git)
</p>
</div>
{error && <p className="text-red-500 text-sm">{error}</p>}
<div className="flex gap-3 pt-2">
<button
type="submit"
disabled={submitting}
className="px-4 py-2 bg-brand text-white rounded hover:bg-red-700 transition-colors disabled:opacity-50"
>
{submitting ? "Pulling..." : "Pull Project"}
</button>
<button
type="button"
onClick={onCancel}
className="px-4 py-2 border border-border-default text-text-secondary rounded hover:bg-bg-tertiary transition-colors"
>
Cancel
</button>
</div>
</form>
</div>
);
}
interface EditProjectFormProps { interface EditProjectFormProps {
project: Project; project: Project;
onCancel: () => void; onCancel: () => void;
@ -820,6 +897,7 @@ export default function ProjectManager({ user }: ProjectManagerProps) {
); );
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [showNewForm, setShowNewForm] = useState(false); const [showNewForm, setShowNewForm] = useState(false);
const [showPullForm, setShowPullForm] = useState(false);
useEffect(() => { useEffect(() => {
loadProjects(); loadProjects();
@ -854,6 +932,12 @@ export default function ProjectManager({ user }: ProjectManagerProps) {
loadProjects(); loadProjects();
}; };
const handleProjectPulled = (project: Project) => {
setShowPullForm(false);
loadProjects();
navigate(`/projects/${project.slug}`);
};
const handleProjectDeleted = () => { const handleProjectDeleted = () => {
setSelectedProject(null); setSelectedProject(null);
loadProjects(); loadProjects();
@ -908,26 +992,43 @@ export default function ProjectManager({ user }: ProjectManagerProps) {
); );
} }
const showForm = showNewForm || showPullForm;
return ( return (
<div className="flex-1 flex bg-bg-primary overflow-hidden"> <div className="flex-1 flex bg-bg-primary overflow-hidden">
{/* Left Sidebar - Project List */} {/* Left Sidebar - Project List */}
<aside className="w-64 border-r border-border-subtle bg-bg-secondary flex flex-col overflow-hidden"> <aside className="w-64 border-r border-border-subtle bg-bg-secondary flex flex-col overflow-hidden">
<div className="p-3 border-b border-border-subtle flex-shrink-0"> <div className="p-3 border-b border-border-subtle flex-shrink-0 space-y-2">
<button <button
onClick={() => setShowNewForm(true)} onClick={() => {
setShowNewForm(true);
setShowPullForm(false);
}}
className="w-full px-3 py-2 bg-brand text-white rounded hover:bg-red-700 transition-colors text-sm font-medium" className="w-full px-3 py-2 bg-brand text-white rounded hover:bg-red-700 transition-colors text-sm font-medium"
> >
New Project New Project
</button> </button>
<button
onClick={() => {
setShowPullForm(true);
setShowNewForm(false);
}}
className="w-full px-3 py-2 border border-border-default text-text-secondary rounded hover:bg-bg-tertiary hover:text-text-primary transition-colors text-sm font-medium"
>
Pull Project
</button>
</div> </div>
<div className="flex-1 overflow-y-auto p-2"> <div className="flex-1 overflow-y-auto p-2">
{showNewForm ? ( {showForm ? (
<div className="p-2"> <div className="p-2">
<p className="text-sm text-text-muted mb-2"> <p className="text-sm text-text-muted mb-2">
Creating new project... {showNewForm ? "Creating new project..." : "Pulling project..."}
</p> </p>
<button <button
onClick={() => setShowNewForm(false)} onClick={() => {
setShowNewForm(false);
setShowPullForm(false);
}}
className="text-sm text-text-secondary hover:text-text-primary transition-colors" className="text-sm text-text-secondary hover:text-text-primary transition-colors"
> >
Cancel Cancel
@ -971,6 +1072,13 @@ export default function ProjectManager({ user }: ProjectManagerProps) {
onSuccess={handleProjectCreated} onSuccess={handleProjectCreated}
/> />
</div> </div>
) : showPullForm ? (
<div className="flex-1 overflow-y-auto p-6">
<PullProjectForm
onCancel={() => setShowPullForm(false)}
onSuccess={handleProjectPulled}
/>
</div>
) : selectedProject ? ( ) : selectedProject ? (
<> <>
{/* Center - Project Inspector */} {/* Center - Project Inspector */}

View File

@ -28,6 +28,7 @@ export class ProjectApiControllerV1 extends DtpController {
this.router.get("/", this.getProjects.bind(this)); this.router.get("/", this.getProjects.bind(this));
this.router.post("/", this.createProject.bind(this)); this.router.post("/", this.createProject.bind(this));
this.router.post("/pull", this.pullProject.bind(this));
this.router.get("/:projectId", this.getProject.bind(this)); this.router.get("/:projectId", this.getProject.bind(this));
this.router.put("/:projectId", this.updateProject.bind(this)); this.router.put("/:projectId", this.updateProject.bind(this));
this.router.delete("/:projectId", this.deleteProject.bind(this)); this.router.delete("/:projectId", this.deleteProject.bind(this));
@ -81,6 +82,65 @@ export class ProjectApiControllerV1 extends DtpController {
} }
} }
async pullProject(req: Request, res: Response): Promise<void> {
try {
const { gitUrl } = req.body;
if (!gitUrl || typeof gitUrl !== "string") {
res.status(400).json({
success: false,
message: "gitUrl is required",
});
return;
}
// Validate the URL is parseable
const trimmedUrl = gitUrl.trim();
const isSshFormat = trimmedUrl.includes(":") && !trimmedUrl.startsWith("http") && !trimmedUrl.startsWith("ssh://");
if (!isSshFormat) {
try {
new URL(trimmedUrl);
} catch {
res.status(400).json({
success: false,
message: "invalid git URL format",
});
return;
}
}
const project = await projectService.createFromGitUrl(req.user, trimmedUrl);
res.status(201).json({
success: true,
data: project,
});
} catch (error: any) {
if (error.statusCode === 400) {
res.status(400).json({
success: false,
message: error.message,
});
return;
}
// Handle duplicate gitUrl (unique partial index violation)
if (error.code === 11000) {
res.status(409).json({
success: false,
message: "a project with this git URL already exists",
});
return;
}
this.log.error("failed to pull project", { error });
res.status(500).json({
success: false,
message: "failed to pull project",
});
}
}
async getProject(req: Request, res: Response): Promise<void> { async getProject(req: Request, res: Response): Promise<void> {
try { try {
const id = req.params.projectId as string; const id = req.params.projectId as string;

View File

@ -47,6 +47,69 @@ class ProjectService extends DtpService {
this.log.info("service stopped"); this.log.info("service stopped");
} }
/**
* Parse a git URL to extract the repository name.
* Handles common formats:
* https://github.com/user/repo.git
* https://github.com/user/repo
* git@github.com:user/repo.git
* ssh://git@github.com/user/repo.git
*/
parseGitUrl(gitUrl: string): { name: string; slug: string } {
const url = gitUrl.trim();
let repoName: string;
// SSH format: git@host:owner/repo.git
if (url.includes(":") && !url.startsWith("http") && !url.startsWith("ssh://")) {
const parts = url.split(":").pop()!;
repoName = parts.split("/").pop()!;
} else {
// HTTP(S) or ssh:// format
const parsed = new URL(url);
const segments = parsed.pathname.split("/").filter(Boolean);
repoName = segments[segments.length - 1] || "";
}
// Strip .git suffix
repoName = repoName.replace(/\.git$/, "");
return {
name: repoName,
slug: slug(repoName),
};
}
/**
* Create a project from a git URL by parsing the URL to derive
* the project name and slug automatically. Handles slug collisions
* by appending a numeric suffix (e.g., my-repo-2).
*/
async createFromGitUrl(user: IUser, gitUrl: string): Promise<IProject> {
const { name, slug: baseSlug } = this.parseGitUrl(gitUrl);
if (!name) {
const error = new Error("could not derive project name from git URL");
error.statusCode = 400;
throw error;
}
// Resolve slug collisions by appending a numeric suffix
let projectSlug = baseSlug;
let suffix = 2;
while (await Project.findOne({ slug: projectSlug, user: user._id })) {
projectSlug = `${baseSlug}-${suffix}`;
suffix++;
}
this.log.info("creating project from git URL", { name, slug: projectSlug, gitUrl });
return this.create(user, {
name,
slug: projectSlug,
gitUrl,
});
}
async create(user: IUser, definition: IProjectDefinition): Promise<IProject> { async create(user: IUser, definition: IProjectDefinition): Promise<IProject> {
const NOW = new Date(); const NOW = new Date();

View File

@ -1,4 +1,4 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect, beforeAll } from 'vitest';
import fs from 'node:fs'; import fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
@ -23,6 +23,23 @@ describe('Project API Endpoints', () => {
expect(content).toContain('createProject'); expect(content).toContain('createProject');
}); });
it('should have POST /pull route', () => {
const controllerPath = path.join(ROOT_DIR, 'src', 'controllers', 'api', 'v1', 'project.ts');
const content = fs.readFileSync(controllerPath, 'utf-8');
expect(content).toContain('pullProject');
expect(content).toContain('/pull');
});
it('should register /pull route before /:projectId', () => {
const controllerPath = path.join(ROOT_DIR, 'src', 'controllers', 'api', 'v1', 'project.ts');
const content = fs.readFileSync(controllerPath, 'utf-8');
const pullIndex = content.indexOf('"/pull"');
const paramIndex = content.indexOf('"/:projectId"');
expect(pullIndex).toBeGreaterThan(0);
expect(paramIndex).toBeGreaterThan(0);
expect(pullIndex).toBeLessThan(paramIndex);
});
it('should use requireUser middleware', () => { it('should use requireUser middleware', () => {
const controllerPath = path.join(ROOT_DIR, 'src', 'controllers', 'api', 'v1', 'project.ts'); const controllerPath = path.join(ROOT_DIR, 'src', 'controllers', 'api', 'v1', 'project.ts');
const content = fs.readFileSync(controllerPath, 'utf-8'); const content = fs.readFileSync(controllerPath, 'utf-8');
@ -55,6 +72,18 @@ describe('Project API Endpoints', () => {
expect(content).toContain('getForUser'); expect(content).toContain('getForUser');
}); });
it('should have createFromGitUrl method', () => {
const servicePath = path.join(ROOT_DIR, 'src', 'services', 'project.ts');
const content = fs.readFileSync(servicePath, 'utf-8');
expect(content).toContain('createFromGitUrl');
});
it('should have parseGitUrl method', () => {
const servicePath = path.join(ROOT_DIR, 'src', 'services', 'project.ts');
const content = fs.readFileSync(servicePath, 'utf-8');
expect(content).toContain('parseGitUrl');
});
it('should populate user with password excluded', () => { it('should populate user with password excluded', () => {
const servicePath = path.join(ROOT_DIR, 'src', 'services', 'project.ts'); const servicePath = path.join(ROOT_DIR, 'src', 'services', 'project.ts');
const content = fs.readFileSync(servicePath, 'utf-8'); const content = fs.readFileSync(servicePath, 'utf-8');
@ -62,6 +91,60 @@ describe('Project API Endpoints', () => {
}); });
}); });
describe('Git URL Parsing', () => {
// We import the service to test parseGitUrl directly
let projectService: any;
beforeAll(async () => {
const mod = await import(path.join(ROOT_DIR, 'src', 'services', 'project.ts'));
projectService = mod.default;
});
it('should parse HTTPS URL with .git suffix', () => {
const result = projectService.parseGitUrl('https://github.com/user/my-repo.git');
expect(result.name).toBe('my-repo');
expect(result.slug).toBe('my-repo');
});
it('should parse HTTPS URL without .git suffix', () => {
const result = projectService.parseGitUrl('https://github.com/user/my-repo');
expect(result.name).toBe('my-repo');
expect(result.slug).toBe('my-repo');
});
it('should parse SSH format URL', () => {
const result = projectService.parseGitUrl('git@github.com:user/my-repo.git');
expect(result.name).toBe('my-repo');
expect(result.slug).toBe('my-repo');
});
it('should parse SSH URL with ssh:// prefix', () => {
const result = projectService.parseGitUrl('ssh://git@github.com/user/my-repo.git');
expect(result.name).toBe('my-repo');
expect(result.slug).toBe('my-repo');
});
it('should handle repo names with special characters', () => {
const result = projectService.parseGitUrl('https://github.com/user/my_cool_repo.git');
expect(result.name).toBe('my_cool_repo');
// slug() removes underscores entirely
expect(result.slug).toBe('mycoolrepo');
});
it('should handle repo names with mixed case', () => {
const result = projectService.parseGitUrl('https://github.com/user/MyRepo.git');
expect(result.name).toBe('MyRepo');
// slug() lowercases
expect(result.slug).toBe('myrepo');
});
it('should trim whitespace from URL', () => {
const result = projectService.parseGitUrl(' https://github.com/user/my-repo.git ');
expect(result.name).toBe('my-repo');
expect(result.slug).toBe('my-repo');
});
});
describe('Frontend API Client', () => { describe('Frontend API Client', () => {
it('should add Authorization header with token', () => { it('should add Authorization header with token', () => {
const apiPath = path.join(ROOT_DIR, 'frontend', 'src', 'lib', 'api.ts'); const apiPath = path.join(ROOT_DIR, 'frontend', 'src', 'lib', 'api.ts');
@ -81,6 +164,39 @@ describe('Project API Endpoints', () => {
const content = fs.readFileSync(apiPath, 'utf-8'); const content = fs.readFileSync(apiPath, 'utf-8');
expect(content).toContain('getAll'); expect(content).toContain('getAll');
}); });
it('should have pull method for project API', () => {
const apiPath = path.join(ROOT_DIR, 'frontend', 'src', 'lib', 'api.ts');
const content = fs.readFileSync(apiPath, 'utf-8');
expect(content).toContain('pull:');
expect(content).toContain('/projects/pull');
});
});
describe('Pull Project UI', () => {
it('should have PullProjectForm component', () => {
const uiPath = path.join(ROOT_DIR, 'frontend', 'src', 'pages', 'ProjectManager.tsx');
const content = fs.readFileSync(uiPath, 'utf-8');
expect(content).toContain('PullProjectForm');
});
it('should have Pull Project button in sidebar', () => {
const uiPath = path.join(ROOT_DIR, 'frontend', 'src', 'pages', 'ProjectManager.tsx');
const content = fs.readFileSync(uiPath, 'utf-8');
expect(content).toContain('Pull Project');
});
it('should have showPullForm state', () => {
const uiPath = path.join(ROOT_DIR, 'frontend', 'src', 'pages', 'ProjectManager.tsx');
const content = fs.readFileSync(uiPath, 'utf-8');
expect(content).toContain('showPullForm');
});
it('should have handleProjectPulled handler', () => {
const uiPath = path.join(ROOT_DIR, 'frontend', 'src', 'pages', 'ProjectManager.tsx');
const content = fs.readFileSync(uiPath, 'utf-8');
expect(content).toContain('handleProjectPulled');
});
}); });
describe('User Interface', () => { describe('User Interface', () => {

View File

@ -37,6 +37,7 @@
"openai": "^6.34.0", "openai": "^6.34.0",
"playwright": "1.59.1", "playwright": "1.59.1",
"simple-git": "^3.36.0", "simple-git": "^3.36.0",
"simplegit": "^1.0.2",
"socket.io-client": "^4.8.3", "socket.io-client": "^4.8.3",
"turndown": "7.2.2" "turndown": "7.2.2"
}, },

View File

@ -121,6 +121,9 @@ importers:
serve-favicon: serve-favicon:
specifier: ^2.5.1 specifier: ^2.5.1
version: 2.5.1 version: 2.5.1
simplegit:
specifier: ^1.0.2
version: 1.0.2
slug: slug:
specifier: ^11.0.1 specifier: ^11.0.1
version: 11.0.1 version: 11.0.1
@ -405,6 +408,9 @@ importers:
simple-git: simple-git:
specifier: ^3.36.0 specifier: ^3.36.0
version: 3.36.0 version: 3.36.0
simplegit:
specifier: ^1.0.2
version: 1.0.2
socket.io-client: socket.io-client:
specifier: ^4.8.3 specifier: ^4.8.3
version: 4.8.3 version: 4.8.3
@ -1830,6 +1836,10 @@ packages:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'} engines: {node: '>= 14'}
ansi-regex@2.1.1:
resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==}
engines: {node: '>=0.10.0'}
ansi-regex@5.0.1: ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -1880,6 +1890,9 @@ packages:
resolution: {integrity: sha512-p4jj6Fws4Iy2m0iCmI2am2ZNZCgbdgE+P8F/8csmn2vx7ixXrO2zGcuNsD46X5uZSVecmkEy/M06X2vG8KD6dQ==} resolution: {integrity: sha512-p4jj6Fws4Iy2m0iCmI2am2ZNZCgbdgE+P8F/8csmn2vx7ixXrO2zGcuNsD46X5uZSVecmkEy/M06X2vG8KD6dQ==}
engines: {node: '>=0.8.0'} engines: {node: '>=0.8.0'}
async@1.5.2:
resolution: {integrity: sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==}
async@2.6.4: async@2.6.4:
resolution: {integrity: sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==} resolution: {integrity: sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==}
@ -2006,6 +2019,10 @@ packages:
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
camelcase@2.1.1:
resolution: {integrity: sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==}
engines: {node: '>=0.10.0'}
camera-controls@3.1.2: camera-controls@3.1.2:
resolution: {integrity: sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==} resolution: {integrity: sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==}
engines: {node: '>=22.0.0', npm: '>=10.5.1'} engines: {node: '>=22.0.0', npm: '>=10.5.1'}
@ -2041,6 +2058,9 @@ packages:
resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==}
engines: {node: '>= 12'} engines: {node: '>= 12'}
cliui@3.2.0:
resolution: {integrity: sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==}
cliui@8.0.1: cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -2049,6 +2069,10 @@ packages:
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
code-point-at@1.1.0:
resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==}
engines: {node: '>=0.10.0'}
codemirror@6.0.2: codemirror@6.0.2:
resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==}
@ -2205,6 +2229,10 @@ packages:
supports-color: supports-color:
optional: true optional: true
decamelize@1.2.0:
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
engines: {node: '>=0.10.0'}
decimal.js@10.6.0: decimal.js@10.6.0:
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
@ -2661,6 +2689,13 @@ packages:
inherits@2.0.4: inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
ini@1.3.8:
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
invert-kv@1.0.0:
resolution: {integrity: sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==}
engines: {node: '>=0.10.0'}
ioredis@5.10.1: ioredis@5.10.1:
resolution: {integrity: sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==} resolution: {integrity: sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==}
engines: {node: '>=12.22.0'} engines: {node: '>=12.22.0'}
@ -2692,6 +2727,10 @@ packages:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
is-fullwidth-code-point@1.0.0:
resolution: {integrity: sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==}
engines: {node: '>=0.10.0'}
is-fullwidth-code-point@3.0.0: is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -2801,6 +2840,10 @@ packages:
resolution: {integrity: sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA==} resolution: {integrity: sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA==}
engines: {node: '>=0.2.0'} engines: {node: '>=0.2.0'}
lcid@1.0.0:
resolution: {integrity: sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==}
engines: {node: '>=0.10.0'}
less@4.6.4: less@4.6.4:
resolution: {integrity: sha512-OJmO5+HxZLLw0RLzkqaNHzcgEAQG7C0y3aMbwtCzIUFZsLMNNq/1IdAdHEycQ58CwUO3jPTHmoN+tE5I7FQxNg==} resolution: {integrity: sha512-OJmO5+HxZLLw0RLzkqaNHzcgEAQG7C0y3aMbwtCzIUFZsLMNNq/1IdAdHEycQ58CwUO3jPTHmoN+tE5I7FQxNg==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -3111,6 +3154,10 @@ packages:
engines: {node: ^18 || >=20} engines: {node: ^18 || >=20}
hasBin: true hasBin: true
nconf@0.8.5:
resolution: {integrity: sha512-YXTpOk2LI4QD2vAr4v40+nELcEbUA5BkIoeIz4Orfx9XA0Gxkkf70+KOvIVNDP2YQBz5XWg7l1zgiPvWb0zTPw==}
engines: {node: '>= 0.4.0'}
needle@3.5.0: needle@3.5.0:
resolution: {integrity: sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==} resolution: {integrity: sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==}
engines: {node: '>= 4.4.x'} engines: {node: '>= 4.4.x'}
@ -3152,6 +3199,10 @@ packages:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
number-is-nan@1.0.1:
resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==}
engines: {node: '>=0.10.0'}
numeral@2.0.6: numeral@2.0.6:
resolution: {integrity: sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==} resolution: {integrity: sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==}
@ -3200,6 +3251,10 @@ packages:
resolution: {integrity: sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==} resolution: {integrity: sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==}
engines: {node: '>=4'} engines: {node: '>=4'}
os-locale@1.4.0:
resolution: {integrity: sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==}
engines: {node: '>=0.10.0'}
parse-node-version@1.0.1: parse-node-version@1.0.1:
resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==}
engines: {node: '>= 0.10'} engines: {node: '>= 0.10'}
@ -3515,6 +3570,9 @@ packages:
scheduler@0.27.0: scheduler@0.27.0:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
secure-keys@1.0.0:
resolution: {integrity: sha512-nZi59hW3Sl5P3+wOO89eHBAAGwmCPd2aE1+dLZV5MO+ItQctIvAqihzaAXIQhvtH4KJPxM080HsnqltR2y8cWg==}
semver@5.7.2: semver@5.7.2:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true hasBin: true
@ -3597,6 +3655,10 @@ packages:
simple-git@3.36.0: simple-git@3.36.0:
resolution: {integrity: sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==} resolution: {integrity: sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==}
simplegit@1.0.2:
resolution: {integrity: sha512-jEQKokh61NP+oqj7oFpXtd29zOSO8rR1X1Rs11tNtBLxLhrR976zBnRmzR6zZes7MkqhVUtS1QnyiuBxGDLDrg==}
hasBin: true
slash@3.0.0: slash@3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -3691,6 +3753,10 @@ packages:
resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==}
engines: {node: '>=4'} engines: {node: '>=4'}
string-width@1.0.2:
resolution: {integrity: sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==}
engines: {node: '>=0.10.0'}
string-width@4.2.3: string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -3698,6 +3764,10 @@ packages:
string_decoder@1.3.0: string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
strip-ansi@3.0.1:
resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==}
engines: {node: '>=0.10.0'}
strip-ansi@6.0.1: strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -4060,10 +4130,19 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
hasBin: true hasBin: true
window-size@0.1.4:
resolution: {integrity: sha512-2thx4pB0cV3h+Bw7QmMXcEbdmOzv9t0HFplJH/Lz6yu60hXYy5RT8rUu+wlIreVxWsGN20mo+MHeCSfUpQBwPw==}
engines: {node: '>= 0.10.0'}
hasBin: true
with@7.0.2: with@7.0.2:
resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==} resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
wrap-ansi@2.1.0:
resolution: {integrity: sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==}
engines: {node: '>=0.10.0'}
wrap-ansi@7.0.0: wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -4102,6 +4181,9 @@ packages:
resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
y18n@3.2.2:
resolution: {integrity: sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==}
y18n@5.0.8: y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -4114,6 +4196,9 @@ packages:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'} engines: {node: '>=12'}
yargs@3.32.0:
resolution: {integrity: sha512-ONJZiimStfZzhKamYvR/xvmgW3uEkAUFSP91y2caTEPhzF6uP2JfPiVZcq66b/YR0C3uitxSV7+T1x8p5bkmMg==}
yauzl@2.10.0: yauzl@2.10.0:
resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
@ -5401,6 +5486,8 @@ snapshots:
agent-base@7.1.4: {} agent-base@7.1.4: {}
ansi-regex@2.1.1: {}
ansi-regex@5.0.1: {} ansi-regex@5.0.1: {}
ansi-styles@4.3.0: ansi-styles@4.3.0:
@ -5436,6 +5523,8 @@ snapshots:
async-each-series@0.1.1: {} async-each-series@0.1.1: {}
async@1.5.2: {}
async@2.6.4: async@2.6.4:
dependencies: dependencies:
lodash: 4.18.1 lodash: 4.18.1
@ -5615,6 +5704,8 @@ snapshots:
call-bind-apply-helpers: 1.0.2 call-bind-apply-helpers: 1.0.2
get-intrinsic: 1.3.0 get-intrinsic: 1.3.0
camelcase@2.1.1: {}
camera-controls@3.1.2(three@0.184.0): camera-controls@3.1.2(three@0.184.0):
dependencies: dependencies:
three: 0.184.0 three: 0.184.0
@ -5652,6 +5743,12 @@ snapshots:
cli-width@4.1.0: {} cli-width@4.1.0: {}
cliui@3.2.0:
dependencies:
string-width: 1.0.2
strip-ansi: 3.0.1
wrap-ansi: 2.1.0
cliui@8.0.1: cliui@8.0.1:
dependencies: dependencies:
string-width: 4.2.3 string-width: 4.2.3
@ -5660,6 +5757,8 @@ snapshots:
cluster-key-slot@1.1.2: {} cluster-key-slot@1.1.2: {}
code-point-at@1.1.0: {}
codemirror@6.0.2: codemirror@6.0.2:
dependencies: dependencies:
'@codemirror/autocomplete': 6.20.2 '@codemirror/autocomplete': 6.20.2
@ -5808,6 +5907,8 @@ snapshots:
dependencies: dependencies:
ms: 2.1.3 ms: 2.1.3
decamelize@1.2.0: {}
decimal.js@10.6.0: {} decimal.js@10.6.0: {}
decode-uri-component@0.2.2: {} decode-uri-component@0.2.2: {}
@ -6368,6 +6469,10 @@ snapshots:
inherits@2.0.4: {} inherits@2.0.4: {}
ini@1.3.8: {}
invert-kv@1.0.0: {}
ioredis@5.10.1: ioredis@5.10.1:
dependencies: dependencies:
'@ioredis/commands': 1.5.1 '@ioredis/commands': 1.5.1
@ -6407,6 +6512,10 @@ snapshots:
is-extglob@2.1.1: {} is-extglob@2.1.1: {}
is-fullwidth-code-point@1.0.0:
dependencies:
number-is-nan: 1.0.1
is-fullwidth-code-point@3.0.0: {} is-fullwidth-code-point@3.0.0: {}
is-glob@4.0.3: is-glob@4.0.3:
@ -6552,6 +6661,10 @@ snapshots:
lazy@1.0.11: {} lazy@1.0.11: {}
lcid@1.0.0:
dependencies:
invert-kv: 1.0.0
less@4.6.4: less@4.6.4:
dependencies: dependencies:
copy-anything: 3.0.5 copy-anything: 3.0.5
@ -6822,6 +6935,13 @@ snapshots:
nanoid@5.1.11: {} nanoid@5.1.11: {}
nconf@0.8.5:
dependencies:
async: 1.5.2
ini: 1.3.8
secure-keys: 1.0.0
yargs: 3.32.0
needle@3.5.0: needle@3.5.0:
dependencies: dependencies:
iconv-lite: 0.6.3 iconv-lite: 0.6.3
@ -6853,6 +6973,8 @@ snapshots:
normalize-path@3.0.0: {} normalize-path@3.0.0: {}
number-is-nan@1.0.1: {}
numeral@2.0.6: {} numeral@2.0.6: {}
object-assign@4.1.1: {} object-assign@4.1.1: {}
@ -6887,6 +7009,10 @@ snapshots:
dependencies: dependencies:
is-wsl: 1.1.0 is-wsl: 1.1.0
os-locale@1.4.0:
dependencies:
lcid: 1.0.0
parse-node-version@1.0.1: {} parse-node-version@1.0.1: {}
parse5@7.3.0: parse5@7.3.0:
@ -7216,6 +7342,8 @@ snapshots:
scheduler@0.27.0: {} scheduler@0.27.0: {}
secure-keys@1.0.0: {}
semver@5.7.2: semver@5.7.2:
optional: true optional: true
@ -7353,6 +7481,10 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
simplegit@1.0.2:
dependencies:
nconf: 0.8.5
slash@3.0.0: {} slash@3.0.0: {}
slug@11.0.1: {} slug@11.0.1: {}
@ -7449,6 +7581,12 @@ snapshots:
strict-uri-encode@2.0.0: {} strict-uri-encode@2.0.0: {}
string-width@1.0.2:
dependencies:
code-point-at: 1.1.0
is-fullwidth-code-point: 1.0.0
strip-ansi: 3.0.1
string-width@4.2.3: string-width@4.2.3:
dependencies: dependencies:
emoji-regex: 8.0.0 emoji-regex: 8.0.0
@ -7459,6 +7597,10 @@ snapshots:
dependencies: dependencies:
safe-buffer: 5.2.1 safe-buffer: 5.2.1
strip-ansi@3.0.1:
dependencies:
ansi-regex: 2.1.1
strip-ansi@6.0.1: strip-ansi@6.0.1:
dependencies: dependencies:
ansi-regex: 5.0.1 ansi-regex: 5.0.1
@ -7810,6 +7952,8 @@ snapshots:
siginfo: 2.0.0 siginfo: 2.0.0
stackback: 0.0.2 stackback: 0.0.2
window-size@0.1.4: {}
with@7.0.2: with@7.0.2:
dependencies: dependencies:
'@babel/parser': 7.29.2 '@babel/parser': 7.29.2
@ -7817,6 +7961,11 @@ snapshots:
assert-never: 1.4.0 assert-never: 1.4.0
babel-walk: 3.0.0-canary-5 babel-walk: 3.0.0-canary-5
wrap-ansi@2.1.0:
dependencies:
string-width: 1.0.2
strip-ansi: 3.0.1
wrap-ansi@7.0.0: wrap-ansi@7.0.0:
dependencies: dependencies:
ansi-styles: 4.3.0 ansi-styles: 4.3.0
@ -7840,6 +7989,8 @@ snapshots:
xmlhttprequest-ssl@2.1.2: {} xmlhttprequest-ssl@2.1.2: {}
y18n@3.2.2: {}
y18n@5.0.8: {} y18n@5.0.8: {}
yargs-parser@21.1.1: {} yargs-parser@21.1.1: {}
@ -7854,6 +8005,16 @@ snapshots:
y18n: 5.0.8 y18n: 5.0.8
yargs-parser: 21.1.1 yargs-parser: 21.1.1
yargs@3.32.0:
dependencies:
camelcase: 2.1.1
cliui: 3.2.0
decamelize: 1.2.0
os-locale: 1.4.0
string-width: 1.0.2
window-size: 0.1.4
y18n: 3.2.2
yauzl@2.10.0: yauzl@2.10.0:
dependencies: dependencies:
buffer-crc32: 0.2.13 buffer-crc32: 0.2.13