@amodalai/amodal 0.2.1 → 0.2.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.
- package/CHANGELOG.md +29 -0
- package/dist/src/commands/connect-channel.d.ts +11 -0
- package/dist/src/commands/connect-channel.d.ts.map +1 -0
- package/dist/src/commands/connect-channel.js +169 -0
- package/dist/src/commands/connect-channel.js.map +1 -0
- package/dist/src/commands/connect.d.ts +3 -1
- package/dist/src/commands/connect.d.ts.map +1 -1
- package/dist/src/commands/connect.js +27 -16
- package/dist/src/commands/connect.js.map +1 -1
- package/dist/src/commands/groups/connect.d.ts +8 -0
- package/dist/src/commands/groups/connect.d.ts.map +1 -0
- package/dist/src/commands/groups/connect.js +17 -0
- package/dist/src/commands/groups/connect.js.map +1 -0
- package/dist/src/commands/groups/pkg.d.ts.map +1 -1
- package/dist/src/commands/groups/pkg.js +0 -12
- package/dist/src/commands/groups/pkg.js.map +1 -1
- package/dist/src/commands/index.d.ts +1 -1
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +3 -1
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +4 -1
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/inspect.d.ts.map +1 -1
- package/dist/src/commands/inspect.js +32 -38
- package/dist/src/commands/inspect.js.map +1 -1
- package/dist/src/commands/install-pkg.d.ts +5 -5
- package/dist/src/commands/install-pkg.d.ts.map +1 -1
- package/dist/src/commands/install-pkg.js +35 -77
- package/dist/src/commands/install-pkg.js.map +1 -1
- package/dist/src/commands/uninstall.d.ts +2 -2
- package/dist/src/commands/uninstall.d.ts.map +1 -1
- package/dist/src/commands/uninstall.js +6 -32
- package/dist/src/commands/uninstall.js.map +1 -1
- package/dist/src/commands/validate.d.ts.map +1 -1
- package/dist/src/commands/validate.js +15 -21
- package/dist/src/commands/validate.js.map +1 -1
- package/dist/src/shared/tarball.js +1 -1
- package/dist/src/shared/tarball.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/src/commands/command-exports.test.ts +1 -1
- package/src/commands/connect-channel.ts +197 -0
- package/src/commands/connect.test.ts +28 -40
- package/src/commands/connect.ts +30 -24
- package/src/commands/groups/connect.ts +20 -0
- package/src/commands/groups/pkg.ts +0 -12
- package/src/commands/index.ts +5 -1
- package/src/commands/init.test.ts +2 -2
- package/src/commands/init.ts +5 -1
- package/src/commands/inspect.test.ts +1 -18
- package/src/commands/inspect.ts +31 -36
- package/src/commands/install-pkg.test.ts +39 -73
- package/src/commands/install-pkg.ts +38 -87
- package/src/commands/uninstall.test.ts +11 -51
- package/src/commands/uninstall.ts +7 -36
- package/src/commands/validate.test.ts +5 -26
- package/src/commands/validate.ts +15 -20
- package/src/e2e-commands.test.ts +0 -1
- package/src/e2e-plugins.test.ts +0 -1
- package/src/e2e.test.ts +0 -89
- package/src/shared/tarball.ts +1 -1
- package/dist/src/commands/diff.d.ts +0 -17
- package/dist/src/commands/diff.d.ts.map +0 -1
- package/dist/src/commands/diff.js +0 -118
- package/dist/src/commands/diff.js.map +0 -1
- package/dist/src/commands/list.d.ts +0 -18
- package/dist/src/commands/list.d.ts.map +0 -1
- package/dist/src/commands/list.js +0 -82
- package/dist/src/commands/list.js.map +0 -1
- package/dist/src/commands/publish.d.ts +0 -18
- package/dist/src/commands/publish.d.ts.map +0 -1
- package/dist/src/commands/publish.js +0 -121
- package/dist/src/commands/publish.js.map +0 -1
- package/dist/src/commands/search.d.ts +0 -19
- package/dist/src/commands/search.d.ts.map +0 -1
- package/dist/src/commands/search.js +0 -97
- package/dist/src/commands/search.js.map +0 -1
- package/dist/src/commands/update.d.ts +0 -19
- package/dist/src/commands/update.d.ts.map +0 -1
- package/dist/src/commands/update.js +0 -158
- package/dist/src/commands/update.js.map +0 -1
- package/src/commands/diff.test.ts +0 -165
- package/src/commands/diff.ts +0 -141
- package/src/commands/list.test.ts +0 -141
- package/src/commands/list.ts +0 -99
- package/src/commands/publish.test.ts +0 -169
- package/src/commands/publish.ts +0 -141
- package/src/commands/search.test.ts +0 -171
- package/src/commands/search.ts +0 -120
- package/src/commands/update.test.ts +0 -256
- package/src/commands/update.ts +0 -196
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
-
* SPDX-License-Identifier: MIT
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
8
|
-
|
|
9
|
-
const mockFindRepoRoot = vi.fn(() => '/test/repo');
|
|
10
|
-
const mockEnsureNpmContext = vi.fn();
|
|
11
|
-
const mockNpmSearch = vi.fn();
|
|
12
|
-
const mockFromNpmName = vi.fn((npm: string) => {
|
|
13
|
-
const prefix = '@amodalai/';
|
|
14
|
-
if (!npm.startsWith(prefix)) throw new Error('Not amodal');
|
|
15
|
-
return npm.slice(prefix.length);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
vi.mock('../shared/repo-discovery.js', () => ({
|
|
19
|
-
findRepoRoot: mockFindRepoRoot,
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
vi.mock('@amodalai/core', () => ({
|
|
23
|
-
ensureNpmContext: mockEnsureNpmContext,
|
|
24
|
-
npmSearch: mockNpmSearch,
|
|
25
|
-
fromNpmName: mockFromNpmName,
|
|
26
|
-
}));
|
|
27
|
-
|
|
28
|
-
const mockPaths = {
|
|
29
|
-
root: '/test/repo/.amodal/packages',
|
|
30
|
-
npmDir: '/test/repo/.amodal/packages/.npm',
|
|
31
|
-
npmrc: '/test/repo/.amodal/packages/.npm/.npmrc',
|
|
32
|
-
packageJson: '/test/repo/.amodal/packages/.npm/package.json',
|
|
33
|
-
nodeModules: '/test/repo/.amodal/packages/.npm/node_modules',
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
describe('runSearch', () => {
|
|
37
|
-
let stderrOutput: string;
|
|
38
|
-
let stdoutOutput: string;
|
|
39
|
-
|
|
40
|
-
beforeEach(() => {
|
|
41
|
-
vi.clearAllMocks();
|
|
42
|
-
mockFindRepoRoot.mockReturnValue('/test/repo');
|
|
43
|
-
mockEnsureNpmContext.mockResolvedValue(mockPaths);
|
|
44
|
-
stderrOutput = '';
|
|
45
|
-
stdoutOutput = '';
|
|
46
|
-
vi.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
47
|
-
stderrOutput += String(chunk);
|
|
48
|
-
return true;
|
|
49
|
-
});
|
|
50
|
-
vi.spyOn(process.stdout, 'write').mockImplementation((chunk) => {
|
|
51
|
-
stdoutOutput += String(chunk);
|
|
52
|
-
return true;
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('should search and display results', async () => {
|
|
57
|
-
mockNpmSearch.mockResolvedValue([
|
|
58
|
-
{name: '@amodalai/connection-salesforce', version: '2.1.0', description: 'Salesforce'},
|
|
59
|
-
{name: '@amodalai/connection-stripe', version: '1.0.0', description: 'Stripe'},
|
|
60
|
-
]);
|
|
61
|
-
|
|
62
|
-
const {runSearch} = await import('./search.js');
|
|
63
|
-
const result = await runSearch({query: 'salesforce'});
|
|
64
|
-
expect(result).toBe(0);
|
|
65
|
-
expect(stdoutOutput).toContain('salesforce');
|
|
66
|
-
expect(stdoutOutput).toContain('stripe');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should filter by type', async () => {
|
|
70
|
-
mockNpmSearch.mockResolvedValue([
|
|
71
|
-
{name: '@amodalai/connection-salesforce', version: '2.1.0', description: 'Salesforce'},
|
|
72
|
-
{name: '@amodalai/skill-triage', version: '1.0.0', description: 'Triage'},
|
|
73
|
-
]);
|
|
74
|
-
|
|
75
|
-
const {runSearch} = await import('./search.js');
|
|
76
|
-
const result = await runSearch({tag: 'connection'});
|
|
77
|
-
expect(result).toBe(0);
|
|
78
|
-
expect(stdoutOutput).toContain('salesforce');
|
|
79
|
-
expect(stdoutOutput).not.toContain('triage');
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('should output JSON when json flag set', async () => {
|
|
83
|
-
mockNpmSearch.mockResolvedValue([
|
|
84
|
-
{name: '@amodalai/connection-salesforce', version: '2.1.0', description: 'Salesforce'},
|
|
85
|
-
]);
|
|
86
|
-
|
|
87
|
-
const {runSearch} = await import('./search.js');
|
|
88
|
-
const result = await runSearch({json: true});
|
|
89
|
-
expect(result).toBe(0);
|
|
90
|
-
const parsed = JSON.parse(stdoutOutput);
|
|
91
|
-
expect(parsed).toHaveLength(1);
|
|
92
|
-
expect(parsed[0].name).toBe('connection-salesforce');
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('should handle no results', async () => {
|
|
96
|
-
mockNpmSearch.mockResolvedValue([]);
|
|
97
|
-
|
|
98
|
-
const {runSearch} = await import('./search.js');
|
|
99
|
-
const result = await runSearch({query: 'nonexistent'});
|
|
100
|
-
expect(result).toBe(0);
|
|
101
|
-
expect(stderrOutput).toContain('No packages found');
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('should handle registry unreachable', async () => {
|
|
105
|
-
mockNpmSearch.mockRejectedValue(new Error('Registry unreachable'));
|
|
106
|
-
|
|
107
|
-
const {runSearch} = await import('./search.js');
|
|
108
|
-
const result = await runSearch({query: 'test'});
|
|
109
|
-
expect(result).toBe(1);
|
|
110
|
-
expect(stderrOutput).toContain('Search failed');
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('should filter out non-amodal packages', async () => {
|
|
114
|
-
mockNpmSearch.mockResolvedValue([
|
|
115
|
-
{name: '@amodalai/connection-salesforce', version: '1.0.0', description: 'Salesforce'},
|
|
116
|
-
{name: 'lodash', version: '4.0.0', description: 'Utility'},
|
|
117
|
-
]);
|
|
118
|
-
|
|
119
|
-
const {runSearch} = await import('./search.js');
|
|
120
|
-
const result = await runSearch({});
|
|
121
|
-
expect(result).toBe(0);
|
|
122
|
-
expect(stdoutOutput).toContain('salesforce');
|
|
123
|
-
expect(stdoutOutput).not.toContain('lodash');
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should work without repo root', async () => {
|
|
127
|
-
mockFindRepoRoot.mockImplementation(() => {
|
|
128
|
-
throw new Error('Not found');
|
|
129
|
-
});
|
|
130
|
-
mockNpmSearch.mockResolvedValue([
|
|
131
|
-
{name: '@amodalai/skill-triage', version: '1.0.0', description: 'Triage'},
|
|
132
|
-
]);
|
|
133
|
-
|
|
134
|
-
const {runSearch} = await import('./search.js');
|
|
135
|
-
const result = await runSearch({query: 'triage'});
|
|
136
|
-
expect(result).toBe(0);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('should list results with short names', async () => {
|
|
140
|
-
mockNpmSearch.mockResolvedValue([
|
|
141
|
-
{name: '@amodalai/connection-salesforce', version: '2.0.0', description: 'Salesforce'},
|
|
142
|
-
{name: '@amodalai/skill-triage', version: '1.0.0', description: 'Triage'},
|
|
143
|
-
]);
|
|
144
|
-
|
|
145
|
-
const {runSearch} = await import('./search.js');
|
|
146
|
-
const result = await runSearch({});
|
|
147
|
-
expect(result).toBe(0);
|
|
148
|
-
expect(stdoutOutput).toContain('connection-salesforce');
|
|
149
|
-
expect(stdoutOutput).toContain('skill-triage');
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('should handle npm context failure', async () => {
|
|
153
|
-
mockEnsureNpmContext.mockRejectedValue(new Error('Permission denied'));
|
|
154
|
-
|
|
155
|
-
const {runSearch} = await import('./search.js');
|
|
156
|
-
const result = await runSearch({});
|
|
157
|
-
expect(result).toBe(1);
|
|
158
|
-
expect(stderrOutput).toContain('Failed to set up npm context');
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it('should show package count', async () => {
|
|
162
|
-
mockNpmSearch.mockResolvedValue([
|
|
163
|
-
{name: '@amodalai/connection-salesforce', version: '1.0.0', description: 'Salesforce'},
|
|
164
|
-
{name: '@amodalai/connection-stripe', version: '1.0.0', description: 'Stripe'},
|
|
165
|
-
]);
|
|
166
|
-
|
|
167
|
-
const {runSearch} = await import('./search.js');
|
|
168
|
-
await runSearch({});
|
|
169
|
-
expect(stderrOutput).toContain('2 packages found');
|
|
170
|
-
});
|
|
171
|
-
});
|
package/src/commands/search.ts
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
-
* SPDX-License-Identifier: MIT
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type {CommandModule} from 'yargs';
|
|
8
|
-
import {
|
|
9
|
-
ensureNpmContext,
|
|
10
|
-
fromNpmName,
|
|
11
|
-
npmSearch,
|
|
12
|
-
} from '@amodalai/core';
|
|
13
|
-
import {findRepoRoot} from '../shared/repo-discovery.js';
|
|
14
|
-
|
|
15
|
-
export interface SearchOptions {
|
|
16
|
-
cwd?: string;
|
|
17
|
-
query?: string;
|
|
18
|
-
tag?: string;
|
|
19
|
-
json?: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Search the registry for packages.
|
|
24
|
-
* Returns 0 on success, 1 on error.
|
|
25
|
-
*/
|
|
26
|
-
export async function runSearch(options: SearchOptions = {}): Promise<number> {
|
|
27
|
-
let repoPath: string | undefined;
|
|
28
|
-
try {
|
|
29
|
-
repoPath = findRepoRoot(options.cwd);
|
|
30
|
-
} catch {
|
|
31
|
-
// Not in a repo — use temp context
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
let paths;
|
|
35
|
-
try {
|
|
36
|
-
paths = await ensureNpmContext(repoPath ?? process.cwd());
|
|
37
|
-
} catch (err) {
|
|
38
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
39
|
-
process.stderr.write(`[search] Failed to set up npm context: ${msg}\n`);
|
|
40
|
-
return 1;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Build query
|
|
44
|
-
let query = '@amodalai/';
|
|
45
|
-
if (options.tag) {
|
|
46
|
-
query += `${options.tag}-`;
|
|
47
|
-
}
|
|
48
|
-
if (options.query) {
|
|
49
|
-
query += options.query;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
process.stderr.write(`[search] Searching for "${query}"...\n`);
|
|
53
|
-
|
|
54
|
-
let results;
|
|
55
|
-
try {
|
|
56
|
-
results = await npmSearch(paths, query);
|
|
57
|
-
} catch (err) {
|
|
58
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
59
|
-
process.stderr.write(`[search] Search failed: ${msg}\n`);
|
|
60
|
-
return 1;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Filter to @amodalai/ packages only
|
|
64
|
-
results = results.filter((r) => r.name.startsWith('@amodalai/'));
|
|
65
|
-
|
|
66
|
-
// Filter by tag if specified (match against npm name prefix)
|
|
67
|
-
if (options.tag) {
|
|
68
|
-
const prefix = `@amodalai/${options.tag}-`;
|
|
69
|
-
results = results.filter((r) => r.name.startsWith(prefix));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (results.length === 0) {
|
|
73
|
-
process.stderr.write('[search] No packages found.\n');
|
|
74
|
-
return 0;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (options.json) {
|
|
78
|
-
const output = results.map((r) => ({
|
|
79
|
-
npm: r.name,
|
|
80
|
-
name: fromNpmName(r.name),
|
|
81
|
-
version: r.version,
|
|
82
|
-
description: r.description,
|
|
83
|
-
}));
|
|
84
|
-
process.stdout.write(JSON.stringify(output, null, 2) + '\n');
|
|
85
|
-
return 0;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Display results
|
|
89
|
-
const nameWidth = Math.max(4, ...results.map((r) => fromNpmName(r.name).length));
|
|
90
|
-
const versionWidth = Math.max(7, ...results.map((r) => r.version.length));
|
|
91
|
-
|
|
92
|
-
for (const r of results) {
|
|
93
|
-
const shortName = fromNpmName(r.name);
|
|
94
|
-
process.stdout.write(` ${shortName.padEnd(nameWidth)} ${r.version.padEnd(versionWidth)} ${r.description}\n`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
process.stderr.write(`\n[search] ${results.length} package${results.length === 1 ? '' : 's'} found.\n`);
|
|
98
|
-
return 0;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export const searchCommand: CommandModule = {
|
|
102
|
-
command: 'search [query]',
|
|
103
|
-
describe: 'Search the registry for packages',
|
|
104
|
-
builder: (yargs) =>
|
|
105
|
-
yargs
|
|
106
|
-
.positional('query', {type: 'string', describe: 'Search query'})
|
|
107
|
-
.option('tag', {type: 'string', describe: 'Filter by tag prefix (e.g., connection, skill)'})
|
|
108
|
-
.option('json', {type: 'boolean', default: false, describe: 'Output as JSON'}),
|
|
109
|
-
handler: async (argv) => {
|
|
110
|
-
const code = await runSearch({
|
|
111
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
112
|
-
query: argv['query'] as string | undefined,
|
|
113
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
114
|
-
tag: argv['tag'] as string | undefined,
|
|
115
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
116
|
-
json: argv['json'] as boolean,
|
|
117
|
-
});
|
|
118
|
-
process.exit(code);
|
|
119
|
-
},
|
|
120
|
-
};
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2025 Amodal Labs, Inc.
|
|
4
|
-
* SPDX-License-Identifier: MIT
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
8
|
-
|
|
9
|
-
const mockFindRepoRoot = vi.fn(() => '/test/repo');
|
|
10
|
-
const mockEnsureNpmContext = vi.fn();
|
|
11
|
-
const mockReadLockFile = vi.fn();
|
|
12
|
-
const mockNpmViewVersions = vi.fn();
|
|
13
|
-
const mockNpmInstall = vi.fn();
|
|
14
|
-
const mockFromNpmName = vi.fn((npm: string) => npm.replace('@amodalai/', ''));
|
|
15
|
-
const mockDiscoverInstalledPackages = vi.fn();
|
|
16
|
-
const mockBuildLockFile = vi.fn();
|
|
17
|
-
|
|
18
|
-
vi.mock('../shared/repo-discovery.js', () => ({
|
|
19
|
-
findRepoRoot: mockFindRepoRoot,
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
vi.mock('@amodalai/core', () => ({
|
|
23
|
-
ensureNpmContext: mockEnsureNpmContext,
|
|
24
|
-
readLockFile: mockReadLockFile,
|
|
25
|
-
npmViewVersions: mockNpmViewVersions,
|
|
26
|
-
npmInstall: mockNpmInstall,
|
|
27
|
-
fromNpmName: mockFromNpmName,
|
|
28
|
-
discoverInstalledPackages: mockDiscoverInstalledPackages,
|
|
29
|
-
buildLockFile: mockBuildLockFile,
|
|
30
|
-
}));
|
|
31
|
-
|
|
32
|
-
vi.mock('semver', () => ({
|
|
33
|
-
maxSatisfying: vi.fn((versions: string[], range: string) => {
|
|
34
|
-
// Simple mock: return the last version for '*', first matching for '^'
|
|
35
|
-
if (range === '*') return versions[versions.length - 1];
|
|
36
|
-
// For ^x.y.z, return last version with same major
|
|
37
|
-
const major = range.replace('^', '').split('.')[0];
|
|
38
|
-
const matching = versions.filter((v) => v.startsWith(major + '.'));
|
|
39
|
-
return matching.length > 0 ? matching[matching.length - 1] : null;
|
|
40
|
-
}),
|
|
41
|
-
}));
|
|
42
|
-
|
|
43
|
-
const mockPaths = {
|
|
44
|
-
root: '/test/repo/.amodal/packages',
|
|
45
|
-
npmDir: '/test/repo/.amodal/packages/.npm',
|
|
46
|
-
npmrc: '/test/repo/.amodal/packages/.npm/.npmrc',
|
|
47
|
-
packageJson: '/test/repo/.amodal/packages/.npm/package.json',
|
|
48
|
-
nodeModules: '/test/repo/.amodal/packages/.npm/node_modules',
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
describe('runUpdate', () => {
|
|
52
|
-
let stderrOutput: string;
|
|
53
|
-
|
|
54
|
-
beforeEach(() => {
|
|
55
|
-
vi.clearAllMocks();
|
|
56
|
-
mockFindRepoRoot.mockReturnValue('/test/repo');
|
|
57
|
-
mockEnsureNpmContext.mockResolvedValue(mockPaths);
|
|
58
|
-
mockNpmInstall.mockResolvedValue({version: '2.0.0', integrity: 'sha512-new'});
|
|
59
|
-
mockDiscoverInstalledPackages.mockResolvedValue([]);
|
|
60
|
-
mockBuildLockFile.mockResolvedValue(undefined);
|
|
61
|
-
stderrOutput = '';
|
|
62
|
-
vi.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
63
|
-
stderrOutput += String(chunk);
|
|
64
|
-
return true;
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should update all packages', async () => {
|
|
69
|
-
mockReadLockFile.mockResolvedValue({
|
|
70
|
-
lockVersion: 2,
|
|
71
|
-
packages: {
|
|
72
|
-
'@amodalai/connection-salesforce': {version: '1.0.0', integrity: 'sha512-old'},
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
mockNpmViewVersions.mockResolvedValue(['1.0.0', '1.1.0', '1.2.0']);
|
|
76
|
-
|
|
77
|
-
const {runUpdate} = await import('./update.js');
|
|
78
|
-
const result = await runUpdate();
|
|
79
|
-
expect(result).toBe(0);
|
|
80
|
-
expect(mockNpmInstall).toHaveBeenCalled();
|
|
81
|
-
expect(stderrOutput).toContain('Updated');
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should update single package by name', async () => {
|
|
85
|
-
mockReadLockFile.mockResolvedValue({
|
|
86
|
-
lockVersion: 2,
|
|
87
|
-
packages: {
|
|
88
|
-
'@amodalai/connection-salesforce': {version: '1.0.0', integrity: 'sha512-old'},
|
|
89
|
-
'@amodalai/skill-triage': {version: '1.0.0', integrity: 'sha512-old'},
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
mockNpmViewVersions.mockResolvedValue(['1.0.0', '1.1.0']);
|
|
93
|
-
|
|
94
|
-
const {runUpdate} = await import('./update.js');
|
|
95
|
-
const result = await runUpdate({name: 'connection-salesforce'});
|
|
96
|
-
expect(result).toBe(0);
|
|
97
|
-
expect(mockNpmViewVersions).toHaveBeenCalledTimes(1);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('should report already up to date', async () => {
|
|
101
|
-
mockReadLockFile.mockResolvedValue({
|
|
102
|
-
lockVersion: 2,
|
|
103
|
-
packages: {
|
|
104
|
-
'@amodalai/connection-salesforce': {version: '1.2.0', integrity: 'sha512-old'},
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
mockNpmViewVersions.mockResolvedValue(['1.0.0', '1.1.0', '1.2.0']);
|
|
108
|
-
|
|
109
|
-
const {runUpdate} = await import('./update.js');
|
|
110
|
-
const result = await runUpdate();
|
|
111
|
-
expect(result).toBe(0);
|
|
112
|
-
expect(stderrOutput).toContain('up to date');
|
|
113
|
-
expect(mockNpmInstall).not.toHaveBeenCalled();
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('should show dry run output', async () => {
|
|
117
|
-
mockReadLockFile.mockResolvedValue({
|
|
118
|
-
lockVersion: 2,
|
|
119
|
-
packages: {
|
|
120
|
-
'@amodalai/connection-salesforce': {version: '1.0.0', integrity: 'sha512-old'},
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
mockNpmViewVersions.mockResolvedValue(['1.0.0', '1.1.0']);
|
|
124
|
-
|
|
125
|
-
const {runUpdate} = await import('./update.js');
|
|
126
|
-
const result = await runUpdate({dryRun: true});
|
|
127
|
-
expect(result).toBe(0);
|
|
128
|
-
expect(stderrOutput).toContain('Dry run');
|
|
129
|
-
expect(stderrOutput).toContain('1.1.0');
|
|
130
|
-
expect(mockNpmInstall).not.toHaveBeenCalled();
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('should use latest flag for cross-major updates', async () => {
|
|
134
|
-
mockReadLockFile.mockResolvedValue({
|
|
135
|
-
lockVersion: 2,
|
|
136
|
-
packages: {
|
|
137
|
-
'@amodalai/connection-salesforce': {version: '1.0.0', integrity: 'sha512-old'},
|
|
138
|
-
},
|
|
139
|
-
});
|
|
140
|
-
mockNpmViewVersions.mockResolvedValue(['1.0.0', '1.1.0', '2.0.0', '3.0.0']);
|
|
141
|
-
|
|
142
|
-
const {runUpdate} = await import('./update.js');
|
|
143
|
-
const result = await runUpdate({latest: true});
|
|
144
|
-
expect(result).toBe(0);
|
|
145
|
-
expect(stderrOutput).toContain('3.0.0');
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('should return 1 when no lock file', async () => {
|
|
149
|
-
mockReadLockFile.mockResolvedValue(null);
|
|
150
|
-
|
|
151
|
-
const {runUpdate} = await import('./update.js');
|
|
152
|
-
const result = await runUpdate();
|
|
153
|
-
expect(result).toBe(1);
|
|
154
|
-
expect(stderrOutput).toContain('No lock file');
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('should return 1 when repo not found', async () => {
|
|
158
|
-
mockFindRepoRoot.mockImplementation(() => {
|
|
159
|
-
throw new Error('Not found');
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
const {runUpdate} = await import('./update.js');
|
|
163
|
-
const result = await runUpdate();
|
|
164
|
-
expect(result).toBe(1);
|
|
165
|
-
expect(stderrOutput).toContain('Not found');
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it('should handle registry unreachable', async () => {
|
|
169
|
-
mockReadLockFile.mockResolvedValue({
|
|
170
|
-
lockVersion: 2,
|
|
171
|
-
packages: {
|
|
172
|
-
'@amodalai/connection-salesforce': {version: '1.0.0', integrity: 'sha512-old'},
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
mockNpmViewVersions.mockRejectedValue(new Error('Registry unreachable'));
|
|
176
|
-
|
|
177
|
-
const {runUpdate} = await import('./update.js');
|
|
178
|
-
const result = await runUpdate();
|
|
179
|
-
expect(result).toBe(1);
|
|
180
|
-
expect(stderrOutput).toContain('Failed to check');
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('should continue on partial failure', async () => {
|
|
184
|
-
mockReadLockFile.mockResolvedValue({
|
|
185
|
-
lockVersion: 2,
|
|
186
|
-
packages: {
|
|
187
|
-
'@amodalai/connection-salesforce': {version: '1.0.0', integrity: 'sha512-old'},
|
|
188
|
-
'@amodalai/skill-triage': {version: '1.0.0', integrity: 'sha512-old'},
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
mockNpmViewVersions
|
|
192
|
-
.mockResolvedValueOnce(['1.0.0', '1.1.0'])
|
|
193
|
-
.mockResolvedValueOnce(['1.0.0', '1.1.0']);
|
|
194
|
-
mockNpmInstall
|
|
195
|
-
.mockRejectedValueOnce(new Error('Install failed'))
|
|
196
|
-
.mockResolvedValueOnce({version: '1.1.0', integrity: 'sha512-new'});
|
|
197
|
-
|
|
198
|
-
const {runUpdate} = await import('./update.js');
|
|
199
|
-
const result = await runUpdate();
|
|
200
|
-
expect(result).toBe(1);
|
|
201
|
-
expect(stderrOutput).toContain('1 of 2');
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('should handle empty lock file', async () => {
|
|
205
|
-
mockReadLockFile.mockResolvedValue({lockVersion: 2, packages: {}});
|
|
206
|
-
|
|
207
|
-
const {runUpdate} = await import('./update.js');
|
|
208
|
-
const result = await runUpdate();
|
|
209
|
-
expect(result).toBe(0);
|
|
210
|
-
expect(stderrOutput).toContain('No packages installed');
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it('should report no matching packages', async () => {
|
|
214
|
-
mockReadLockFile.mockResolvedValue({
|
|
215
|
-
lockVersion: 2,
|
|
216
|
-
packages: {
|
|
217
|
-
'@amodalai/connection-salesforce': {version: '1.0.0', integrity: 'sha512-old'},
|
|
218
|
-
},
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
const {runUpdate} = await import('./update.js');
|
|
222
|
-
const result = await runUpdate({name: 'nonexistent'});
|
|
223
|
-
expect(result).toBe(0);
|
|
224
|
-
expect(stderrOutput).toContain('No matching packages');
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
it('should rebuild lock file after update', async () => {
|
|
228
|
-
mockReadLockFile.mockResolvedValue({
|
|
229
|
-
lockVersion: 2,
|
|
230
|
-
packages: {
|
|
231
|
-
'@amodalai/connection-salesforce': {version: '1.0.0', integrity: 'sha512-old'},
|
|
232
|
-
},
|
|
233
|
-
});
|
|
234
|
-
mockNpmViewVersions.mockResolvedValue(['1.0.0', '1.1.0']);
|
|
235
|
-
|
|
236
|
-
const {runUpdate} = await import('./update.js');
|
|
237
|
-
await runUpdate();
|
|
238
|
-
expect(mockDiscoverInstalledPackages).toHaveBeenCalledWith(mockPaths);
|
|
239
|
-
expect(mockBuildLockFile).toHaveBeenCalled();
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it('should use singular form for 1 package', async () => {
|
|
243
|
-
mockReadLockFile.mockResolvedValue({
|
|
244
|
-
lockVersion: 2,
|
|
245
|
-
packages: {
|
|
246
|
-
'@amodalai/connection-salesforce': {version: '1.0.0', integrity: 'sha512-old'},
|
|
247
|
-
},
|
|
248
|
-
});
|
|
249
|
-
mockNpmViewVersions.mockResolvedValue(['1.0.0', '1.1.0']);
|
|
250
|
-
|
|
251
|
-
const {runUpdate} = await import('./update.js');
|
|
252
|
-
await runUpdate();
|
|
253
|
-
expect(stderrOutput).toContain('1 package updated');
|
|
254
|
-
expect(stderrOutput).not.toContain('1 packages');
|
|
255
|
-
});
|
|
256
|
-
});
|