feat: Add time field to abbreviated metadata for pnpm time-based resolution (#834)
- [x] Add time field to abbreviated manifests in `_listPackageAbbreviatedManifests` method - [x] Fix cache update logic to populate time field when adding versions to abbreviated manifests - [x] Fix cache update logic to remove time field when removing versions from abbreviated manifests - [x] Add comprehensive test coverage for time field in abbreviated manifests - [x] All tests passing (30/30 in ShowPackageController.test.ts) closes https://github.com/cnpm/cnpmcore/issues/609 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Package metadata now consistently includes time information (created, modified, and per-version publish timestamps) in both full and abbreviated manifests. * Removing a version also removes its corresponding time entry to keep metadata accurate. * Added guards to prevent writing invalid time data, improving stability. * **Tests** * Added tests to verify presence and correctness of time fields in abbreviated manifests, including created/modified and per-version timestamps. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: fengmk2 <156269+fengmk2@users.noreply.github.com> Co-authored-by: MK (fengmk2) <fengmk2@gmail.com>
This commit is contained in:
@@ -925,6 +925,11 @@ export class PackageManagerService extends AbstractService {
|
|||||||
if (abbreviatedManifest) {
|
if (abbreviatedManifest) {
|
||||||
abbreviatedManifests.versions[packageVersion.version] =
|
abbreviatedManifests.versions[packageVersion.version] =
|
||||||
abbreviatedManifest;
|
abbreviatedManifest;
|
||||||
|
// abbreviatedManifests.time is guaranteed to exist since it's initialized in _listPackageAbbreviatedManifests
|
||||||
|
if (abbreviatedManifests.time) {
|
||||||
|
abbreviatedManifests.time[packageVersion.version] =
|
||||||
|
packageVersion.publishTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -937,6 +942,8 @@ export class PackageManagerService extends AbstractService {
|
|||||||
delete fullManifests.time[version];
|
delete fullManifests.time[version];
|
||||||
// eslint-disable-next-line typescript-eslint/no-dynamic-delete
|
// eslint-disable-next-line typescript-eslint/no-dynamic-delete
|
||||||
delete abbreviatedManifests.versions[version];
|
delete abbreviatedManifests.versions[version];
|
||||||
|
// eslint-disable-next-line typescript-eslint/no-dynamic-delete
|
||||||
|
delete abbreviatedManifests.time?.[version];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1357,6 +1364,10 @@ export class PackageManagerService extends AbstractService {
|
|||||||
modified: pkg.updatedAt,
|
modified: pkg.updatedAt,
|
||||||
name: pkg.fullname,
|
name: pkg.fullname,
|
||||||
versions: {},
|
versions: {},
|
||||||
|
time: {
|
||||||
|
created: pkg.createdAt,
|
||||||
|
modified: pkg.updatedAt,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const packageVersion of packageVersions) {
|
for (const packageVersion of packageVersions) {
|
||||||
@@ -1366,6 +1377,10 @@ export class PackageManagerService extends AbstractService {
|
|||||||
);
|
);
|
||||||
if (manifest) {
|
if (manifest) {
|
||||||
data.versions[packageVersion.version] = manifest;
|
data.versions[packageVersion.version] = manifest;
|
||||||
|
// data.time is guaranteed to exist since we initialize it above
|
||||||
|
if (data.time) {
|
||||||
|
data.time[packageVersion.version] = packageVersion.publishTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -384,6 +384,34 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => {
|
|||||||
.expect(201);
|
.expect(201);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should include time field in abbreviated manifests for pnpm time-based resolution', async () => {
|
||||||
|
mock(app.config.cnpmcore, 'syncMode', 'all');
|
||||||
|
const res = await app
|
||||||
|
.httpRequest()
|
||||||
|
.get(`/${name}`)
|
||||||
|
.set('Accept', 'application/vnd.npm.install-v1+json')
|
||||||
|
.expect(200)
|
||||||
|
.expect('content-type', 'application/json; charset=utf-8');
|
||||||
|
const pkg = res.body;
|
||||||
|
|
||||||
|
// Verify time field structure exists
|
||||||
|
assert.ok(pkg.time, 'time field should be present in abbreviated manifests');
|
||||||
|
assert.ok(pkg.time.created, 'time.created should be present');
|
||||||
|
assert.ok(pkg.time.modified, 'time.modified should be present');
|
||||||
|
|
||||||
|
// Verify each version has a publish time
|
||||||
|
const versions = Object.keys(pkg.versions);
|
||||||
|
for (const version of versions) {
|
||||||
|
assert.ok(pkg.time[version], `time.${version} should be present for version ${version}`);
|
||||||
|
assert.ok(pkg.time[version] instanceof Date || typeof pkg.time[version] === 'string',
|
||||||
|
`time.${version} should be a Date or string`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify at least the expected versions have time entries
|
||||||
|
assert.ok(pkg.time['1.0.0'], 'time.1.0.0 should be present');
|
||||||
|
assert.ok(pkg.time['2.0.0'], 'time.2.0.0 should be present');
|
||||||
|
});
|
||||||
|
|
||||||
it('should show one scoped package with abbreviated manifests', async () => {
|
it('should show one scoped package with abbreviated manifests', async () => {
|
||||||
const res = await app
|
const res = await app
|
||||||
.httpRequest()
|
.httpRequest()
|
||||||
|
|||||||
Reference in New Issue
Block a user