Compare commits

...

574 Commits

Author SHA1 Message Date
MK (fengmk2)
8fdb51c706 Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: MK (fengmk2) <fengmk2@gmail.com>
2025-11-10 23:32:45 +08:00
MK (fengmk2)
12ac2638cb Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: MK (fengmk2) <fengmk2@gmail.com>
2025-11-10 23:32:32 +08:00
MK (fengmk2)
1d1d21615e Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: MK (fengmk2) <fengmk2@gmail.com>
2025-11-10 23:32:18 +08:00
MK
d913bf0ea1 FIXUP 2025-11-10 23:30:16 +08:00
MK
22d7ffb14a FIXUP 2025-11-10 23:27:55 +08:00
MK
70a0265758 fix: try to avoid recalculating integrity
Especially for packages with many versions, reduce one JSON stringify operation.
2025-11-10 23:21:27 +08:00
renovate[bot]
95543b1f9d chore(deps): update dependency oxlint-tsgolint to ^0.5.0 (#875)
[skip ci]

This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [oxlint-tsgolint](https://redirect.github.com/oxc-project/tsgolint) |
[`^0.3.0` ->
`^0.5.0`](https://renovatebot.com/diffs/npm/oxlint-tsgolint/0.3.0/0.5.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/oxlint-tsgolint/0.5.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/oxlint-tsgolint/0.3.0/0.5.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>oxc-project/tsgolint (oxlint-tsgolint)</summary>

###
[`v0.5.0`](https://redirect.github.com/oxc-project/tsgolint/releases/tag/v0.5.0)

[Compare
Source](https://redirect.github.com/oxc-project/tsgolint/compare/v0.4.0...v0.5.0)

#### What's Changed

- chore(deps): update typescript-go digest to
[`b278afd`](https://redirect.github.com/oxc-project/tsgolint/commit/b278afd)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;344](https://redirect.github.com/oxc-project/tsgolint/pull/344)
- perf: `no-unnecessary-boolean-literal-compare`: only build fixes when
needed by [@&#8203;camchenry](https://redirect.github.com/camchenry) in
[#&#8203;332](https://redirect.github.com/oxc-project/tsgolint/pull/332)
- perf: `no-unnecessary-type-arguments`: only compute remove range when
creating fixes by
[@&#8203;camchenry](https://redirect.github.com/camchenry) in
[#&#8203;333](https://redirect.github.com/oxc-project/tsgolint/pull/333)
- chore(deps): update typescript-go digest to
[`24b38de`](https://redirect.github.com/oxc-project/tsgolint/commit/24b38de)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;345](https://redirect.github.com/oxc-project/tsgolint/pull/345)
- chore(deps): update typescript-go digest to
[`d891e4f`](https://redirect.github.com/oxc-project/tsgolint/commit/d891e4f)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;348](https://redirect.github.com/oxc-project/tsgolint/pull/348)
- chore(deps): update typescript-go digest to
[`8ac3092`](https://redirect.github.com/oxc-project/tsgolint/commit/8ac3092)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;350](https://redirect.github.com/oxc-project/tsgolint/pull/350)
- chore(deps): update typescript-go digest to
[`4705d38`](https://redirect.github.com/oxc-project/tsgolint/commit/4705d38)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;352](https://redirect.github.com/oxc-project/tsgolint/pull/352)
- chore(deps): update crate-ci/typos action to v1.39.0 by
[@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;354](https://redirect.github.com/oxc-project/tsgolint/pull/354)
- chore(deps): update typescript-go digest to
[`71a622f`](https://redirect.github.com/oxc-project/tsgolint/commit/71a622f)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;353](https://redirect.github.com/oxc-project/tsgolint/pull/353)
- chore(deps): update typescript-go digest to
[`240b101`](https://redirect.github.com/oxc-project/tsgolint/commit/240b101)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;355](https://redirect.github.com/oxc-project/tsgolint/pull/355)
- chore(deps): update typescript-go digest to
[`6fb55b7`](https://redirect.github.com/oxc-project/tsgolint/commit/6fb55b7)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;357](https://redirect.github.com/oxc-project/tsgolint/pull/357)
- chore(deps): update dependency dprint-typescript to v0.95.12 by
[@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;359](https://redirect.github.com/oxc-project/tsgolint/pull/359)
- chore(deps): update taiki-e/install-action action to v2.62.45 by
[@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;360](https://redirect.github.com/oxc-project/tsgolint/pull/360)
- chore(deps): update github.com/go-json-experiment/json digest to
[`4849db3`](https://redirect.github.com/oxc-project/tsgolint/commit/4849db3)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;358](https://redirect.github.com/oxc-project/tsgolint/pull/358)
- chore(deps): lock file maintenance npm packages by
[@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;362](https://redirect.github.com/oxc-project/tsgolint/pull/362)
- chore(deps): update typescript-go digest to
[`82039b6`](https://redirect.github.com/oxc-project/tsgolint/commit/82039b6)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;361](https://redirect.github.com/oxc-project/tsgolint/pull/361)
- chore(dpes): update golang to ^1.25.0 by
[@&#8203;sunrabbit123](https://redirect.github.com/sunrabbit123) in
[#&#8203;363](https://redirect.github.com/oxc-project/tsgolint/pull/363)
- docs: mark `strict-boolean-expressions` as implemented by
[@&#8203;camc314](https://redirect.github.com/camc314) in
[#&#8203;364](https://redirect.github.com/oxc-project/tsgolint/pull/364)
- feat: improve invalid config diagnostics by
[@&#8203;camc314](https://redirect.github.com/camc314) in
[#&#8203;365](https://redirect.github.com/oxc-project/tsgolint/pull/365)
- feat: link to github issue for deprecated tsconfig options by
[@&#8203;camc314](https://redirect.github.com/camc314) in
[#&#8203;366](https://redirect.github.com/oxc-project/tsgolint/pull/366)
- perf: `no-unnecessary-type-assertion`: only do scanning when fixes are
generated by [@&#8203;camchenry](https://redirect.github.com/camchenry)
in
[#&#8203;334](https://redirect.github.com/oxc-project/tsgolint/pull/334)
- chore(deps): update typescript-go digest to
[`13d3e19`](https://redirect.github.com/oxc-project/tsgolint/commit/13d3e19)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;367](https://redirect.github.com/oxc-project/tsgolint/pull/367)
- fix(justfile): fix `just shim` command by
[@&#8203;camc314](https://redirect.github.com/camc314) in
[#&#8203;369](https://redirect.github.com/oxc-project/tsgolint/pull/369)
- feat: implement `typescript/no-deprecated` by
[@&#8203;camc314](https://redirect.github.com/camc314) in
[#&#8203;368](https://redirect.github.com/oxc-project/tsgolint/pull/368)

#### New Contributors

- [@&#8203;sunrabbit123](https://redirect.github.com/sunrabbit123) made
their first contribution in
[#&#8203;363](https://redirect.github.com/oxc-project/tsgolint/pull/363)

**Full Changelog**:
<https://github.com/oxc-project/tsgolint/compare/v0.4.0...v0.5.0>

###
[`v0.4.0`](https://redirect.github.com/oxc-project/tsgolint/releases/tag/v0.4.0)

[Compare
Source](https://redirect.github.com/oxc-project/tsgolint/compare/v0.3.0...v0.4.0)

#### What's Changed

- chore(deps): update typescript-go digest to
[`def283d`](https://redirect.github.com/oxc-project/tsgolint/commit/def283d)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;315](https://redirect.github.com/oxc-project/tsgolint/pull/315)
- chore(deps): update typescript-go digest to
[`d461fad`](https://redirect.github.com/oxc-project/tsgolint/commit/d461fad)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;323](https://redirect.github.com/oxc-project/tsgolint/pull/323)
- chore(deps): update taiki-e/install-action action to v2.62.38 by
[@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;327](https://redirect.github.com/oxc-project/tsgolint/pull/327)
- chore(deps): update github.com/go-json-experiment/json digest to
[`8a0206d`](https://redirect.github.com/oxc-project/tsgolint/commit/8a0206d)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;325](https://redirect.github.com/oxc-project/tsgolint/pull/325)
- chore(deps): update github-actions (major) by
[@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;328](https://redirect.github.com/oxc-project/tsgolint/pull/328)
- chore(deps): lock file maintenance npm packages by
[@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;329](https://redirect.github.com/oxc-project/tsgolint/pull/329)
- chore(deps): update typescript-go digest to
[`b7840c2`](https://redirect.github.com/oxc-project/tsgolint/commit/b7840c2)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;326](https://redirect.github.com/oxc-project/tsgolint/pull/326)
- chore(deps): update typescript-go digest to
[`33eeaf3`](https://redirect.github.com/oxc-project/tsgolint/commit/33eeaf3)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;330](https://redirect.github.com/oxc-project/tsgolint/pull/330)
- chore(deps): update typescript-go digest to
[`f0ca632`](https://redirect.github.com/oxc-project/tsgolint/commit/f0ca632)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;331](https://redirect.github.com/oxc-project/tsgolint/pull/331)
- chore(deps): update typescript-go digest to
[`a4fa408`](https://redirect.github.com/oxc-project/tsgolint/commit/a4fa408)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;337](https://redirect.github.com/oxc-project/tsgolint/pull/337)
- chore(deps): update typescript-go digest to
[`ca68e0b`](https://redirect.github.com/oxc-project/tsgolint/commit/ca68e0b)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;339](https://redirect.github.com/oxc-project/tsgolint/pull/339)
- chore(deps): update typescript-go digest to
[`4037f3a`](https://redirect.github.com/oxc-project/tsgolint/commit/4037f3a)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;340](https://redirect.github.com/oxc-project/tsgolint/pull/340)
- refactor: allow suggestions/fixes to be generated lazily by
[@&#8203;camchenry](https://redirect.github.com/camchenry) in
[#&#8203;313](https://redirect.github.com/oxc-project/tsgolint/pull/313)
- chore(deps): update typescript-go digest to
[`6642b0a`](https://redirect.github.com/oxc-project/tsgolint/commit/6642b0a)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;342](https://redirect.github.com/oxc-project/tsgolint/pull/342)
- feat(overlayfs): implement overlay filesystem with source overrides
support by [@&#8203;camc314](https://redirect.github.com/camc314) in
[#&#8203;291](https://redirect.github.com/oxc-project/tsgolint/pull/291)
- feat!: make fixes/suggestions opt-in by
[@&#8203;camchenry](https://redirect.github.com/camchenry) in
[#&#8203;317](https://redirect.github.com/oxc-project/tsgolint/pull/317)
- perf: `no-array-delete`: only get token ranges when fixing by
[@&#8203;camchenry](https://redirect.github.com/camchenry) in
[#&#8203;320](https://redirect.github.com/oxc-project/tsgolint/pull/320)
- perf: `no-confusing-void-expression`: only generate fixes and
suggestions when fixing by
[@&#8203;camchenry](https://redirect.github.com/camchenry) in
[#&#8203;321](https://redirect.github.com/oxc-project/tsgolint/pull/321)
- perf: `no-duplicate-type-constituents`: only build fixes when needed
by [@&#8203;camchenry](https://redirect.github.com/camchenry) in
[#&#8203;324](https://redirect.github.com/oxc-project/tsgolint/pull/324)
- feat: expose program diagnostics by
[@&#8203;camc314](https://redirect.github.com/camc314) in
[#&#8203;343](https://redirect.github.com/oxc-project/tsgolint/pull/343)

**Full Changelog**:
<https://github.com/oxc-project/tsgolint/compare/v0.3.0...v0.4.0>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cnpm/cnpmcore).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTkuNCIsInVwZGF0ZWRJblZlciI6IjQxLjE1OS40IiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-06 21:17:43 +08:00
时瑾
d7de1cded8 feat: add ffmpeg-builds binary mirror (#879)
[skip ci]

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* FFmpeg builds are now available as a supported binary source, offering
static builds for Windows and Linux platforms (x86_64). Both master
branch and latest release versions are accessible.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-06 21:12:56 +08:00
renovate[bot]
0f11e7730a fix(deps): update dependency ssri to v13 (#870)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [ssri](https://redirect.github.com/npm/ssri) | [`^8.0.1` ->
`^13.0.0`](https://renovatebot.com/diffs/npm/ssri/8.0.1/13.0.0) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/ssri/13.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/ssri/8.0.1/13.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>npm/ssri (ssri)</summary>

###
[`v13.0.0`](https://redirect.github.com/npm/ssri/blob/HEAD/CHANGELOG.md#1300-2025-10-22)

[Compare
Source](https://redirect.github.com/npm/ssri/compare/v12.0.0...v13.0.0)

##### ⚠️ BREAKING CHANGES

- `ssri` now supports node `^20.17.0 || >=22.9.0`

##### Bug Fixes

-
[`46a2520`](46a2520214)
[#&#8203;155](https://redirect.github.com/npm/ssri/pull/155) align to
npm 11 node engine range
([#&#8203;155](https://redirect.github.com/npm/ssri/issues/155))
([@&#8203;owlstronaut](https://redirect.github.com/owlstronaut))
-
[`8f0bbf2`](8f0bbf2717)
[#&#8203;151](https://redirect.github.com/npm/ssri/pull/151) improve
`SRI_REGEX`
([#&#8203;151](https://redirect.github.com/npm/ssri/issues/151))
([@&#8203;ericcornelissen](https://redirect.github.com/ericcornelissen))

##### Chores

-
[`79e0018`](79e0018941)
[#&#8203;146](https://redirect.github.com/npm/ssri/pull/146) postinstall
workflow updates
([#&#8203;146](https://redirect.github.com/npm/ssri/issues/146))
([@&#8203;owlstronaut](https://redirect.github.com/owlstronaut))
-
[`89b775a`](89b775a9cf)
[#&#8203;154](https://redirect.github.com/npm/ssri/pull/154) bump
[@&#8203;npmcli/template-oss](https://redirect.github.com/npmcli/template-oss)
from 4.26.0 to 4.27.1
([#&#8203;154](https://redirect.github.com/npm/ssri/issues/154))
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot],
[@&#8203;npm-cli-bot](https://redirect.github.com/npm-cli-bot))

###
[`v12.0.0`](https://redirect.github.com/npm/ssri/blob/HEAD/CHANGELOG.md#1200-2024-09-24)

[Compare
Source](https://redirect.github.com/npm/ssri/compare/v11.0.0...v12.0.0)

##### ⚠️ BREAKING CHANGES

- `ssri` now supports node `^18.17.0 || >=20.5.0`

##### Bug Fixes

-
[`b7a3f9a`](b7a3f9ad35)
[#&#8203;141](https://redirect.github.com/npm/ssri/pull/141) align to
npm 10 node engine range
([@&#8203;hashtagchris](https://redirect.github.com/hashtagchris))

##### Chores

-
[`f8121e9`](f8121e9e5e)
[#&#8203;141](https://redirect.github.com/npm/ssri/pull/141) run
template-oss-apply
([@&#8203;hashtagchris](https://redirect.github.com/hashtagchris))

###
[`v11.0.0`](https://redirect.github.com/npm/ssri/blob/HEAD/CHANGELOG.md#1100-2024-09-03)

[Compare
Source](https://redirect.github.com/npm/ssri/compare/v10.0.6...v11.0.0)

##### ⚠️ BREAKING CHANGES

- `ssri` is now compatible with the following semver range for node:
`^16.14.0 || >=18.0.0`

##### Bug Fixes

-
[`29a6e2c`](29a6e2c2e0)
Address breaking engine change in dependency
([@&#8203;hashtagchris](https://redirect.github.com/hashtagchris))

##### Chores

-
[`db4219f`](db4219f28a)
bump
[@&#8203;npmcli/eslint-config](https://redirect.github.com/npmcli/eslint-config)
from 4.0.5 to 5.0.0
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])
-
[`f2dd012`](f2dd0125e4)
template-oss-apply
([@&#8203;hashtagchris](https://redirect.github.com/hashtagchris))
-
[`f2a2a9d`](f2a2a9d09f)
postinstall for dependabot template-oss PR
([@&#8203;hashtagchris](https://redirect.github.com/hashtagchris))
-
[`4508f71`](4508f71d8a)
bump
[@&#8203;npmcli/template-oss](https://redirect.github.com/npmcli/template-oss)
from 4.22.0 to 4.23.3
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])

###
[`v10.0.6`](https://redirect.github.com/npm/ssri/blob/HEAD/CHANGELOG.md#1006-2024-05-04)

[Compare
Source](https://redirect.github.com/npm/ssri/compare/v10.0.5...v10.0.6)

##### Bug Fixes

-
[`f3773e2`](f3773e21f9)
[#&#8203;127](https://redirect.github.com/npm/ssri/pull/127) linting:
no-unused-vars
([@&#8203;lukekarrys](https://redirect.github.com/lukekarrys))

##### Chores

-
[`55e7dfb`](55e7dfbe3f)
[#&#8203;127](https://redirect.github.com/npm/ssri/pull/127) bump
[@&#8203;npmcli/template-oss](https://redirect.github.com/npmcli/template-oss)
to 4.22.0 ([@&#8203;lukekarrys](https://redirect.github.com/lukekarrys))
-
[`96d1795`](96d1795b93)
[#&#8203;127](https://redirect.github.com/npm/ssri/pull/127) postinstall
for dependabot template-oss PR
([@&#8203;lukekarrys](https://redirect.github.com/lukekarrys))
-
[`10d5e8a`](10d5e8ae49)
[#&#8203;125](https://redirect.github.com/npm/ssri/pull/125) bump
[@&#8203;npmcli/template-oss](https://redirect.github.com/npmcli/template-oss)
from 4.21.3 to 4.21.4
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])

###
[`v10.0.5`](https://redirect.github.com/npm/ssri/blob/HEAD/CHANGELOG.md#1005-2023-08-14)

[Compare
Source](https://redirect.github.com/npm/ssri/compare/v10.0.4...v10.0.5)

##### Dependencies

-
[`00dacfd`](00dacfd5e9)
[#&#8203;94](https://redirect.github.com/npm/ssri/pull/94) bump minipass
from 5.0.0 to 7.0.3

###
[`v10.0.4`](https://redirect.github.com/npm/ssri/blob/HEAD/CHANGELOG.md#1004-2023-04-26)

[Compare
Source](https://redirect.github.com/npm/ssri/compare/v10.0.3...v10.0.4)

##### Dependencies

-
[`152e2bc`](152e2bce43)
[#&#8203;78](https://redirect.github.com/npm/ssri/pull/78) bump minipass
from 4.2.7 to 5.0.0
([#&#8203;78](https://redirect.github.com/npm/ssri/issues/78))

###
[`v10.0.3`](https://redirect.github.com/npm/ssri/blob/HEAD/CHANGELOG.md#1003-2023-04-11)

[Compare
Source](https://redirect.github.com/npm/ssri/compare/v10.0.2...v10.0.3)

##### Bug Fixes

-
[`7fef846`](7fef8463d4)
[#&#8203;79](https://redirect.github.com/npm/ssri/pull/79) optimize
adding this.algorithm to algorithms list
([@&#8203;wraithgar](https://redirect.github.com/wraithgar))
-
[`d90f674`](d90f674cd5)
[#&#8203;79](https://redirect.github.com/npm/ssri/pull/79) prevent
DEFAULT\_ALGORITHM mutation
([@&#8203;wraithgar](https://redirect.github.com/wraithgar))
-
[`4e94d15`](4e94d15dd9)
[#&#8203;79](https://redirect.github.com/npm/ssri/pull/79)
Integrity#match prioritizes overlapping hashes
([@&#8203;wraithgar](https://redirect.github.com/wraithgar))
-
[`dce3dab`](dce3dab846)
[#&#8203;79](https://redirect.github.com/npm/ssri/pull/79) faster stream
verification ([@&#8203;H4ad](https://redirect.github.com/H4ad))

###
[`v10.0.2`](https://redirect.github.com/npm/ssri/blob/HEAD/CHANGELOG.md#1002-2023-04-03)

[Compare
Source](https://redirect.github.com/npm/ssri/compare/v10.0.1...v10.0.2)

##### Bug Fixes

-
[`8e80eca`](8e80eca497)
[#&#8203;74](https://redirect.github.com/npm/ssri/pull/74) move from
symbols to private methods
([#&#8203;74](https://redirect.github.com/npm/ssri/issues/74))
([@&#8203;wraithgar](https://redirect.github.com/wraithgar))
-
[`a316b12`](a316b12e0c)
[#&#8203;75](https://redirect.github.com/npm/ssri/pull/75) faster
toString for integrity
([#&#8203;75](https://redirect.github.com/npm/ssri/issues/75))
([@&#8203;H4ad](https://redirect.github.com/H4ad))
-
[`6e6877d`](6e6877d55c)
[#&#8203;72](https://redirect.github.com/npm/ssri/pull/72) remove spread
of defaultOpts
([#&#8203;72](https://redirect.github.com/npm/ssri/issues/72))
([@&#8203;H4ad](https://redirect.github.com/H4ad))

###
[`v10.0.1`](https://redirect.github.com/npm/ssri/blob/HEAD/CHANGELOG.md#1001-2022-12-07)

[Compare
Source](https://redirect.github.com/npm/ssri/compare/v10.0.0...v10.0.1)

##### Dependencies

-
[`4f6ba1e`](4f6ba1e5cc)
[#&#8203;64](https://redirect.github.com/npm/ssri/pull/64) bump minipass
from 3.3.6 to 4.0.0

###
[`v10.0.0`](https://redirect.github.com/npm/ssri/blob/HEAD/CHANGELOG.md#1000-2022-10-10)

[Compare
Source](https://redirect.github.com/npm/ssri/compare/v9.0.1...v10.0.0)

##### ⚠️ BREAKING CHANGES

- `ssri` is now compatible with the following semver range for node:
`^14.17.0 || ^16.13.0 || >=18.0.0`

##### Features

-
[`3de0c45`](3de0c4502d)
[#&#8203;52](https://redirect.github.com/npm/ssri/pull/52) postinstall
for dependabot template-oss PR
([@&#8203;lukekarrys](https://redirect.github.com/lukekarrys))

##### Bug Fixes

-
[`2e876d1`](2e876d12a6)
[#&#8203;48](https://redirect.github.com/npm/ssri/pull/48) properly
handle missing algorithm type
([#&#8203;48](https://redirect.github.com/npm/ssri/issues/48))
([@&#8203;ahmedwelhakim](https://redirect.github.com/ahmedwelhakim))

#####
[9.0.1](https://redirect.github.com/npm/ssri/compare/v9.0.0...v9.0.1)
(2022-05-19)

##### Bug Fixes

- store emitted events and re-emit them for late listeners
([#&#8203;39](https://redirect.github.com/npm/ssri/issues/39))
([c5421f1](c5421f1fb4))

###
[`v9.0.1`](https://redirect.github.com/npm/ssri/blob/HEAD/CHANGELOG.md#1000-2022-10-10)

[Compare
Source](https://redirect.github.com/npm/ssri/compare/v9.0.0...v9.0.1)

##### ⚠️ BREAKING CHANGES

- `ssri` is now compatible with the following semver range for node:
`^14.17.0 || ^16.13.0 || >=18.0.0`

##### Features

-
[`3de0c45`](3de0c4502d)
[#&#8203;52](https://redirect.github.com/npm/ssri/pull/52) postinstall
for dependabot template-oss PR
([@&#8203;lukekarrys](https://redirect.github.com/lukekarrys))

##### Bug Fixes

-
[`2e876d1`](2e876d12a6)
[#&#8203;48](https://redirect.github.com/npm/ssri/pull/48) properly
handle missing algorithm type
([#&#8203;48](https://redirect.github.com/npm/ssri/issues/48))
([@&#8203;ahmedwelhakim](https://redirect.github.com/ahmedwelhakim))

#####
[9.0.1](https://redirect.github.com/npm/ssri/compare/v9.0.0...v9.0.1)
(2022-05-19)

##### Bug Fixes

- store emitted events and re-emit them for late listeners
([#&#8203;39](https://redirect.github.com/npm/ssri/issues/39))
([c5421f1](c5421f1fb4))

###
[`v9.0.0`](https://redirect.github.com/npm/ssri/blob/HEAD/CHANGELOG.md#1000-2022-10-10)

[Compare
Source](https://redirect.github.com/npm/ssri/compare/v8.0.1...v9.0.0)

##### ⚠️ BREAKING CHANGES

- `ssri` is now compatible with the following semver range for node:
`^14.17.0 || ^16.13.0 || >=18.0.0`

##### Features

-
[`3de0c45`](3de0c4502d)
[#&#8203;52](https://redirect.github.com/npm/ssri/pull/52) postinstall
for dependabot template-oss PR
([@&#8203;lukekarrys](https://redirect.github.com/lukekarrys))

##### Bug Fixes

-
[`2e876d1`](2e876d12a6)
[#&#8203;48](https://redirect.github.com/npm/ssri/pull/48) properly
handle missing algorithm type
([#&#8203;48](https://redirect.github.com/npm/ssri/issues/48))
([@&#8203;ahmedwelhakim](https://redirect.github.com/ahmedwelhakim))

#####
[9.0.1](https://redirect.github.com/npm/ssri/compare/v9.0.0...v9.0.1)
(2022-05-19)

##### Bug Fixes

- store emitted events and re-emit them for late listeners
([#&#8203;39](https://redirect.github.com/npm/ssri/issues/39))
([c5421f1](c5421f1fb4))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cnpm/cnpmcore).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTkuNCIsInVwZGF0ZWRJblZlciI6IjQxLjE1OS40IiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-29 23:36:35 +08:00
MK (fengmk2)
0d32146562 chore: add deployment test (#874)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Added an npm build script to simplify project compilation.

* **Chores**
  * Simplified CI dependency installation to use standard npm install.
* Added a new deployment test job that runs multi-service integration
checks, build, health checks, and graceful shutdown.
* Introduced an environment-variable gate to allow opting into local
filesystem behavior in production.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-29 23:35:06 +08:00
renovate[bot]
6c29f084b2 fix(deps): update dependency validate-npm-package-name to v7 [skip ci] (#873)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[validate-npm-package-name](https://redirect.github.com/npm/validate-npm-package-name)
| [`^6.0.0` ->
`^7.0.0`](https://renovatebot.com/diffs/npm/validate-npm-package-name/6.0.2/7.0.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/validate-npm-package-name/7.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/validate-npm-package-name/6.0.2/7.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>npm/validate-npm-package-name
(validate-npm-package-name)</summary>

###
[`v7.0.0`](https://redirect.github.com/npm/validate-npm-package-name/blob/HEAD/CHANGELOG.md#700-2025-10-22)

[Compare
Source](https://redirect.github.com/npm/validate-npm-package-name/compare/v6.0.2...v7.0.0)

##### ⚠️ BREAKING CHANGES

- align to npm 11 node engine range
([#&#8203;147](https://redirect.github.com/npm/validate-npm-package-name/issues/147))

##### Bug Fixes

-
[`69e0cbb`](69e0cbbd77)
[#&#8203;147](https://redirect.github.com/npm/validate-npm-package-name/pull/147)
align to npm 11 node engine range
([#&#8203;147](https://redirect.github.com/npm/validate-npm-package-name/issues/147))
([@&#8203;owlstronaut](https://redirect.github.com/owlstronaut))

##### Chores

-
[`c6866b9`](c6866b9d96)
[#&#8203;146](https://redirect.github.com/npm/validate-npm-package-name/pull/146)
bump
[@&#8203;npmcli/template-oss](https://redirect.github.com/npmcli/template-oss)
from 4.26.0 to 4.27.1
([#&#8203;146](https://redirect.github.com/npm/validate-npm-package-name/issues/146))
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot],
[@&#8203;npm-cli-bot](https://redirect.github.com/npm-cli-bot))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cnpm/cnpmcore).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTkuNCIsInVwZGF0ZWRJblZlciI6IjQxLjE1OS40IiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-29 21:19:12 +08:00
renovate[bot]
01385f4954 chore(deps): update dependency type-fest to v5 [skip ci] (#871)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [type-fest](https://redirect.github.com/sindresorhus/type-fest) |
[`^2.5.3` ->
`^5.0.0`](https://renovatebot.com/diffs/npm/type-fest/2.19.0/5.1.0) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/type-fest/5.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/type-fest/2.19.0/5.1.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>sindresorhus/type-fest (type-fest)</summary>

###
[`v5.1.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v5.1.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v5.0.1...v5.1.0)

##### New types

-
[`TupleOf`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/tuple-of.d.ts)
([#&#8203;1247](https://redirect.github.com/sindresorhus/type-fest/issues/1247))
[`7fb2f75`](https://redirect.github.com/sindresorhus/type-fest/commit/7fb2f75)
-
[`Xor`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/xor.d.ts)
([#&#8203;1254](https://redirect.github.com/sindresorhus/type-fest/issues/1254))
[`ad04bc5`](https://redirect.github.com/sindresorhus/type-fest/commit/ad04bc5)
-
[`SplitOnRestElement`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/split-on-rest-element.d.ts)
([#&#8203;1166](https://redirect.github.com/sindresorhus/type-fest/issues/1166))
[`34b8fad`](https://redirect.github.com/sindresorhus/type-fest/commit/34b8fad)
-
[`ExtractRestElement`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/extract-rest-element.d.ts)
([#&#8203;1166](https://redirect.github.com/sindresorhus/type-fest/issues/1166))
[`34b8fad`](https://redirect.github.com/sindresorhus/type-fest/commit/34b8fad)
-
[`ExcludeRestElement`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/exclude-rest-element.d.ts)
([#&#8203;1166](https://redirect.github.com/sindresorhus/type-fest/issues/1166))
[`34b8fad`](https://redirect.github.com/sindresorhus/type-fest/commit/34b8fad)

##### Improvements

- `ReadonlyTuple`: Deprecate in favor of `TupleOf`
([#&#8203;1256](https://redirect.github.com/sindresorhus/type-fest/issues/1256))
[`af4bebc`](https://redirect.github.com/sindresorhus/type-fest/commit/af4bebc)
- `TsConfigJson`: Add missing lib enum values
([#&#8203;1263](https://redirect.github.com/sindresorhus/type-fest/issues/1263))
[`72f491f`](https://redirect.github.com/sindresorhus/type-fest/commit/72f491f)
- `TsConfigJson`: Add `rewriteRelativeImportExtensions`
([#&#8203;1262](https://redirect.github.com/sindresorhus/type-fest/issues/1262))
[`7d011ce`](https://redirect.github.com/sindresorhus/type-fest/commit/7d011ce)

##### Fixes

- `PartialDeep`: Fix behavior with functions containing multiple call
signatures
([#&#8203;1259](https://redirect.github.com/sindresorhus/type-fest/issues/1259))
[`3bd9de6`](https://redirect.github.com/sindresorhus/type-fest/commit/3bd9de6)
- `IsEqual`: Fix behaviour when instantiated with `never` and `unknown`
([#&#8203;1251](https://redirect.github.com/sindresorhus/type-fest/issues/1251))
[`785549f`](https://redirect.github.com/sindresorhus/type-fest/commit/785549f)
- `FixedLengthArray`: Fix element type
([#&#8203;1246](https://redirect.github.com/sindresorhus/type-fest/issues/1246))
[`ee29ef7`](https://redirect.github.com/sindresorhus/type-fest/commit/ee29ef7)
- `is-equal`: Fix handling with intersecting wrapped types
([#&#8203;1231](https://redirect.github.com/sindresorhus/type-fest/issues/1231))
[`5af60a1`](https://redirect.github.com/sindresorhus/type-fest/commit/5af60a1)

***

###
[`v5.0.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v5.0.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v5.0.0...v5.0.1)

- Add missing exports
[`4f9c248`](https://redirect.github.com/sindresorhus/type-fest/commit/4f9c248)

***

###
[`v5.0.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v5.0.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.41.0...v5.0.0)

##### Breaking

- This package is now pure ESM. **Please [read
this](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c).**
- Require TypeScript 5.9
[`b5b0214`](https://redirect.github.com/sindresorhus/type-fest/commit/b5b0214)
- Require Node.js 20
[`cc2b0f2`](https://redirect.github.com/sindresorhus/type-fest/commit/cc2b0f2)
- Reminder: `type-fest` requires `strict: true` in your tsconfig.
- `StringKeyOf`: Rename to
[`KeyAsString`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/key-as-string.d.ts)
[`e492c9c`](https://redirect.github.com/sindresorhus/type-fest/commit/e492c9c)
-
[`ArrayTail`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/array-tail.d.ts):
Enable
[`preserveReadonly`](68469722a9/source/array-tail.d.ts (L8-L30))
by default and remove the option
[`b34b1d8`](https://redirect.github.com/sindresorhus/type-fest/commit/b34b1d8)
-
[`CamelCase`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/camel-case.d.ts)
/
[`CamelCasedProperties`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/camel-cased-properties.d.ts)
/
[`CamelCasedPropertiesDeep`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/camel-cased-properties-deep.d.ts)
/
[`PascalCase`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/pascal-case.d.ts)
/
[`PascalCasedProperties`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/pascal-cased-properties.d.ts)
/
[`PascalCasedPropertiesDeep`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/pascal-cased-properties-deep.d.ts):
Disable `preserveConsecutiveUppercase` by default
[`8226c1b`](https://redirect.github.com/sindresorhus/type-fest/commit/8226c1b)
  - This aligns it with the general JavaScript naming convention.
-
[`PartialDeep`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/partial-deep.d.ts):
Disable `allowUndefinedInNonTupleArrays` by default
[`b3c4524`](https://redirect.github.com/sindresorhus/type-fest/commit/b3c4524)
-
[`Split`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/split.d.ts):
Enable `strictLiteralChecks` by default
[`544a846`](https://redirect.github.com/sindresorhus/type-fest/commit/544a846)
-
[`Paths`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/paths.d.ts):
Default `maxRecursionDepth` 5 (was 10)
[`2ab5dec`](https://redirect.github.com/sindresorhus/type-fest/commit/2ab5dec)
-
[`ObservableLike`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/globals/observable-like.d.ts):
Move to sub-export
[`2a1072e`](https://redirect.github.com/sindresorhus/type-fest/commit/2a1072e)
- Deprecate `If*` types in favor of a single
[`If`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/if.d.ts)
[`4c2151a`](https://redirect.github.com/sindresorhus/type-fest/commit/4c2151a)

##### New types

-
[`Alphanumeric`](fc14f87e7f/source/characters.d.ts)
— Single alphanumeric character (`A–Z`, `a–z`, `0–9`).
[`484e030`](https://redirect.github.com/sindresorhus/type-fest/commit/484e030)
-
[`AllExtend`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/all-extend.d.ts)
— Evaluates to `true` if every element of a tuple/array extends `U`.
[`c8c6d55`](https://redirect.github.com/sindresorhus/type-fest/commit/c8c6d55)
-
[`ConditionalSimplify`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/conditional-simplify.d.ts)
— Simplifies a type’s unions/intersections with opt-in controls.
[`b7a4771`](https://redirect.github.com/sindresorhus/type-fest/commit/b7a4771)
-
[`ConditionalSimplifyDeep`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/conditional-simplify-deep.d.ts)
— Deep version of `ConditionalSimplify` that recurses into objects.
[`b7a4771`](https://redirect.github.com/sindresorhus/type-fest/commit/b7a4771)
-
[`DigitCharacter`](fc14f87e7f/source/characters.d.ts)
— Single ASCII digit character (`0–9`).
[`484e030`](https://redirect.github.com/sindresorhus/type-fest/commit/484e030)
-
[`ExcludeStrict`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/exclude-strict.d.ts)
— Non-distributive, stricter variant of `Exclude<T, U>`.
[`e6f62a2`](https://redirect.github.com/sindresorhus/type-fest/commit/e6f62a2)
-
[`ExtendsStrict`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/extends-strict.d.ts)
— Non-distributive `A extends B` check.
[`d71242a`](https://redirect.github.com/sindresorhus/type-fest/commit/d71242a)
-
[`ExtractStrict`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/extract-strict.d.ts)
— Non-distributive, stricter variant of `Extract<T, U>`.
[`98d24fa`](https://redirect.github.com/sindresorhus/type-fest/commit/98d24fa)
-
[`IsLowercase`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/is-lowercase.d.ts)
— Evaluates to `true` if a string literal is all lowercase.
[`afe132c`](https://redirect.github.com/sindresorhus/type-fest/commit/afe132c)
-
[`IsNullable`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/is-nullable.d.ts)
— Evaluates to `true` if `T` includes `null`.
[`5067e25`](https://redirect.github.com/sindresorhus/type-fest/commit/5067e25)
-
[`IsOptional`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/is-optional.d.ts)
— Evaluates to `true` if `T` includes `undefined`.
[`5067e25`](https://redirect.github.com/sindresorhus/type-fest/commit/5067e25)
-
[`IsOptionalKeyOf`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/is-optional-key-of.d.ts)
— Evaluates to `true` if property `K` of `T` is optional.
[`93728b5`](https://redirect.github.com/sindresorhus/type-fest/commit/93728b5)
-
[`IsReadonlyKeyOf`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/is-readonly-key-of.d.ts)
— Evaluates to `true` if property `K` of `T` is readonly.
[`93728b5`](https://redirect.github.com/sindresorhus/type-fest/commit/93728b5)
-
[`IsRequiredKeyOf`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/is-required-key-of.d.ts)
— Evaluates to `true` if property `K` of `T` is required.
[`93728b5`](https://redirect.github.com/sindresorhus/type-fest/commit/93728b5)
-
[`IsUnion`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/is-union.d.ts)
— Evaluates to `true` if `T` is a union type.
[`b3d92ed`](https://redirect.github.com/sindresorhus/type-fest/commit/b3d92ed)
-
[`IsUndefined`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/is-undefined.d.ts)
— Evaluates to `true` if the type is exactly `undefined`.
[`f7bc576`](https://redirect.github.com/sindresorhus/type-fest/commit/f7bc576)
-
[`IsUppercase`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/is-uppercase.d.ts)
— Evaluates to `true` if a string literal is all uppercase.
[`afe132c`](https://redirect.github.com/sindresorhus/type-fest/commit/afe132c)
-
[`LowercaseLetter`](fc14f87e7f/source/characters.d.ts)
— Single lowercase Latin letter (`a–z`).
[`484e030`](https://redirect.github.com/sindresorhus/type-fest/commit/484e030)
-
[`RemovePrefix`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/remove-prefix.d.ts)
— Removes a specified prefix from a string literal.
[`18a1c04`](https://redirect.github.com/sindresorhus/type-fest/commit/18a1c04)
-
[`UppercaseLetter`](fc14f87e7f/source/characters.d.ts)
— Single uppercase Latin letter (`A–Z`).
[`484e030`](https://redirect.github.com/sindresorhus/type-fest/commit/484e030)

##### Improvements

-
[`Jsonify`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/jsonify.d.ts):
Handle `unknown` as `JsonValue`
[`642bb13`](https://redirect.github.com/sindresorhus/type-fest/commit/642bb13)
-
[`SetRequired`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/set-required.d.ts)
/
[`SetOptional`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/set-optional.d.ts)
/
[`SetReadonly`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/set-readonly.d.ts):
Handle functions with properties
[`a5e45d4`](https://redirect.github.com/sindresorhus/type-fest/commit/a5e45d4)
-
[`Schema`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/schema.d.ts):
Preserve arrays/remove extraneous unions
[`8a96def`](https://redirect.github.com/sindresorhus/type-fest/commit/8a96def);
drop `undefined` for `recurseIntoArrays`
[`1cb955b`](https://redirect.github.com/sindresorhus/type-fest/commit/1cb955b)
-
[`ReadonlyKeysOf`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/readonly-keys-of.d.ts)
/
[`WritableKeysOf`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/writable-keys-of.d.ts):
Add `object` constraint
[`a6efbe0`](https://redirect.github.com/sindresorhus/type-fest/commit/a6efbe0)
-
[`TsConfigJson`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/tsconfig-json.d.ts):
Add TypeScript 5.9 fields
[`d2bda94`](https://redirect.github.com/sindresorhus/type-fest/commit/d2bda94)

##### Fixes

-
[`Or`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/or.d.ts):
Fix with `boolean`, `never`, `any`
[`42d6106`](https://redirect.github.com/sindresorhus/type-fest/commit/42d6106)
-
[`And`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/and.d.ts):
Fix with `boolean`, `never`, `any`
[`b38ac60`](https://redirect.github.com/sindresorhus/type-fest/commit/b38ac60)
-
[`IsStringLiteral`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/is-literal.d.ts):
Fix uncollapsed unions, and tagged types
[`eb37799`](https://redirect.github.com/sindresorhus/type-fest/commit/eb37799)
/
[`d1b35c7`](https://redirect.github.com/sindresorhus/type-fest/commit/d1b35c7)
-
[`Paths`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/paths.d.ts):
Fix behavior with index signatures
[`9926e5d`](https://redirect.github.com/sindresorhus/type-fest/commit/9926e5d)
-
[`ConditionalKeys`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/conditional-keys.d.ts):
Fix behavior with arrays and unions
[`4d7cc50`](https://redirect.github.com/sindresorhus/type-fest/commit/4d7cc50)
-
[`RequiredDeep`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/required-deep.d.ts):
Fix with `undefined`
[`bfcdbc4`](https://redirect.github.com/sindresorhus/type-fest/commit/bfcdbc4)
-
[`Split`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/split.d.ts):
Fix template strings ending with interpolation
[`853b881`](https://redirect.github.com/sindresorhus/type-fest/commit/853b881)
-
[`ArrayTail`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/array-tail.d.ts):
Fix fix non-tuple arrays
[`f3aabd8`](https://redirect.github.com/sindresorhus/type-fest/commit/f3aabd8)
- Fix `UnionMin` and `UnionMax`
[`d52d5e7`](https://redirect.github.com/sindresorhus/type-fest/commit/d52d5e7)

##### Meta

Huge thanks to all the contributors to this release, especially
[@&#8203;som-sm](https://redirect.github.com/som-sm) 🙌

***

###
[`v4.41.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.41.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.40.1...v4.41.0)

- Add
[`SetNonNullableDeep`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/set-non-nullable-deep.d.ts)
type
([#&#8203;1117](https://redirect.github.com/sindresorhus/type-fest/issues/1117))
[`b9606e7`](https://redirect.github.com/sindresorhus/type-fest/commit/b9606e7)
- `LessThan` / `GreaterThan` / `GreaterThanOrEqual`: Fix behaviour with
unions
([#&#8203;1116](https://redirect.github.com/sindresorhus/type-fest/issues/1116))
[`afd809a`](https://redirect.github.com/sindresorhus/type-fest/commit/afd809a)
- `RequireAllOrNone` / `RequireAtLeastOne` / `RequireExactlyOne` /
`RequireOneOrNone`: Fix behaviour with `any` and `never`
([#&#8203;1113](https://redirect.github.com/sindresorhus/type-fest/issues/1113))
[`8c154e9`](https://redirect.github.com/sindresorhus/type-fest/commit/8c154e9)

***

###
[`v4.40.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.40.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.40.0...v4.40.1)

- `PartialDeep`: Fix behaviour with functions containing properties
([#&#8203;1108](https://redirect.github.com/sindresorhus/type-fest/issues/1108))
[`86a3a69`](https://redirect.github.com/sindresorhus/type-fest/commit/86a3a69)
- `CamelCasedPropertiesDeep` / `DelimiterCasedPropertiesDeep` /
`KebabCasedPropertiesDeep` / `PascalCasedPropertiesDeep` /
`SnakeCasedPropertiesDeep`: Fix behaviour when property value is
`unknown`
([#&#8203;1112](https://redirect.github.com/sindresorhus/type-fest/issues/1112))
[`cfcf9ec`](https://redirect.github.com/sindresorhus/type-fest/commit/cfcf9ec)

***

###
[`v4.40.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.40.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.39.1...v4.40.0)

- Add
[`NonEmptyString`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/non-empty-string.d.ts)
type
([#&#8203;1103](https://redirect.github.com/sindresorhus/type-fest/issues/1103))
[`19a9c37`](https://redirect.github.com/sindresorhus/type-fest/commit/19a9c37)
- Add
[`UnknownMap`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/unknown-map.d.ts)
type
([#&#8203;1106](https://redirect.github.com/sindresorhus/type-fest/issues/1106))
[`b4ace2d`](https://redirect.github.com/sindresorhus/type-fest/commit/b4ace2d)
- Add
[`UnknownSet`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/unknown-set.d.ts)
type
([#&#8203;1106](https://redirect.github.com/sindresorhus/type-fest/issues/1106))
[`b4ace2d`](https://redirect.github.com/sindresorhus/type-fest/commit/b4ace2d)
- `IsFloat` / `IsInteger`: Fix instantiations with numbers represented
using exponential notation
([#&#8203;1101](https://redirect.github.com/sindresorhus/type-fest/issues/1101))
[`21a92f6`](https://redirect.github.com/sindresorhus/type-fest/commit/21a92f6)

***

###
[`v4.39.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.39.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.39.0...v4.39.1)

- `OptionalKeysOf` / `WritableKeysOf`: Fix generic assignability with
`keyof T`
([#&#8203;1098](https://redirect.github.com/sindresorhus/type-fest/issues/1098))
[`1b41ed3`](https://redirect.github.com/sindresorhus/type-fest/commit/1b41ed3)

***

###
[`v4.39.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.39.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.38.0...v4.39.0)

- `ArrayTail`: Add
[`preserveReadonly`](07cb87075f/source/array-tail.d.ts (L8-L30))
option
([#&#8203;1091](https://redirect.github.com/sindresorhus/type-fest/issues/1091))
[`544271e`](https://redirect.github.com/sindresorhus/type-fest/commit/544271e)
- `PartialDeep `: Fix behaviour when `strictNullChecks` is disabled
([#&#8203;1096](https://redirect.github.com/sindresorhus/type-fest/issues/1096))
[`7536bae`](https://redirect.github.com/sindresorhus/type-fest/commit/7536bae)
- `OptionalKeysOf` / `RequiredKeysOf`: Fix instantiations with unions
and arrays
([#&#8203;1089](https://redirect.github.com/sindresorhus/type-fest/issues/1089))
[`e1ac7b2`](https://redirect.github.com/sindresorhus/type-fest/commit/e1ac7b2)
- `WritableKeysOf` / `ReadonlyKeysOf`: Fix behavior with unions and
optional properties
([#&#8203;1088](https://redirect.github.com/sindresorhus/type-fest/issues/1088))
[`bbf9137`](https://redirect.github.com/sindresorhus/type-fest/commit/bbf9137)

***

###
[`v4.38.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.38.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.37.0...v4.38.0)

- `AsyncReturnType`: Add support for `PromiseLike`
([#&#8203;1082](https://redirect.github.com/sindresorhus/type-fest/issues/1082))
[`72ccde9`](https://redirect.github.com/sindresorhus/type-fest/commit/72ccde9)
- `DelimiterCase` / `SnakeCase` / `ScreamingSnakeCase` / `KebabCase`:
Fix instantiations containing punctuations
([#&#8203;1080](https://redirect.github.com/sindresorhus/type-fest/issues/1080))
[`063e28d`](https://redirect.github.com/sindresorhus/type-fest/commit/063e28d)
- `DelimiterCase`: Pass `Options` generic to all related types
([#&#8203;1078](https://redirect.github.com/sindresorhus/type-fest/issues/1078))
[`1974944`](https://redirect.github.com/sindresorhus/type-fest/commit/1974944)
- `CamelCasedPropertiesDeep`: Make nested array objects respect the
options
([#&#8203;1077](https://redirect.github.com/sindresorhus/type-fest/issues/1077))
[`c11c9ca`](https://redirect.github.com/sindresorhus/type-fest/commit/c11c9ca)

***

###
[`v4.37.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.37.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.36.0...v4.37.0)

- `Sum`: Add negative return value support
([#&#8203;1068](https://redirect.github.com/sindresorhus/type-fest/issues/1068))
[`af5bfb7`](https://redirect.github.com/sindresorhus/type-fest/commit/af5bfb7)
- `Subtract`: Add negative return value support
([#&#8203;1061](https://redirect.github.com/sindresorhus/type-fest/issues/1061))
[`2b85ae2`](https://redirect.github.com/sindresorhus/type-fest/commit/2b85ae2)
- `Split`: Add
[`strictLiteralChecks`](ed8c987129/source/split.d.ts (L12-L34))
option
([#&#8203;1067](https://redirect.github.com/sindresorhus/type-fest/issues/1067))
[`cc93f85`](https://redirect.github.com/sindresorhus/type-fest/commit/cc93f85)
- `Split`: Fix instantiations with unions
([#&#8203;1067](https://redirect.github.com/sindresorhus/type-fest/issues/1067))
[`cc93f85`](https://redirect.github.com/sindresorhus/type-fest/commit/cc93f85)
- `Replace`: Fix instantiations with unions
([#&#8203;1065](https://redirect.github.com/sindresorhus/type-fest/issues/1065))
[`a733698`](https://redirect.github.com/sindresorhus/type-fest/commit/a733698)
- `DelimiterCase` / `SnakeCase` / `ScreamingSnakeCase` / `KebabCase`:
Fix default value for `splitOnNumbers` option
([#&#8203;1073](https://redirect.github.com/sindresorhus/type-fest/issues/1073))
[`e462e72`](https://redirect.github.com/sindresorhus/type-fest/commit/e462e72)

***

###
[`v4.36.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.36.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.35.0...v4.36.0)

- `TsConfigJson`: Add TypeScript 5.8 fields
([#&#8203;1064](https://redirect.github.com/sindresorhus/type-fest/issues/1064))
[`918156a`](https://redirect.github.com/sindresorhus/type-fest/commit/918156a)
- `Replace`: Add support for generating longer strings
([#&#8203;1060](https://redirect.github.com/sindresorhus/type-fest/issues/1060))
[`3c03a0d`](https://redirect.github.com/sindresorhus/type-fest/commit/3c03a0d)
- `DelimiterCase`: Internal improvements
([#&#8203;930](https://redirect.github.com/sindresorhus/type-fest/issues/930))
[`a463c30`](https://redirect.github.com/sindresorhus/type-fest/commit/a463c30)

***

###
[`v4.35.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.35.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.34.1...v4.35.0)

- Add
[`TupleToObject`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/tuple-to-object.d.ts)
type
([#&#8203;1055](https://redirect.github.com/sindresorhus/type-fest/issues/1055))
[`c8149ec`](https://redirect.github.com/sindresorhus/type-fest/commit/c8149ec)
- `Paths`: Add
[`depth`](db3403a4b3/source/paths.d.ts (L95-L128))
option
([#&#8203;1058](https://redirect.github.com/sindresorhus/type-fest/issues/1058))
[`2633e5b`](https://redirect.github.com/sindresorhus/type-fest/commit/2633e5b)

***

###
[`v4.34.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.34.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.34.0...v4.34.1)

- `OmitDeep`: Fix import statement
([#&#8203;1052](https://redirect.github.com/sindresorhus/type-fest/issues/1052))
[`e5b66a4`](https://redirect.github.com/sindresorhus/type-fest/commit/e5b66a4)

***

###
[`v4.34.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.34.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.33.0...v4.34.0)

- Add
[`AllUnionFields`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/all-union-fields.d.ts)
type
([#&#8203;997](https://redirect.github.com/sindresorhus/type-fest/issues/997))
[`9aba4c3`](https://redirect.github.com/sindresorhus/type-fest/commit/9aba4c3)
- `Paths`: Add
[`leavesOnly`](00c2210831/source/paths.d.ts (L54-L93))
option
([#&#8203;1050](https://redirect.github.com/sindresorhus/type-fest/issues/1050))
[`7dfb307`](https://redirect.github.com/sindresorhus/type-fest/commit/7dfb307)
- `OmitDeep`: Fix removal of multiple paths within arrays
([#&#8203;1049](https://redirect.github.com/sindresorhus/type-fest/issues/1049))
[`fa6e31b`](https://redirect.github.com/sindresorhus/type-fest/commit/fa6e31b)

***

###
[`v4.33.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.33.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.32.0...v4.33.0)

- `StringRepeat`: Add support for generating longer strings & fix
instantiations with unions
([#&#8203;1046](https://redirect.github.com/sindresorhus/type-fest/issues/1046))
[`fbccaab`](https://redirect.github.com/sindresorhus/type-fest/commit/fbccaab)
- `Split`: Add support for longer strings
([#&#8203;1042](https://redirect.github.com/sindresorhus/type-fest/issues/1042))
[`49605b9`](https://redirect.github.com/sindresorhus/type-fest/commit/49605b9)
- `Get`: Optimize performance
([#&#8203;1031](https://redirect.github.com/sindresorhus/type-fest/issues/1031))
[`cfb5947`](https://redirect.github.com/sindresorhus/type-fest/commit/cfb5947)
- `KeysOfUnion`: Fix assignability with `keyof`
([#&#8203;1009](https://redirect.github.com/sindresorhus/type-fest/issues/1009))
[`4789c7c`](https://redirect.github.com/sindresorhus/type-fest/commit/4789c7c)
- `IsStringLiteral`: Fix instantiations with infinite string types
([#&#8203;1044](https://redirect.github.com/sindresorhus/type-fest/issues/1044))
[`e7800af`](https://redirect.github.com/sindresorhus/type-fest/commit/e7800af)
- `SetRequiredDeep`: Fix handling of unions in nested keys
([#&#8203;1037](https://redirect.github.com/sindresorhus/type-fest/issues/1037))
[`bf5ce3c`](https://redirect.github.com/sindresorhus/type-fest/commit/bf5ce3c)
- `StringSlice`: Fix return type when passing in non-literal
([#&#8203;1036](https://redirect.github.com/sindresorhus/type-fest/issues/1036))
[`979eccf`](https://redirect.github.com/sindresorhus/type-fest/commit/979eccf)
- `Sum`/`Subtract`: Fix instantiations with unions
([#&#8203;1034](https://redirect.github.com/sindresorhus/type-fest/issues/1034))
[`69bfd51`](https://redirect.github.com/sindresorhus/type-fest/commit/69bfd51)

***

###
[`v4.32.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.32.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.31.0...v4.32.0)

##### New types

-
[`IsTuple`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/is-tuple.d.ts)
([#&#8203;1024](https://redirect.github.com/sindresorhus/type-fest/issues/1024))
[`1e0872d`](https://redirect.github.com/sindresorhus/type-fest/commit/1e0872d)
-
[`SetRequiredDeep`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/set-required-deep.d.ts)
([#&#8203;939](https://redirect.github.com/sindresorhus/type-fest/issues/939))
[`3d54627`](https://redirect.github.com/sindresorhus/type-fest/commit/3d54627)

##### Improvements

- `PartialDeep`: Add
[`allowUndefinedInNonTupleArrays`](20f1995b4a/source/partial-deep.d.ts (L14-L38))
option
([#&#8203;1019](https://redirect.github.com/sindresorhus/type-fest/issues/1019))
[`278df80`](https://redirect.github.com/sindresorhus/type-fest/commit/278df80)

##### Fixes

- `SetRequired`: Fix support for removal of optional modifiers from
tuples
([#&#8203;1030](https://redirect.github.com/sindresorhus/type-fest/issues/1030))
[`c897aad`](https://redirect.github.com/sindresorhus/type-fest/commit/c897aad)

***

###
[`v4.31.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.31.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.30.2...v4.31.0)

- `SetFieldType`: Add option to preserve property modifiers
([#&#8203;1017](https://redirect.github.com/sindresorhus/type-fest/issues/1017))
[`59517cb`](https://redirect.github.com/sindresorhus/type-fest/commit/59517cb)
- `IsEqual`: Fix identity issue
([#&#8203;1012](https://redirect.github.com/sindresorhus/type-fest/issues/1012))
[`591700a`](https://redirect.github.com/sindresorhus/type-fest/commit/591700a)
- `SetOptional`: Fix instantiations with index signatures
([#&#8203;1014](https://redirect.github.com/sindresorhus/type-fest/issues/1014))
[`cb269ff`](https://redirect.github.com/sindresorhus/type-fest/commit/cb269ff)
- `SetRequired`: Fix instantiations with index signatures
([#&#8203;1014](https://redirect.github.com/sindresorhus/type-fest/issues/1014))
[`cb269ff`](https://redirect.github.com/sindresorhus/type-fest/commit/cb269ff)
- `SetReadonly`: Fix instantiations with index signatures
([#&#8203;1014](https://redirect.github.com/sindresorhus/type-fest/issues/1014))
[`cb269ff`](https://redirect.github.com/sindresorhus/type-fest/commit/cb269ff)

###
[`v4.30.2`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.30.2)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.30.1...v4.30.2)

- Fix missing export
([#&#8203;1011](https://redirect.github.com/sindresorhus/type-fest/issues/1011))
[`527d600`](https://redirect.github.com/sindresorhus/type-fest/commit/527d600)
- `SetOptional`/`SetRequired`/`SetReadonly`: Fix when the second
argument is `any`
([#&#8203;1007](https://redirect.github.com/sindresorhus/type-fest/issues/1007))
[`fdbcb11`](https://redirect.github.com/sindresorhus/type-fest/commit/fdbcb11)
- `SetOptional`: Fix when instantiated with unions
([#&#8203;1007](https://redirect.github.com/sindresorhus/type-fest/issues/1007))
[`fdbcb11`](https://redirect.github.com/sindresorhus/type-fest/commit/fdbcb11)

###
[`v4.30.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.30.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.30.0...v4.30.1)

- `Arrayable`: Make it more usable by removing `readonly`
([#&#8203;1003](https://redirect.github.com/sindresorhus/type-fest/issues/1003))
[`a128f69`](https://redirect.github.com/sindresorhus/type-fest/commit/a128f69)

###
[`v4.30.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.30.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.29.1...v4.30.0)

- Add
[`SharedUnionFields`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/shared-union-fields.d.ts)
type
([#&#8203;994](https://redirect.github.com/sindresorhus/type-fest/issues/994))
[`a716c29`](https://redirect.github.com/sindresorhus/type-fest/commit/a716c29)
- `ArrayTail`: Fix support for optional parameters
([#&#8203;977](https://redirect.github.com/sindresorhus/type-fest/issues/977))
[`f6b1387`](https://redirect.github.com/sindresorhus/type-fest/commit/f6b1387)

###
[`v4.29.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.29.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.29.0...v4.29.1)

- `IsAny`: Fix circular constraint error on TypeScript 5.4+
([#&#8203;993](https://redirect.github.com/sindresorhus/type-fest/issues/993))
[`32d94dd`](https://redirect.github.com/sindresorhus/type-fest/commit/32d94dd)

###
[`v4.29.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.29.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.28.1...v4.29.0)

- Add
[`IntClosedRange`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/int-closed-range.d.ts)
type
([#&#8203;992](https://redirect.github.com/sindresorhus/type-fest/issues/992))
[`d7b692b`](https://redirect.github.com/sindresorhus/type-fest/commit/d7b692b)
- `Schema`: Add
[`recurseIntoArrays`](1d44863d3a/source/schema.d.ts (L89-L113))
option
([#&#8203;960](https://redirect.github.com/sindresorhus/type-fest/issues/960))
[`fbbb8ba`](https://redirect.github.com/sindresorhus/type-fest/commit/fbbb8ba)

###
[`v4.28.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.28.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.28.0...v4.28.1)

- `SharedUnionFieldsDeep`: Fix support for optional fields
([#&#8203;988](https://redirect.github.com/sindresorhus/type-fest/issues/988))
[`4b49b93`](https://redirect.github.com/sindresorhus/type-fest/commit/4b49b93)

###
[`v4.28.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.28.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.27.1...v4.28.0)

- `TsConfigJson`: Add TypeScript 5.7 fields
([#&#8203;987](https://redirect.github.com/sindresorhus/type-fest/issues/987))
[`9d6fff3`](https://redirect.github.com/sindresorhus/type-fest/commit/9d6fff3)

###
[`v4.27.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.27.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.27.0...v4.27.1)

- `SharedUnionFieldsDeep`: Fix propagation for non union root types
([#&#8203;984](https://redirect.github.com/sindresorhus/type-fest/issues/984))
[`f215f9f`](https://redirect.github.com/sindresorhus/type-fest/commit/f215f9f)
- `ArraySlice`: Fix support for union input
([#&#8203;986](https://redirect.github.com/sindresorhus/type-fest/issues/986))
[`0efbae3`](https://redirect.github.com/sindresorhus/type-fest/commit/0efbae3)

###
[`v4.27.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.27.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.26.1...v4.27.0)

- Add
[`Words`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/words.d.ts)
type
([#&#8203;975](https://redirect.github.com/sindresorhus/type-fest/issues/975))
[`13c0c83`](https://redirect.github.com/sindresorhus/type-fest/commit/13c0c83)
- `Get`: Fix handling of paths with number template literal
([#&#8203;968](https://redirect.github.com/sindresorhus/type-fest/issues/968))
[`b93f54a`](https://redirect.github.com/sindresorhus/type-fest/commit/b93f54a)
- `TsConfigJson`: Add `noCheck` to `compilerOptions`
([#&#8203;981](https://redirect.github.com/sindresorhus/type-fest/issues/981))
[`a470913`](https://redirect.github.com/sindresorhus/type-fest/commit/a470913)

###
[`v4.26.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.26.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.26.0...v4.26.1)

- `Exact`: Fix usage with recursive types and unions
([#&#8203;949](https://redirect.github.com/sindresorhus/type-fest/issues/949))
[`91f6d39`](https://redirect.github.com/sindresorhus/type-fest/commit/91f6d39)

###
[`v4.26.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.26.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.25.0...v4.26.0)

- Add
[`UnionToTuple`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/union-to-tuple.d.ts)
type
([#&#8203;945](https://redirect.github.com/sindresorhus/type-fest/issues/945))
[`1f4f7a1`](https://redirect.github.com/sindresorhus/type-fest/commit/1f4f7a1)

###
[`v4.25.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.25.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.24.0...v4.25.0)

- Add `StringRepeat` type
([#&#8203;938](https://redirect.github.com/sindresorhus/type-fest/issues/938))
[`a83e87e`](https://redirect.github.com/sindresorhus/type-fest/commit/a83e87e)
- Add `Arrayable` type
[#&#8203;270](https://redirect.github.com/sindresorhus/type-fest/issues/270)
([#&#8203;935](https://redirect.github.com/sindresorhus/type-fest/issues/935))
[`9aabcb9`](https://redirect.github.com/sindresorhus/type-fest/commit/9aabcb9)

###
[`v4.24.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.24.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.23.0...v4.24.0)

- `Path`: Add `bracketNotation` option
([#&#8203;926](https://redirect.github.com/sindresorhus/type-fest/issues/926))
[`3b15a94`](https://redirect.github.com/sindresorhus/type-fest/commit/3b15a94)

###
[`v4.23.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.23.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.22.1...v4.23.0)

- `Paths`: Add `maxRecursionDepth` option
([#&#8203;920](https://redirect.github.com/sindresorhus/type-fest/issues/920))
[`052e887`](https://redirect.github.com/sindresorhus/type-fest/commit/052e887)

###
[`v4.22.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.22.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.22.0...v4.22.1)

- Fix missing exported internal types
([#&#8203;918](https://redirect.github.com/sindresorhus/type-fest/issues/918))
[`4b74444`](https://redirect.github.com/sindresorhus/type-fest/commit/4b74444)

###
[`v4.22.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.22.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.21.0...v4.22.0)

##### New types

-
[`ArrayTail`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/array-tail.d.ts)
([#&#8203;913](https://redirect.github.com/sindresorhus/type-fest/issues/913))
[`128b21e`](https://redirect.github.com/sindresorhus/type-fest/commit/128b21e)
-
[`NonEmptyTuple`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/non-empty-tuple.d.ts)
([#&#8203;915](https://redirect.github.com/sindresorhus/type-fest/issues/915))
[`bb57638`](https://redirect.github.com/sindresorhus/type-fest/commit/bb57638)
-
[`FindGlobalType`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/find-global-type.d.ts)
([#&#8203;908](https://redirect.github.com/sindresorhus/type-fest/issues/908))
[`0086cd6`](https://redirect.github.com/sindresorhus/type-fest/commit/0086cd6)
-
[`FindGlobalInstanceType`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/find-global-type.d.ts)
([#&#8203;908](https://redirect.github.com/sindresorhus/type-fest/issues/908))
[`0086cd6`](https://redirect.github.com/sindresorhus/type-fest/commit/0086cd6)

##### Improvements

- Ensure all `RequireX` types' second parameter is optional
([#&#8203;907](https://redirect.github.com/sindresorhus/type-fest/issues/907))
[`fee4e04`](https://redirect.github.com/sindresorhus/type-fest/commit/fee4e04)
-
[`StructuredCloneable`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/find-global-type.d.ts):
Include web-specific types when available
([#&#8203;908](https://redirect.github.com/sindresorhus/type-fest/issues/908))
[`0086cd6`](https://redirect.github.com/sindresorhus/type-fest/commit/0086cd6)

##### Fixes

- `Exact`: Fix type when class is present
([#&#8203;911](https://redirect.github.com/sindresorhus/type-fest/issues/911))
[`bf85819`](https://redirect.github.com/sindresorhus/type-fest/commit/bf85819)

###
[`v4.21.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.21.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.20.1...v4.21.0)

##### New types

-
[`StructuredCloneable`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/structured-cloneable.d.ts)
([#&#8203;897](https://redirect.github.com/sindresorhus/type-fest/issues/897))
[`737550b`](https://redirect.github.com/sindresorhus/type-fest/commit/737550b)

##### Fixes

- `Jsonify`: Convert `undefined` to `null` in union element of array
([#&#8203;901](https://redirect.github.com/sindresorhus/type-fest/issues/901))
[`60c1024`](https://redirect.github.com/sindresorhus/type-fest/commit/60c1024)
- `Exact`: Fix support for `Date` in union
([#&#8203;902](https://redirect.github.com/sindresorhus/type-fest/issues/902))
[`d89a709`](https://redirect.github.com/sindresorhus/type-fest/commit/d89a709)
- `CamelCasedPropertiesDeep`: Fix handling of non-recursive types inside
target type
([#&#8203;890](https://redirect.github.com/sindresorhus/type-fest/issues/890))
[`476024d`](https://redirect.github.com/sindresorhus/type-fest/commit/476024d)

###
[`v4.20.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.20.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.20.0...v4.20.1)

- `Schema`: Fix handling of arrays
([#&#8203;887](https://redirect.github.com/sindresorhus/type-fest/issues/887))
[`c570ec2`](https://redirect.github.com/sindresorhus/type-fest/commit/c570ec2)
- `Paths`: Prevent infinite recursion
([#&#8203;891](https://redirect.github.com/sindresorhus/type-fest/issues/891))
[`7d4e875`](https://redirect.github.com/sindresorhus/type-fest/commit/7d4e875)

###
[`v4.20.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.20.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.19.0...v4.20.0)

- `SimplifyDeep`: Support array
([#&#8203;888](https://redirect.github.com/sindresorhus/type-fest/issues/888))
[`a6ab051`](https://redirect.github.com/sindresorhus/type-fest/commit/a6ab051)
- `IsLiteral`: Return `false` for tagged types
([#&#8203;886](https://redirect.github.com/sindresorhus/type-fest/issues/886))
[`587380c`](https://redirect.github.com/sindresorhus/type-fest/commit/587380c)

###
[`v4.19.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.19.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.18.3...v4.19.0)

- Add
[`SimplifyDeep`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/simplify-deep.d.ts)
type
([#&#8203;882](https://redirect.github.com/sindresorhus/type-fest/issues/882))
[`3a04fcf`](https://redirect.github.com/sindresorhus/type-fest/commit/3a04fcf)

###
[`v4.18.3`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.18.3)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.18.2...v4.18.3)

- `ConditionalKeys`: Fix filtering out never type
([#&#8203;881](https://redirect.github.com/sindresorhus/type-fest/issues/881))
[`863511d`](https://redirect.github.com/sindresorhus/type-fest/commit/863511d)

###
[`v4.18.2`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.18.2)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.18.1...v4.18.2)

- `CamelCasedPropertiesDeep`: Fix tuple being incorrectly turned into
array
([#&#8203;818](https://redirect.github.com/sindresorhus/type-fest/issues/818))
[`4e7bb18`](https://redirect.github.com/sindresorhus/type-fest/commit/4e7bb18)

###
[`v4.18.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.18.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.18.0...v4.18.1)

- Fix missing exports
([#&#8203;876](https://redirect.github.com/sindresorhus/type-fest/issues/876))
[`ed860e9`](https://redirect.github.com/sindresorhus/type-fest/commit/ed860e9)

###
[`v4.18.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.18.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.17.0...v4.18.0)

##### New types

-
[`Or`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/or.d.ts)
[`909c38e`](https://redirect.github.com/sindresorhus/type-fest/commit/909c38e)
-
[`And`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/and.d.ts)
[`9d628aa`](https://redirect.github.com/sindresorhus/type-fest/commit/9d628aa)

##### Improvements

- `TsConfigJson`: Add `preserve` module type and `ES2022` lib types
([#&#8203;874](https://redirect.github.com/sindresorhus/type-fest/issues/874))
[`7096613`](https://redirect.github.com/sindresorhus/type-fest/commit/7096613)
- `Opaque`: Mark as deprecated
([#&#8203;867](https://redirect.github.com/sindresorhus/type-fest/issues/867))
[`ef7b580`](https://redirect.github.com/sindresorhus/type-fest/commit/ef7b580)
- `UnwrapOpaque`: Mark as deprecated
([#&#8203;867](https://redirect.github.com/sindresorhus/type-fest/issues/867))
[`ef7b580`](https://redirect.github.com/sindresorhus/type-fest/commit/ef7b580)

###
[`v4.17.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.17.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.16.0...v4.17.0)

##### New types

-
[`IsNull`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/is-null.d.ts)
[`d639574`](https://redirect.github.com/sindresorhus/type-fest/commit/d639574)

##### Fixes

- `Zero`: Fix missing export
([#&#8203;870](https://redirect.github.com/sindresorhus/type-fest/issues/870))
[`91a2b1e`](https://redirect.github.com/sindresorhus/type-fest/commit/91a2b1e)

###
[`v4.16.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.16.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.15.0...v4.16.0)

##### New types

-
[`IsInteger`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/is-integer.d.ts)
-
[`IsFloat`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/is-float.d.ts)

##### Fixes

- `Integer`: Fix handling of some edge-cases
([#&#8203;857](https://redirect.github.com/sindresorhus/type-fest/issues/857))
[`f5b09de`](https://redirect.github.com/sindresorhus/type-fest/commit/f5b09de)
- `Float`: Fix handling of some edge-cases
([#&#8203;857](https://redirect.github.com/sindresorhus/type-fest/issues/857))
[`f5b09de`](https://redirect.github.com/sindresorhus/type-fest/commit/f5b09de)

###
[`v4.15.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.15.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.14.0...v4.15.0)

##### New types

-
[`SingleKeyObject`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/single-key-object.d.ts)
([#&#8203;849](https://redirect.github.com/sindresorhus/type-fest/issues/849))
[`fa1c3f3`](https://redirect.github.com/sindresorhus/type-fest/commit/fa1c3f3)
-
[`IfEmptyObject`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/if-empty-object.d.ts)
([#&#8203;849](https://redirect.github.com/sindresorhus/type-fest/issues/849))
[`fa1c3f3`](https://redirect.github.com/sindresorhus/type-fest/commit/fa1c3f3)

##### Fixes

- `ConditionalKeys`: Fix TypeScript 5.4 compatibility
([#&#8203;852](https://redirect.github.com/sindresorhus/type-fest/issues/852))
[`0fb2d62`](https://redirect.github.com/sindresorhus/type-fest/commit/0fb2d62)
- `WritableDeep`: Fix TypeScript 5.4 compatibility
([#&#8203;839](https://redirect.github.com/sindresorhus/type-fest/issues/839))
[`2878773`](https://redirect.github.com/sindresorhus/type-fest/commit/2878773)
- `ReadonlyDeep`: Fix TypeScript 5.4 compatibility
([#&#8203;839](https://redirect.github.com/sindresorhus/type-fest/issues/839))
[`2878773`](https://redirect.github.com/sindresorhus/type-fest/commit/2878773)

###
[`v4.14.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.14.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.13.1...v4.14.0)

- Add
[`DistributedPick`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/distributed-pick.d.ts)
type
([#&#8203;841](https://redirect.github.com/sindresorhus/type-fest/issues/841))
[`fa4099c`](https://redirect.github.com/sindresorhus/type-fest/commit/fa4099c)

###
[`v4.13.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.13.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.13.0...v4.13.1)

- `SetParameterType`: Properly fix compatibility with TypeScript 5.4
([#&#8203;836](https://redirect.github.com/sindresorhus/type-fest/issues/836))
[`a186adb`](https://redirect.github.com/sindresorhus/type-fest/commit/a186adb)

###
[`v4.13.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.13.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.12.0...v4.13.0)

##### New types

-
[`DistributedOmit`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/distributed-omit.d.ts)
([#&#8203;820](https://redirect.github.com/sindresorhus/type-fest/issues/820))
[`bc49577`](https://redirect.github.com/sindresorhus/type-fest/commit/bc49577)

##### Improvements

- `ArraySlice`: Support array spread
([#&#8203;832](https://redirect.github.com/sindresorhus/type-fest/issues/832))
[`d2d0d01`](https://redirect.github.com/sindresorhus/type-fest/commit/d2d0d01)

##### Fixes

- `SetParameterType`: Fix compatibility with TypeScript 5.4
([#&#8203;835](https://redirect.github.com/sindresorhus/type-fest/issues/835))
[`2bc451e`](https://redirect.github.com/sindresorhus/type-fest/commit/2bc451e)
- `OmitDeep`: Fix handling for when the given path is not matched
([#&#8203;834](https://redirect.github.com/sindresorhus/type-fest/issues/834))
[`4f14bff`](https://redirect.github.com/sindresorhus/type-fest/commit/4f14bff)

###
[`v4.12.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.12.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.11.1...v4.12.0)

##### New types

-
[`ArraySlice`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/array-slice.d.ts)
-
[`StringSlice`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/string-slice.d.ts)
-
[`IsNegative`](85221aa12f/source/numeric.d.ts (L172-L187))
-
[`GreaterThan`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/greater-than.d.ts)
-
[`GreaterThanOrEqual`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/greater-than-or-equal.d.ts)
-
[`LessThan`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/less-than.d.ts)
-
[`LessThanOrEqual`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/less-than-or-equal.d.ts)
-
[`Sum`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/sum.d.ts)
-
[`Subtract`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/subtract.d.ts)

###
[`v4.11.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.11.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.11.0...v4.11.1)

- `OmitDeep`: Fix internally imported type
([#&#8203;824](https://redirect.github.com/sindresorhus/type-fest/issues/824))
[`2061925`](https://redirect.github.com/sindresorhus/type-fest/commit/2061925)

###
[`v4.11.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.11.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.10.3...v4.11.0)

##### New types

-
[`OmitDeep`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/omit-deep.d.ts)
([#&#8203;816](https://redirect.github.com/sindresorhus/type-fest/issues/816))
[`0e196aa`](https://redirect.github.com/sindresorhus/type-fest/commit/0e196aa)
-
[`ArraySplice`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/array-splice.d.ts)
([#&#8203;816](https://redirect.github.com/sindresorhus/type-fest/issues/816))
[`0e196aa`](https://redirect.github.com/sindresorhus/type-fest/commit/0e196aa)

##### Improvements

- `Tagged`: Add metadata support
([#&#8203;723](https://redirect.github.com/sindresorhus/type-fest/issues/723))
[`3ec8dba`](https://redirect.github.com/sindresorhus/type-fest/commit/3ec8dba)

###
[`v4.10.3`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.10.3)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.10.2...v4.10.3)

- `PartialOnUndefinedDeep`: Fix it incorrectly removing non-optional
properties when the input type contains an index signature
([#&#8203;764](https://redirect.github.com/sindresorhus/type-fest/issues/764))
[`2f4b55a`](https://redirect.github.com/sindresorhus/type-fest/commit/2f4b55a)

###
[`v4.10.2`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.10.2)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.10.1...v4.10.2)

- `MergeDeep`: Fix compatibility with TypeScript 5.4
([#&#8203;807](https://redirect.github.com/sindresorhus/type-fest/issues/807))
[`5f6165a`](https://redirect.github.com/sindresorhus/type-fest/commit/5f6165a)

###
[`v4.10.1`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.10.1)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.10.0...v4.10.1)

- Fix support for `exactOptionalPropertyTypes: true` tsconfig
([#&#8203;804](https://redirect.github.com/sindresorhus/type-fest/issues/804))
[`a54e313`](https://redirect.github.com/sindresorhus/type-fest/commit/a54e313)

###
[`v4.10.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.10.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.9.0...v4.10.0)

- `Get`: Fix handling of readonly array
([#&#8203;799](https://redirect.github.com/sindresorhus/type-fest/issues/799))
[`4a38651`](https://redirect.github.com/sindresorhus/type-fest/commit/4a38651)
- `SharedUnionFieldsDeep`: Skip if input type is not a union type
([#&#8203;798](https://redirect.github.com/sindresorhus/type-fest/issues/798))
[`6f1db93`](https://redirect.github.com/sindresorhus/type-fest/commit/6f1db93)
- `DelimiterCasedPropertiesDeep`: Don't recurse into intersection type
that include primitive value
([#&#8203;789](https://redirect.github.com/sindresorhus/type-fest/issues/789))
[`eb96609`](https://redirect.github.com/sindresorhus/type-fest/commit/eb96609)
- `Merge`: Don't turn undefined into optional key
([#&#8203;787](https://redirect.github.com/sindresorhus/type-fest/issues/787))
[`0aec247`](https://redirect.github.com/sindresorhus/type-fest/commit/0aec247)

###
[`v4.9.0`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.9.0)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.8.3...v4.9.0)

##### New types

-
[`SharedUnionFieldsDeep`](https://redirect.github.com/sindresorhus/type-fest/blob/main/source/shared-union-fields-deep.d.ts)
([#&#8203;783](https://redirect.github.com/sindresorhus/type-fest/issues/783))
[`94bb3d3`](https://redirect.github.com/sindresorhus/type-fest/commit/94bb3d3)

##### Improvements

- `ReadonlyDeep`: Fix usage with properties defined with `void`
([#&#8203;782](https://redirect.github.com/sindresorhus/type-fest/issues/782))
[`a919f93`](https://redirect.github.com/sindresorhus/type-fest/commit/a919f93)
- `ConditionalPickDeep`: Support interface
([#&#8203;776](https://redirect.github.com/sindresorhus/type-fest/issues/776))
[`ebb7a59`](https://redirect.github.com/sindresorhus/type-fest/commit/ebb7a59)

###
[`v4.8.3`](https://redirect.github.com/sindresorhus/type-fest/releases/tag/v4.8.3)

[Compare
Source](https://redirect.github.com/sindresorhus/type-fest/compare/v4.8.2...v4.8.3)

- `MergeDeep`: Fix optional key when value type is `any` or `never`
([#&#8203;777](https://redirect.github.com/sindresorhus/type-fest/issues/777))
[`609c097`](https://redirect.github.com/sindresorhus/type-fest/commit/609c097)
- `Paths`: Ensure it doesn'

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cnpm/cnpmcore).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTkuNCIsInVwZGF0ZWRJblZlciI6IjQxLjE1OS40IiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-29 21:18:28 +08:00
renovate[bot]
098c75a4ca fix(deps): update dependency read-env-value to v2 (#869)
[skip ci]

This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[read-env-value](https://redirect.github.com/node-modules/read-env-value)
| [`^1.0.0` ->
`^2.0.0`](https://renovatebot.com/diffs/npm/read-env-value/1.1.0/2.0.2)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/read-env-value/2.0.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/read-env-value/1.1.0/2.0.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>node-modules/read-env-value (read-env-value)</summary>

###
[`v2.0.2`](https://redirect.github.com/node-modules/read-env-value/blob/HEAD/CHANGELOG.md#small202-2025-08-08-small)

[Compare
Source](https://redirect.github.com/node-modules/read-env-value/compare/v2.0.1...v2.0.2)

- fix: remove src on publish package
([#&#8203;9](https://redirect.github.com/node-modules/read-env-value/issues/9))
([4a2f39c](https://redirect.github.com/node-modules/read-env-value/commit/4a2f39c)),
closes
[#&#8203;9](https://redirect.github.com/node-modules/read-env-value/issues/9)
- test: remove experimental-strip-types flag
([#&#8203;8](https://redirect.github.com/node-modules/read-env-value/issues/8))
([983da2f](https://redirect.github.com/node-modules/read-env-value/commit/983da2f)),
closes
[#&#8203;8](https://redirect.github.com/node-modules/read-env-value/issues/8)
- chore: change jsr name to
[@&#8203;nw/read-env-value](https://redirect.github.com/nw/read-env-value)
([fbbd50b](https://redirect.github.com/node-modules/read-env-value/commit/fbbd50b))

###
[`v2.0.1`](https://redirect.github.com/node-modules/read-env-value/blob/HEAD/CHANGELOG.md#201-2025-07-31)

[Compare
Source](https://redirect.github.com/node-modules/read-env-value/compare/v2.0.0...v2.0.1)

##### Bug Fixes

- duel publish to npm and jsr
([7cd320e](7cd320e410))

###
[`v2.0.0`](https://redirect.github.com/node-modules/read-env-value/blob/HEAD/CHANGELOG.md#200-2025-07-30)

[Compare
Source](https://redirect.github.com/node-modules/read-env-value/compare/v1.1.0...v2.0.0)

##### ⚠ BREAKING CHANGES

- drop Node.js < 22.17.0 support

##### Features

- esm only
([#&#8203;6](https://redirect.github.com/node-modules/read-env-value/issues/6))
([e7f4dad](e7f4dad3f4))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cnpm/cnpmcore).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTkuNCIsInVwZGF0ZWRJblZlciI6IjQxLjE1OS40IiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-29 21:17:46 +08:00
renovate[bot]
c3059c7138 fix(deps): update dependency npm-package-arg to v13 (#866)
[skip ci]

This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [npm-package-arg](https://redirect.github.com/npm/npm-package-arg) |
[`^10.1.0` ->
`^13.0.0`](https://renovatebot.com/diffs/npm/npm-package-arg/10.1.0/13.0.1)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/npm-package-arg/13.0.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/npm-package-arg/10.1.0/13.0.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>npm/npm-package-arg (npm-package-arg)</summary>

###
[`v13.0.1`](https://redirect.github.com/npm/npm-package-arg/blob/HEAD/CHANGELOG.md#1301-2025-10-06)

[Compare
Source](https://redirect.github.com/npm/npm-package-arg/compare/v13.0.0...v13.0.1)

##### Bug Fixes

-
[`f00dea0`](f00dea08e9)
[#&#8203;211](https://redirect.github.com/npm/npm-package-arg/pull/211)
Correct tarball regex to detect literal dots
([@&#8203;markovejnovic](https://redirect.github.com/markovejnovic))

###
[`v13.0.0`](https://redirect.github.com/npm/npm-package-arg/blob/HEAD/CHANGELOG.md#1300-2025-07-24)

[Compare
Source](https://redirect.github.com/npm/npm-package-arg/compare/v12.0.2...v13.0.0)

##### ⚠️ BREAKING CHANGES

- `npm-package-arg` now supports node `^20.17.0 || >=22.9.0`

##### Bug Fixes

-
[`aa3ed29`](aa3ed290c5)
[#&#8203;207](https://redirect.github.com/npm/npm-package-arg/pull/207)
align to npm 11 node engine range
([@&#8203;owlstronaut](https://redirect.github.com/owlstronaut))

##### Dependencies

-
[`fb6ea64`](fb6ea6416f)
[#&#8203;207](https://redirect.github.com/npm/npm-package-arg/pull/207)
`hosted-git-info@9.0.0`

###
[`v12.0.2`](https://redirect.github.com/npm/npm-package-arg/blob/HEAD/CHANGELOG.md#1202-2025-02-05)

[Compare
Source](https://redirect.github.com/npm/npm-package-arg/compare/v12.0.1...v12.0.2)

##### Bug Fixes

-
[`14cb8a1`](14cb8a18b3)
[#&#8203;200](https://redirect.github.com/npm/npm-package-arg/pull/200)
properly parse non-url encoded file specs
([#&#8203;200](https://redirect.github.com/npm/npm-package-arg/issues/200))
([@&#8203;wraithgar](https://redirect.github.com/wraithgar))

##### Chores

-
[`1343a54`](1343a54064)
[#&#8203;199](https://redirect.github.com/npm/npm-package-arg/pull/199)
bump
[@&#8203;npmcli/template-oss](https://redirect.github.com/npmcli/template-oss)
from 4.23.4 to 4.23.5
([#&#8203;199](https://redirect.github.com/npm/npm-package-arg/issues/199))
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot],
[@&#8203;npm-cli-bot](https://redirect.github.com/npm-cli-bot))

###
[`v12.0.1`](https://redirect.github.com/npm/npm-package-arg/blob/HEAD/CHANGELOG.md#1201-2024-12-10)

[Compare
Source](https://redirect.github.com/npm/npm-package-arg/compare/v12.0.0...v12.0.1)

##### Bug Fixes

-
[`ea07a6e`](ea07a6edc7)
[#&#8203;197](https://redirect.github.com/npm/npm-package-arg/pull/197)
allow for git usernames that start with a number
([#&#8203;197](https://redirect.github.com/npm/npm-package-arg/issues/197))
([@&#8203;wraithgar](https://redirect.github.com/wraithgar))

##### Chores

-
[`41aa799`](41aa799ee5)
[#&#8203;196](https://redirect.github.com/npm/npm-package-arg/pull/196)
bump
[@&#8203;npmcli/template-oss](https://redirect.github.com/npmcli/template-oss)
from 4.23.3 to 4.23.4
([#&#8203;196](https://redirect.github.com/npm/npm-package-arg/issues/196))
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot],
[@&#8203;npm-cli-bot](https://redirect.github.com/npm-cli-bot))

###
[`v12.0.0`](https://redirect.github.com/npm/npm-package-arg/blob/HEAD/CHANGELOG.md#1200-2024-09-25)

[Compare
Source](https://redirect.github.com/npm/npm-package-arg/compare/v11.0.3...v12.0.0)

##### ⚠️ BREAKING CHANGES

- `npm-package-arg` now supports node `^18.17.0 || >=20.5.0`

##### Bug Fixes

-
[`6bf84db`](6bf84db8c3)
[#&#8203;194](https://redirect.github.com/npm/npm-package-arg/pull/194)
align to npm 10 node engine range
([@&#8203;reggi](https://redirect.github.com/reggi))

##### Dependencies

-
[`3361e59`](3361e59441)
[#&#8203;194](https://redirect.github.com/npm/npm-package-arg/pull/194)
`validate-npm-package-name@6.0.0`
-
[`06e3bd6`](06e3bd64cd)
[#&#8203;194](https://redirect.github.com/npm/npm-package-arg/pull/194)
`proc-log@5.0.0`
-
[`96dd671`](96dd671a06)
[#&#8203;194](https://redirect.github.com/npm/npm-package-arg/pull/194)
`hosted-git-info@8.0.0`

##### Chores

-
[`163925e`](163925e693)
[#&#8203;194](https://redirect.github.com/npm/npm-package-arg/pull/194)
run template-oss-apply
([@&#8203;reggi](https://redirect.github.com/reggi))
-
[`a8a9bdd`](a8a9bddc72)
[#&#8203;190](https://redirect.github.com/npm/npm-package-arg/pull/190)
bump
[@&#8203;npmcli/eslint-config](https://redirect.github.com/npmcli/eslint-config)
from 4.0.5 to 5.0.0
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])
-
[`f8d32ec`](f8d32ec3d0)
[#&#8203;188](https://redirect.github.com/npm/npm-package-arg/pull/188)
postinstall for dependabot template-oss PR
([@&#8203;hashtagchris](https://redirect.github.com/hashtagchris))
-
[`a867f96`](a867f96444)
[#&#8203;188](https://redirect.github.com/npm/npm-package-arg/pull/188)
bump
[@&#8203;npmcli/template-oss](https://redirect.github.com/npmcli/template-oss)
from 4.23.1 to 4.23.3
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])

###
[`v11.0.3`](https://redirect.github.com/npm/npm-package-arg/blob/HEAD/CHANGELOG.md#1103-2024-07-22)

[Compare
Source](https://redirect.github.com/npm/npm-package-arg/compare/v11.0.2...v11.0.3)

##### Bug Fixes

-
[`59d53b3`](59d53b3ea2)
[#&#8203;184](https://redirect.github.com/npm/npm-package-arg/pull/184)
throws an err when alias is without name
([#&#8203;184](https://redirect.github.com/npm/npm-package-arg/issues/184))
([@&#8203;milaninfy](https://redirect.github.com/milaninfy))

##### Chores

-
[`911661e`](911661e2bd)
[#&#8203;176](https://redirect.github.com/npm/npm-package-arg/pull/176)
bump
[@&#8203;npmcli/template-oss](https://redirect.github.com/npmcli/template-oss)
to 4.22.0 ([@&#8203;lukekarrys](https://redirect.github.com/lukekarrys))
-
[`575012e`](575012e190)
[#&#8203;186](https://redirect.github.com/npm/npm-package-arg/pull/186)
bump
[@&#8203;npmcli/template-oss](https://redirect.github.com/npmcli/template-oss)
from 4.22.0 to 4.23.1
([#&#8203;186](https://redirect.github.com/npm/npm-package-arg/issues/186))
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot],
[@&#8203;wraithgar](https://redirect.github.com/wraithgar))
-
[`74d06ae`](74d06ae66d)
[#&#8203;176](https://redirect.github.com/npm/npm-package-arg/pull/176)
postinstall for dependabot template-oss PR
([@&#8203;lukekarrys](https://redirect.github.com/lukekarrys))

###
[`v11.0.2`](https://redirect.github.com/npm/npm-package-arg/blob/HEAD/CHANGELOG.md#1102-2024-04-12)

[Compare
Source](https://redirect.github.com/npm/npm-package-arg/compare/v11.0.1...v11.0.2)

##### Documentation

-
[`1765111`](17651118e1)
[#&#8203;171](https://redirect.github.com/npm/npm-package-arg/pull/171)
readme: fix broken badge URL
([#&#8203;171](https://redirect.github.com/npm/npm-package-arg/issues/171))
([@&#8203;10xLaCroixDrinker](https://redirect.github.com/10xLaCroixDrinker))

##### Dependencies

-
[`4ccd080`](4ccd08087e)
[#&#8203;173](https://redirect.github.com/npm/npm-package-arg/pull/173)
`proc-log@4.0.0`
([#&#8203;173](https://redirect.github.com/npm/npm-package-arg/issues/173))

##### Chores

-
[`207ba7d`](207ba7d5cf)
[#&#8203;168](https://redirect.github.com/npm/npm-package-arg/pull/168)
postinstall for dependabot template-oss PR
([@&#8203;lukekarrys](https://redirect.github.com/lukekarrys))
-
[`604c1d2`](604c1d2011)
[#&#8203;168](https://redirect.github.com/npm/npm-package-arg/pull/168)
bump
[@&#8203;npmcli/template-oss](https://redirect.github.com/npmcli/template-oss)
from 4.21.1 to 4.21.3
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])
-
[`82273b5`](82273b59ba)
[#&#8203;165](https://redirect.github.com/npm/npm-package-arg/pull/165)
postinstall for dependabot template-oss PR
([@&#8203;lukekarrys](https://redirect.github.com/lukekarrys))
-
[`4228b37`](4228b378a4)
[#&#8203;165](https://redirect.github.com/npm/npm-package-arg/pull/165)
bump
[@&#8203;npmcli/template-oss](https://redirect.github.com/npmcli/template-oss)
from 4.19.0 to 4.21.1
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])
-
[`d4b1447`](d4b144726c)
[#&#8203;147](https://redirect.github.com/npm/npm-package-arg/pull/147)
postinstall for dependabot template-oss PR
([@&#8203;lukekarrys](https://redirect.github.com/lukekarrys))
-
[`c5920a9`](c5920a9545)
[#&#8203;147](https://redirect.github.com/npm/npm-package-arg/pull/147)
bump
[@&#8203;npmcli/template-oss](https://redirect.github.com/npmcli/template-oss)
from 4.18.1 to 4.19.0
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])
-
[`ee68f93`](ee68f93f7b)
[#&#8203;146](https://redirect.github.com/npm/npm-package-arg/pull/146)
postinstall for dependabot template-oss PR
([@&#8203;lukekarrys](https://redirect.github.com/lukekarrys))
-
[`7901052`](79010526af)
[#&#8203;146](https://redirect.github.com/npm/npm-package-arg/pull/146)
bump
[@&#8203;npmcli/template-oss](https://redirect.github.com/npmcli/template-oss)
from 4.18.0 to 4.18.1
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])

###
[`v11.0.1`](https://redirect.github.com/npm/npm-package-arg/blob/HEAD/CHANGELOG.md#1101-2023-09-05)

[Compare
Source](https://redirect.github.com/npm/npm-package-arg/compare/v11.0.0...v11.0.1)

##### Bug Fixes

-
[`74b3c7e`](74b3c7e34a)
[#&#8203;141](https://redirect.github.com/npm/npm-package-arg/pull/141)
use URL instead of url.parse
([#&#8203;141](https://redirect.github.com/npm/npm-package-arg/issues/141))
([@&#8203;wraithgar](https://redirect.github.com/wraithgar))

##### Documentation

-
[`ea00495`](ea00495783)
[#&#8203;142](https://redirect.github.com/npm/npm-package-arg/pull/142)
fix readme typo
([#&#8203;142](https://redirect.github.com/npm/npm-package-arg/issues/142))
([@&#8203;rotu](https://redirect.github.com/rotu))
-
[`26705c5`](26705c5fef)
[#&#8203;143](https://redirect.github.com/npm/npm-package-arg/pull/143)
Fix citations to RFC 8089 (not 8909) for file: url
([#&#8203;143](https://redirect.github.com/npm/npm-package-arg/issues/143))
([@&#8203;rotu](https://redirect.github.com/rotu))

###
[`v11.0.0`](https://redirect.github.com/npm/npm-package-arg/blob/HEAD/CHANGELOG.md#1100-2023-08-15)

[Compare
Source](https://redirect.github.com/npm/npm-package-arg/compare/v10.1.0...v11.0.0)

##### ⚠️ BREAKING CHANGES

- the strict RFC 8089 mode has been removed
- support for node 14 has been removed

##### Bug Fixes

-
[`9344167`](934416709c)
[#&#8203;135](https://redirect.github.com/npm/npm-package-arg/pull/135)
remove strict 8909 mode
([@&#8203;wraithgar](https://redirect.github.com/wraithgar))
-
[`5042ff2`](5042ff2bba)
[#&#8203;139](https://redirect.github.com/npm/npm-package-arg/pull/139)
drop node14 support
([@&#8203;lukekarrys](https://redirect.github.com/lukekarrys))

##### Dependencies

-
[`d2ab7ba`](d2ab7bade1)
[#&#8203;138](https://redirect.github.com/npm/npm-package-arg/pull/138)
bump hosted-git-info from 6.1.1 to 7.0.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cnpm/cnpmcore).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTYuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE1Ni4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-28 23:30:04 +08:00
renovate[bot]
0c987a7225 chore(deps): update dependency oxlint-tsgolint to ^0.3.0 (#863)
[skip ci]

This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [oxlint-tsgolint](https://redirect.github.com/oxc-project/tsgolint) |
[`^0.2.0` ->
`^0.3.0`](https://renovatebot.com/diffs/npm/oxlint-tsgolint/0.2.1/0.3.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/oxlint-tsgolint/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/oxlint-tsgolint/0.2.1/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>oxc-project/tsgolint (oxlint-tsgolint)</summary>

###
[`v0.3.0`](https://redirect.github.com/oxc-project/tsgolint/releases/tag/v0.3.0)

[Compare
Source](https://redirect.github.com/oxc-project/tsgolint/compare/v0.2.1...v0.3.0)

#### What's Changed

- chore(deps): update typescript-go digest to
[`48b739c`](https://redirect.github.com/oxc-project/tsgolint/commit/48b739c)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;311](https://redirect.github.com/oxc-project/tsgolint/pull/311)
- chore(deps): update typescript-go digest to
[`06a7b84`](https://redirect.github.com/oxc-project/tsgolint/commit/06a7b84)
by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;312](https://redirect.github.com/oxc-project/tsgolint/pull/312)
- chore(deps): update vitest to v4 by
[@&#8203;camc314](https://redirect.github.com/camc314) in
[#&#8203;314](https://redirect.github.com/oxc-project/tsgolint/pull/314)
- feat(rules): Implement `strict_boolean_expressions` by
[@&#8203;nnnnoel](https://redirect.github.com/nnnnoel) in
[#&#8203;222](https://redirect.github.com/oxc-project/tsgolint/pull/222)
- fix: resolve panic when compiling wildcard directory patterns and add
tests for non-ASCII characters by
[@&#8203;camc314](https://redirect.github.com/camc314) in
[#&#8203;319](https://redirect.github.com/oxc-project/tsgolint/pull/319)
- fix(strict-boolean-expressions): fix memory leak by
[@&#8203;camc314](https://redirect.github.com/camc314) in
[#&#8203;316](https://redirect.github.com/oxc-project/tsgolint/pull/316)

#### New Contributors

- [@&#8203;nnnnoel](https://redirect.github.com/nnnnoel) made their
first contribution in
[#&#8203;222](https://redirect.github.com/oxc-project/tsgolint/pull/222)

**Full Changelog**:
<https://github.com/oxc-project/tsgolint/compare/v0.2.1...v0.3.0>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cnpm/cnpmcore).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTYuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE1Ni4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-28 23:29:18 +08:00
MK (fengmk2)
89f1250927 refactor: use all in one egg v4 (#855)
required https://github.com/eggjs/egg/pull/5654

---------

Signed-off-by: MK (fengmk2) <fengmk2@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-28 22:17:01 +08:00
renovate[bot]
e1848c71ec chore(deps): update dependency lint-staged to v16 (#853)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [lint-staged](https://redirect.github.com/lint-staged/lint-staged) |
[`^15.5.0` ->
`^16.0.0`](https://renovatebot.com/diffs/npm/lint-staged/15.5.2/16.2.6)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/lint-staged/16.2.6?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/lint-staged/15.5.2/16.2.6?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>lint-staged/lint-staged (lint-staged)</summary>

###
[`v16.2.6`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1626)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v16.2.5...v16.2.6)

##### Patch Changes

-
[#&#8203;1693](https://redirect.github.com/lint-staged/lint-staged/pull/1693)
[`33d4502`](33d4502ef9)
Thanks
[@&#8203;Adrian-Baran-GY](https://redirect.github.com/Adrian-Baran-GY)!
- Fix problems with `--continue-on-error` option, where tasks might have
still been killed (`SIGINT`) when one of them failed.

###
[`v16.2.5`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1625)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v16.2.4...v16.2.5)

##### Patch Changes

-
[#&#8203;1687](https://redirect.github.com/lint-staged/lint-staged/pull/1687)
[`9e02d9d`](9e02d9dc8a)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Fix
unhandled promise rejection when spawning tasks (*instead of the tasks
themselves failing*). Previously when a task failed to spawn,
*lint-staged* also failed and the backup stash might not have been
automatically restored.

###
[`v16.2.4`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1624)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v16.2.3...v16.2.4)

##### Patch Changes

-
[#&#8203;1682](https://redirect.github.com/lint-staged/lint-staged/pull/1682)
[`0176038`](01760380e5)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Update
dependencies, including
[`nano-spawn@2.0.0`](https://redirect.github.com/sindresorhus/nano-spawn/releases/tag/v2.0.0)
with bug fixes.

-
[#&#8203;1671](https://redirect.github.com/lint-staged/lint-staged/pull/1671)
[`581a54e`](581a54eea6)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Speed up
execution by only importing the `yaml` depedency if using YAML
configuration files.

###
[`v16.2.3`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1623)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v16.2.2...v16.2.3)

##### Patch Changes

-
[#&#8203;1669](https://redirect.github.com/lint-staged/lint-staged/pull/1669)
[`27cd541`](27cd5413d9)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - When using
`--fail-on-changes`, automatically hidden (partially) unstaged changes
are no longer counted to make *lint-staged* fail.

###
[`v16.2.2`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1622)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v16.2.1...v16.2.2)

##### Patch Changes

-
[#&#8203;1667](https://redirect.github.com/lint-staged/lint-staged/pull/1667)
[`699f95d`](699f95df8f)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - The backup
stash will not be dropped when using `--fail-on-changes` and there are
errors. When reverting to original state is disabled (via `--no-revert`
or `--fail-on-changes`), hidden (partially) unstaged changes are still
restored automatically so that it's easier to resolve the situation
manually.

Additionally, the example for using the backup stash manually now uses
the correct backup hash, if available:

  ```shell
  % npx lint-staged --fail-on-changes
  ✔ Backed up original state in git stash (c18d55a3)
  ✔ Running tasks for staged files...
  ✖ Tasks modified files and --fail-on-changes was used!
  ↓ Cleaning up temporary files...

  ✖ lint-staged failed because `--fail-on-changes` was used.

  Any lost modifications can be restored from a git stash:

    > git stash list --format="%h %s"
    c18d55a3 On main: lint-staged automatic backup
    > git apply --index c18d55a3
  ```

###
[`v16.2.1`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1621)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v16.2.0...v16.2.1)

##### Patch Changes

-
[#&#8203;1664](https://redirect.github.com/lint-staged/lint-staged/pull/1664)
[`8277b3b`](8277b3b298)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - The
built-in TypeScript types have been updated to more closely match the
implementation. Notably, the list of staged files supplied to task
functions is `readonly string[]` and can't be mutated. Thanks
[@&#8203;outslept](https://redirect.github.com/outslept)!

  ```diff
  export default {
  ---  "*": (files: string[]) => void console.log('staged files', files)
+++ "*": (files: readonly string[]) => void console.log('staged files',
files)
  }
  ```

-
[#&#8203;1654](https://redirect.github.com/lint-staged/lint-staged/pull/1654)
[`70b9af3`](70b9af3ac3)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - This
version has been published from GitHub Actions using [Trusted Publishing
for npm packages](https://docs.npmjs.com/trusted-publishers).

-
[#&#8203;1659](https://redirect.github.com/lint-staged/lint-staged/pull/1659)
[`4996817`](49968170ab)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Fix
searching configuration files when the working directory is a
subdirectory of a git repository, and there are `package.json` files in
the working directory. This situation might happen when running
*lint-staged* for a single package in a monorepo.

-
[#&#8203;1654](https://redirect.github.com/lint-staged/lint-staged/pull/1654)
[`7021f0a`](7021f0af40)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Return the
caret semver range (`^`) to direct dependencies so that future patch and
minor versions are allowed. This enables projects to better maintain and
deduplicate their own transitive dependencies while not requiring direct
updates to *lint-staged*. This was changed in
[16.2.0](https://redirect.github.com/lint-staged/lint-staged/releases/tag/v16.2.0)
after the vulnerability issues with `chalk` and `debug`, which were also
removed in the same version.

Given the recent vulnerabilities in the *npm* ecosystem, it's best to be
very careful when updating dependencies.

###
[`v16.2.0`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1620)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v16.1.6...v16.2.0)

##### Minor Changes

-
[#&#8203;1615](https://redirect.github.com/lint-staged/lint-staged/pull/1615)
[`99eb742`](99eb74200e)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Added a new
option `--fail-on-changes` to make *lint-staged* exit with code 1 when
tasks modify any files, making the `precommit` hook fail. This is
similar to the `git diff --exit-code` option. Using this flag also
implies the `--no-revert` flag which means any changes made by tasks
will be left in the working tree after failing, so that they can be
manually staged and the commit tried again.

-
[#&#8203;1611](https://redirect.github.com/lint-staged/lint-staged/pull/1611)
[`cd05fd3`](cd05fd3495)
Thanks [@&#8203;rlorenzo](https://redirect.github.com/rlorenzo)! - Added
a new option `--continue-on-error` so that *lint-staged* will run all
tasks to completion even if some of them fail. By default, *lint-staded*
will exit early on the first failure.

-
[#&#8203;1637](https://redirect.github.com/lint-staged/lint-staged/pull/1637)
[`82fcc07`](82fcc0789c)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Internal
*lint-staged* errors are now thrown and visible in the console output.
Previously they were caught with the process exit code set to 1, but not
logged. This happens when, for example, there's a syntax error in the
*lint-staged* configuration file.

-
[#&#8203;1647](https://redirect.github.com/lint-staged/lint-staged/pull/1647)
[`a5ecc06`](a5ecc0605d)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Remove
[debug](https://redirect.github.com/debug-js/debug) as a dependency due
to recent malware issue; read more at
[debug-js/debug#1005](https://redirect.github.com/debug-js/debug/issues/1005).
Because of this, the `DEBUG` environment variable is no longer supported
— use the `--debug` to enable debugging

-
[#&#8203;1636](https://redirect.github.com/lint-staged/lint-staged/pull/1636)
[`8db2717`](8db2717574)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Added a new
option `--hide-unstaged` so that *lint-staged* will hide all unstaged
changes to tracked files before running tasks. The changes will be
applied back after running the tasks. Note that the combination of flags
`--hide-unstaged --no-hide-partially-staged` isn't meaningful and
behaves the same as just `--hide-unstaged`.

Thanks to
[@&#8203;ItsNickBarry](https://redirect.github.com/ItsNickBarry) for the
idea and initial implementation in
[#&#8203;1552](https://redirect.github.com/lint-staged/lint-staged/pull/1552).

-
[#&#8203;1648](https://redirect.github.com/lint-staged/lint-staged/pull/1648)
[`7900b3b`](7900b3b79c)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Remove
[lilconfig](https://redirect.github.com/antonk52/lilconfig) to reduce
reliance on third-party dependencies. It was used to find possible
config files outside of those tracked in Git, including from the parent
directories. This behavior has been moved directly into *lint-staged*
and should work about the same.

##### Patch Changes

-
[#&#8203;1633](https://redirect.github.com/lint-staged/lint-staged/pull/1633)
[`7f9e485`](7f9e485a98)
Thanks
[@&#8203;dependabot](https://redirect.github.com/apps/dependabot)! -
Bumps [listr2](https://redirect.github.com/listr2/listr2) from 9.0.3 to
9.0.4.

-
[#&#8203;1626](https://redirect.github.com/lint-staged/lint-staged/pull/1626)
[`99d5a9b`](99d5a9b0dd)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Due to
recent phishing attacks, for example
[chalk@5.6.1](https://redirect.github.com/chalk/chalk/issues/656) was
released with malware. To avoid *lint-staged*'s users being at risk the
**direct dependencies are pinned to exact versions**, instead of
allowing future patch versions with the [caret (`^`)
range](https://docs.npmjs.com/cli/v6/using-npm/semver#caret-ranges-123-025-004).

-
[#&#8203;1588](https://redirect.github.com/lint-staged/lint-staged/pull/1588)
[`035bbf2`](035bbf268a)
Thanks [@&#8203;outslept](https://redirect.github.com/outslept)! -
Increase performance by listing staged files and searching for
configuration concurrently.

-
[#&#8203;1645](https://redirect.github.com/lint-staged/lint-staged/pull/1645)
[`deba3ad`](deba3ad835)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Remove
[chalk](https://redirect.github.com/chalk/chalk) as a dependency due to
recent malware issue; read more at
[chalk/chalk#656](https://redirect.github.com/chalk/chalk/issues/656).

If you are having trouble with ANSI color codes when using
*lint-staged*, you can try setting either `FORCE_COLOR=true` or
`NO_COLOR=true` env variables.

###
[`v16.1.6`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1616)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v16.1.5...v16.1.6)

##### Patch Changes

-
[#&#8203;1610](https://redirect.github.com/lint-staged/lint-staged/pull/1610)
[`e93578e`](e93578e265)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Try to
improve terminating of subprocess of tasks by using `SIGKILL`, and only
calling `pidtree` when the the main task process has a known pid.

###
[`v16.1.5`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1615)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v16.1.4...v16.1.5)

##### Patch Changes

-
[#&#8203;1608](https://redirect.github.com/lint-staged/lint-staged/pull/1608)
[`4e3ce22`](4e3ce225b3)
Thanks [@&#8203;srsatt](https://redirect.github.com/srsatt)! - Detect
the git repo's top-level directory correctly when in a worktree.

###
[`v16.1.4`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1614)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v16.1.3...v16.1.4)

##### Patch Changes

-
[#&#8203;1604](https://redirect.github.com/lint-staged/lint-staged/pull/1604)
[`90b37b0`](90b37b00c2)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Add another
`types` field to `package.json` to make even more sure NPM detects that
*lint-staged* includes built-in TypeScript type definitions.

###
[`v16.1.3`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1613)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v16.1.2...v16.1.3)

##### Patch Changes

-
[#&#8203;1602](https://redirect.github.com/lint-staged/lint-staged/pull/1602)
[`7ea700b`](7ea700bcf3)
Thanks [@&#8203;dword-design](https://redirect.github.com/dword-design)!
- Add the `types` field to `package.json` to make sure NPM detects
*lint-staged* includes built-in TypeScript type definitions.

###
[`v16.1.2`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1612)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v16.1.1...v16.1.2)

##### Patch Changes

-
[#&#8203;1570](https://redirect.github.com/lint-staged/lint-staged/pull/1570)
[`a7c0c88`](a7c0c88bcf)
Thanks [@&#8203;ItsNickBarry](https://redirect.github.com/ItsNickBarry)!
- When using `--diff-filter` with the `D` option to include deleted
staged files, *lint-staged* no longer tries to stage the deleted files,
unless they're no longer deleted. Previously this caused an error from
`git add` like `fatal: pathspec 'deleted-file' did not match any files`.

-
[`38f942e`](38f942ecc4)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Removed an
extraneous log entry that printed `shouldHidePArtiallyStagedFiles` to
console output.

###
[`v16.1.1`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1611)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v16.1.0...v16.1.1)

##### Patch Changes

-
[#&#8203;1565](https://redirect.github.com/lint-staged/lint-staged/pull/1565)
[`3686977`](3686977ccd)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! -
*Lint-staged* now explicitly warns about potential data loss when using
`--no-stash`.

-
[#&#8203;1571](https://redirect.github.com/lint-staged/lint-staged/pull/1571)
[`02299a9`](02299a9e4f)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Function
tasks (introduced in v16.0.0) only receive the staged files matching the
configured glob, instead of all staged files.

-
[#&#8203;1563](https://redirect.github.com/lint-staged/lint-staged/pull/1563)
[`bc61c74`](bc61c74383)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - This
version fixes incorrect behavior where unstaged changes were committed
when using the `--no-stash` option. This happened because `--no-stash`
implied `--no-hide-partially-staged`, meaning unstaged changes to files
which also had other staged changes were added to the commit by
*lint-staged*; this is no longer the case.

The previous (incorrect) behavior can still be achieved by using both
options `--no-stash --no-hide-partially-staged` at the same time.

###
[`v16.1.0`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1610)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v16.0.0...v16.1.0)

##### Minor Changes

-
[#&#8203;1536](https://redirect.github.com/lint-staged/lint-staged/pull/1536)
[`e729daa`](e729daa3b3)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - A new flag
`--no-revert` has been introduced for when task modifications should be
applied to the index before aborting the commit in case of errors. By
default, *lint-staged* will clear all task modifications and revert to
the original state.

-
[#&#8203;1550](https://redirect.github.com/lint-staged/lint-staged/pull/1550)
[`b27fa3f`](b27fa3fecb)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! -
*Lint-staged* now ignores symlinks and leaves them out from the list of
staged files.

##### Patch Changes

-
[#&#8203;1558](https://redirect.github.com/lint-staged/lint-staged/pull/1558)
[`c37dc38`](c37dc38ddd)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - The minimum
required Node.js version is lowered to `20.17` following
[nano-spawn@1.0.2](https://redirect.github.com/sindresorhus/nano-spawn/releases/tag/v1.0.2).

###
[`v16.0.0`](https://redirect.github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1600)

[Compare
Source](https://redirect.github.com/lint-staged/lint-staged/compare/v15.5.2...v16.0.0)

##### Major Changes

-
[#&#8203;1546](https://redirect.github.com/lint-staged/lint-staged/pull/1546)
[`158d15c`](158d15c9ae)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - Processes
are spawned using
[nano-spawn](https://redirect.github.com/sindresorhus/nano-spawn)
instead of [execa](https://redirect.github.com/sindresorhus/execa). If
you are using Node.js scripts as tasks, you might need to explicitly run
them with `node`, especially when using Windows:

  ```json
  {
    "*.js": "node my-js-linter.js"
  }
  ```

-
[#&#8203;1546](https://redirect.github.com/lint-staged/lint-staged/pull/1546)
[`158d15c`](158d15c9ae)
Thanks [@&#8203;iiroj](https://redirect.github.com/iiroj)! - The
`--shell` flag has been removed and *lint-staged* no longer supports
evaluating commands directly via a shell. To migrate existing commands,
you can create a shell script and invoke it instead. Lint-staged will
pass matched staged files as a list of arguments, accessible via
`"$@&#8203;"`:

  ```shell
  ```

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cnpm/cnpmcore).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTYuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE1Ni4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 17:43:27 +00:00
renovate[bot]
e8a3ee5208 chore(deps): update actions/checkout action to v5 (#849)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/checkout](https://redirect.github.com/actions/checkout) |
action | major | `v4` -> `v5` |

---

### Release Notes

<details>
<summary>actions/checkout (actions/checkout)</summary>

### [`v5`](https://redirect.github.com/actions/checkout/compare/v4...v5)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v4...v5)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cnpm/cnpmcore).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTYuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE1Ni4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 17:09:52 +00:00
renovate[bot]
3671c52513 chore(deps): update actions/setup-node action to v6 (#851)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/setup-node](https://redirect.github.com/actions/setup-node) |
action | major | `v4` -> `v6` |

---

### Release Notes

<details>
<summary>actions/setup-node (actions/setup-node)</summary>

###
[`v6`](https://redirect.github.com/actions/setup-node/compare/v5...v6)

[Compare
Source](https://redirect.github.com/actions/setup-node/compare/v5...v6)

###
[`v5`](https://redirect.github.com/actions/setup-node/compare/v4...v5)

[Compare
Source](https://redirect.github.com/actions/setup-node/compare/v4...v5)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cnpm/cnpmcore).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTYuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE1Ni4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 17:09:10 +00:00
renovate[bot]
35a7be34e8 chore(deps): update actions/first-interaction action to v3 (#850)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[actions/first-interaction](https://redirect.github.com/actions/first-interaction)
| action | major | `v1` -> `v3` |

---

### Release Notes

<details>
<summary>actions/first-interaction (actions/first-interaction)</summary>

###
[`v3`](https://redirect.github.com/actions/first-interaction/compare/v2...v3)

[Compare
Source](https://redirect.github.com/actions/first-interaction/compare/v2...v3)

###
[`v2`](https://redirect.github.com/actions/first-interaction/compare/v1...v2)

[Compare
Source](https://redirect.github.com/actions/first-interaction/compare/v1...v2)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cnpm/cnpmcore).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTYuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE1Ni4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 23:12:06 +08:00
renovate[bot]
957f43dab1 chore(deps): update actions/attest-build-provenance action to v3 (#847)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[actions/attest-build-provenance](https://redirect.github.com/actions/attest-build-provenance)
| action | major | `v2` -> `v3` |

---

### Release Notes

<details>
<summary>actions/attest-build-provenance
(actions/attest-build-provenance)</summary>

###
[`v3`](https://redirect.github.com/actions/attest-build-provenance/compare/v2...v3)

[Compare
Source](https://redirect.github.com/actions/attest-build-provenance/compare/v2...v3)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cnpm/cnpmcore).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTYuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE1Ni4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 23:10:32 +08:00
renovate[bot]
4213bd8823 chore(deps): update dependency mocha to v11.7.4 (#846)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [mocha](https://mochajs.org/)
([source](https://redirect.github.com/mochajs/mocha)) | [`11.6.0` ->
`11.7.4`](https://renovatebot.com/diffs/npm/mocha/11.6.0/11.7.4) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/mocha/11.7.4?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/mocha/11.6.0/11.7.4?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>mochajs/mocha (mocha)</summary>

###
[`v11.7.4`](https://redirect.github.com/mochajs/mocha/blob/HEAD/CHANGELOG.md#1174-2025-10-01)

[Compare
Source](https://redirect.github.com/mochajs/mocha/compare/v11.7.3...v11.7.4)

##### 🩹 Fixes

- watch mode using chokidar v4
([#&#8203;5379](https://redirect.github.com/mochajs/mocha/issues/5379))
([c2667c3](c2667c3b3f))

##### 📚 Documentation

- migrate remaining legacy wiki pages to main documentation
([#&#8203;5465](https://redirect.github.com/mochajs/mocha/issues/5465))
([bff9166](bff9166073))

##### 🧹 Chores

- remove trailing spaces
([#&#8203;5475](https://redirect.github.com/mochajs/mocha/issues/5475))
([7f68e5c](7f68e5c156))

###
[`v11.7.3`](https://redirect.github.com/mochajs/mocha/blob/HEAD/CHANGELOG.md#1173-2025-09-30)

[Compare
Source](https://redirect.github.com/mochajs/mocha/compare/v11.7.2...v11.7.3)

##### 🩹 Fixes

- use original require() error for TS files if
ERR\_UNKNOWN\_FILE\_EXTENSION
([#&#8203;5408](https://redirect.github.com/mochajs/mocha/issues/5408))
([ebdbc48](ebdbc48769))

##### 📚 Documentation

- add security escalation policy
([#&#8203;5466](https://redirect.github.com/mochajs/mocha/issues/5466))
([4122c7d](4122c7d13d))
- fix duplicate global leak documentation
([#&#8203;5461](https://redirect.github.com/mochajs/mocha/issues/5461))
([1164b9d](1164b9da89))
- migrate third party UIs wiki page to docs
([#&#8203;5434](https://redirect.github.com/mochajs/mocha/issues/5434))
([6654704](66547045cb))
- update maintainer release notes for release-please
([#&#8203;5453](https://redirect.github.com/mochajs/mocha/issues/5453))
([185ae1e](185ae1eabe))

##### 🤖 Automation

- **deps:** bump actions/setup-node in the github-actions group
([#&#8203;5459](https://redirect.github.com/mochajs/mocha/issues/5459))
([48c6f40](48c6f4068b))

###
[`v11.7.2`](https://redirect.github.com/mochajs/mocha/blob/HEAD/CHANGELOG.md#1172-2025-09-01)

[Compare
Source](https://redirect.github.com/mochajs/mocha/compare/v11.7.1...v11.7.2)

##### 🩹 Fixes

- fail with an informative error message on a file with a broken default
import
([#&#8203;5413](https://redirect.github.com/mochajs/mocha/issues/5413))
([b0e6135](b0e6135059))
- load mjs files correctly
([#&#8203;5429](https://redirect.github.com/mochajs/mocha/issues/5429))
([a947b9b](a947b9b955))

##### 📚 Documentation

- add banner from old site to new site, link from new to old
([#&#8203;5414](https://redirect.github.com/mochajs/mocha/issues/5414))
([dedef11](dedef110a2))
- add info on spies to legacy docs
([#&#8203;5421](https://redirect.github.com/mochajs/mocha/issues/5421))
([21f5544](21f554459c))
- explain node import swallowing error
([#&#8203;5401](https://redirect.github.com/mochajs/mocha/issues/5401))
([09f5b2c](09f5b2c9de))
- fix links in new site
([#&#8203;5416](https://redirect.github.com/mochajs/mocha/issues/5416))
([b2bc769](b2bc769c6c))
- migrate assertion libraries wiki link to main docs
([#&#8203;5442](https://redirect.github.com/mochajs/mocha/issues/5442))
([95f3ca8](95f3ca8bc3))
- migrate count assertions wiki page to docs
([#&#8203;5438](https://redirect.github.com/mochajs/mocha/issues/5438))
([02a306c](02a306c6cb))
- migrate shared behaviours to docs-next
([#&#8203;5432](https://redirect.github.com/mochajs/mocha/issues/5432))
([1dc4aa9](1dc4aa98eb))
- migrate Spies wiki page to explainers
([#&#8203;5420](https://redirect.github.com/mochajs/mocha/issues/5420))
([cbcf007](cbcf007c5a))
- Migrate tagging wiki page to docs
([#&#8203;5435](https://redirect.github.com/mochajs/mocha/issues/5435))
([876247a](876247a8a6))
- migrate third party reporters wiki page to docs
([#&#8203;5433](https://redirect.github.com/mochajs/mocha/issues/5433))
([f70764c](f70764c9a5))
- migrate to global leak wiki page to docs
([#&#8203;5437](https://redirect.github.com/mochajs/mocha/issues/5437))
([8a6fdca](8a6fdcafcc))
- update /next bug report link to be docs issue template
([#&#8203;5424](https://redirect.github.com/mochajs/mocha/issues/5424))
([668cb66](668cb66e12))

##### 🧹 Chores

- add issue form for ️ Performance
([#&#8203;5406](https://redirect.github.com/mochajs/mocha/issues/5406))
([a908b3b](a908b3b866))
- add test for `-R import-only-loader`
([#&#8203;5391](https://redirect.github.com/mochajs/mocha/issues/5391))
([6ee5b48](6ee5b483b8))
- also test Node.js 24 in CI
([#&#8203;5405](https://redirect.github.com/mochajs/mocha/issues/5405))
([15f5980](15f5980528))
- bump CI to use 20.19.4, 22.18.0, 24.6.0
([#&#8203;5430](https://redirect.github.com/mochajs/mocha/issues/5430))
([ace5eb4](ace5eb47a7))
- bump Knip to 5.61.2
([#&#8203;5394](https://redirect.github.com/mochajs/mocha/issues/5394))
([f3d7430](f3d743061d))
- cleanup references of --opts
([#&#8203;5402](https://redirect.github.com/mochajs/mocha/issues/5402))
([1096b37](1096b376c3))
- enabled ESLint's no-unused-vars
([#&#8203;5399](https://redirect.github.com/mochajs/mocha/issues/5399))
([d4168ae](d4168aef4c))
- move callback and object typedefs to a new types.d.ts
([#&#8203;5351](https://redirect.github.com/mochajs/mocha/issues/5351))
([3300d21](3300d2155a))
- rewrite base path instead of copy-pasting
([#&#8203;5431](https://redirect.github.com/mochajs/mocha/issues/5431))
([c6c6740](c6c6740fb4))
- unify caught errors as err
([#&#8203;5439](https://redirect.github.com/mochajs/mocha/issues/5439))
([d4912e7](d4912e705c))
- Update experimental module detection test and pin exact Node versions
([#&#8203;5417](https://redirect.github.com/mochajs/mocha/issues/5417))
([2489090](2489090223))

##### 🤖 Automation

- **deps:** bump actions/checkout in the github-actions group
([#&#8203;5419](https://redirect.github.com/mochajs/mocha/issues/5419))
([03ac2d0](03ac2d0e6e))

###
[`v11.7.1`](https://redirect.github.com/mochajs/mocha/blob/HEAD/CHANGELOG.md#1171-2025-06-24)

[Compare
Source](https://redirect.github.com/mochajs/mocha/compare/v11.7.0...v11.7.1)

##### 🩹 Fixes

- always fallback to import() if require() fails
([#&#8203;5384](https://redirect.github.com/mochajs/mocha/issues/5384))
([295c168](295c168628))

##### 🧹 Chores

- add esm loader test
([#&#8203;5383](https://redirect.github.com/mochajs/mocha/issues/5383))
([f58e49f](f58e49f08d))

###
[`v11.7.0`](https://redirect.github.com/mochajs/mocha/blob/HEAD/CHANGELOG.md#1170-2025-06-18)

[Compare
Source](https://redirect.github.com/mochajs/mocha/compare/v11.6.0...v11.7.0)

##### 🌟 Features

- use require to load esm
([#&#8203;5366](https://redirect.github.com/mochajs/mocha/issues/5366))
([41e24a2](41e24a2429))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these
updates again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cnpm/cnpmcore).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTYuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE1Ni4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 23:09:20 +08:00
renovate[bot]
f3f3584d62 chore: Configure Renovate (#845)
Welcome to [Renovate](https://redirect.github.com/renovatebot/renovate)!
This is an onboarding PR to help you understand and configure settings
before regular Pull Requests begin.

🚦 To activate Renovate, merge this Pull Request. To disable Renovate,
simply close this Pull Request unmerged.



---
### Detected Package Files

 * `docker-compose-es.yml` (docker-compose)
 * `docker-compose-postgres.yml` (docker-compose)
 * `docker-compose.yml` (docker-compose)
 * `.docker/alpine/Dockerfile` (dockerfile)
 * `.docker/debian/Dockerfile` (dockerfile)
 * `.github/workflows/greetings.yml` (github-actions)
 * `.github/workflows/nodejs.yml` (github-actions)
 * `.github/workflows/release-image.yml` (github-actions)
 * `.github/workflows/release.yml` (github-actions)
 * `app/common/package.json` (npm)
 * `app/core/package.json` (npm)
 * `app/infra/package.json` (npm)
 * `app/port/package.json` (npm)
 * `app/repository/package.json` (npm)
 * `package.json` (npm)

### Configuration Summary

Based on the default config's presets, Renovate will:

  - Start dependency updates only once this onboarding PR is merged
  - Hopefully safe environment variables to allow users to configure.
  - Show all Merge Confidence badges for pull requests.
  - Enable Renovate Dependency Dashboard creation.
- Use semantic commit type `fix` for dependencies and `chore` for all
others if semantic commits are in use.
- Ignore `node_modules`, `bower_components`, `vendor` and various
test/tests (except for nuget) directories.
  - Group known monorepo packages together.
  - Use curated list of recommended non-monorepo package groupings.
- Show only the Age and Confidence Merge Confidence badges for pull
requests.
  - Apply crowd-sourced package replacement rules.
  - Apply crowd-sourced workarounds for known problems with packages.

🔡 Do you want to change how Renovate upgrades your dependencies? Add
your custom config to `renovate.json` in this branch. Renovate will
update the Pull Request description the next time it runs.

---

### What to Expect

With your current configuration, Renovate will create 21 Pull Requests:

<details>
<summary>chore(deps): update dependency mocha to v11.7.4</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/mocha-11.x`
  - Merge into: `master`
- Upgrade [mocha](https://redirect.github.com/mochajs/mocha) to `11.7.4`


</details>

<details>
<summary>chore(deps): update actions/attest-build-provenance action to
v3</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/actions-attest-build-provenance-3.x`
  - Merge into: `master`
- Upgrade
[actions/attest-build-provenance](https://redirect.github.com/actions/attest-build-provenance)
to `v3`


</details>

<details>
<summary>chore(deps): update actions/checkout action to v5</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/actions-checkout-5.x`
  - Merge into: `master`
- Upgrade
[actions/checkout](https://redirect.github.com/actions/checkout) to `v5`


</details>

<details>
<summary>chore(deps): update actions/first-interaction action to
v3</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/actions-first-interaction-3.x`
  - Merge into: `master`
- Upgrade
[actions/first-interaction](https://redirect.github.com/actions/first-interaction)
to `v3`


</details>

<details>
<summary>chore(deps): update actions/setup-node action to v6</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/actions-setup-node-6.x`
  - Merge into: `master`
- Upgrade
[actions/setup-node](https://redirect.github.com/actions/setup-node) to
`v6`


</details>

<details>
<summary>chore(deps): update dependency
@&#8203;simplewebauthn/typescript-types to v8</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/simplewebauthn-typescript-types-8.x`
  - Merge into: `master`
- Upgrade
[@simplewebauthn/typescript-types](https://redirect.github.com/MasterKale/SimpleWebAuthn)
to `^8.0.0`


</details>

<details>
<summary>chore(deps): update dependency lint-staged to v16</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/lint-staged-16.x`
  - Merge into: `master`
- Upgrade
[lint-staged](https://redirect.github.com/lint-staged/lint-staged) to
`^16.0.0`


</details>

<details>
<summary>chore(deps): update mysql docker tag to v9</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/mysql-9.x`
  - Merge into: `master`
  - Upgrade mysql to `9.5`


</details>

<details>
<summary>chore(deps): update postgres docker tag to v18</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/postgres-18.x`
  - Merge into: `master`
  - Upgrade postgres to `18`


</details>

<details>
<summary>chore(deps): update redis docker tag to v8</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/redis-8.x`
  - Merge into: `master`
  - Upgrade redis to `8-alpine`


</details>

<details>
<summary>fix(deps): update dependency @&#8203;elastic/elasticsearch to
v9</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/elastic-elasticsearch-9.x`
  - Merge into: `master`
- Upgrade
[@elastic/elasticsearch](https://redirect.github.com/elastic/elasticsearch-js)
to `^9.0.0`


</details>

<details>
<summary>fix(deps): update dependency @&#8203;simplewebauthn/server to
v13</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/simplewebauthn-server-13.x`
  - Merge into: `master`
- Upgrade
[@simplewebauthn/server](https://redirect.github.com/MasterKale/SimpleWebAuthn)
to `^13.0.0`


</details>

<details>
<summary>fix(deps): update dependency base-x to v5</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/base-x-5.x`
  - Merge into: `master`
- Upgrade [base-x](https://redirect.github.com/cryptocoinjs/base-x) to
`^5.0.0`


</details>

<details>
<summary>fix(deps): update dependency mime-types to v3</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/mime-types-3.x`
  - Merge into: `master`
- Upgrade [mime-types](https://redirect.github.com/jshttp/mime-types) to
`^3.0.0`
- Upgrade
[@types/mime-types](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped)
to `^3.0.0`


</details>

<details>
<summary>fix(deps): update dependency npm-package-arg to v13</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/npm-package-arg-13.x`
  - Merge into: `master`
- Upgrade
[npm-package-arg](https://redirect.github.com/npm/npm-package-arg) to
`^13.0.0`


</details>

<details>
<summary>fix(deps): update dependency p-map to v7</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/p-map-7.x`
  - Merge into: `master`
- Upgrade [p-map](https://redirect.github.com/sindresorhus/p-map) to
`^7.0.0`


</details>

<details>
<summary>fix(deps): update dependency read-env-value to v2</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/read-env-value-2.x`
  - Merge into: `master`
- Upgrade
[read-env-value](https://redirect.github.com/node-modules/read-env-value)
to `^2.0.0`


</details>

<details>
<summary>fix(deps): update dependency ssri to v13</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/ssri-13.x`
  - Merge into: `master`
  - Upgrade [ssri](https://redirect.github.com/npm/ssri) to `^13.0.0`


</details>

<details>
<summary>fix(deps): update dependency type-fest to v5</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/type-fest-5.x`
  - Merge into: `master`
- Upgrade
[type-fest](https://redirect.github.com/sindresorhus/type-fest) to
`^5.0.0`


</details>

<details>
<summary>fix(deps): update dependency ua-parser-js to v2</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/ua-parser-js-2.x`
  - Merge into: `master`
- Upgrade
[ua-parser-js](https://redirect.github.com/faisalman/ua-parser-js) to
`^2.0.0`


</details>

<details>
<summary>fix(deps): update dependency validate-npm-package-name to
v7</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/validate-npm-package-name-7.x`
  - Merge into: `master`
- Upgrade
[validate-npm-package-name](https://redirect.github.com/npm/validate-npm-package-name)
to `^7.0.0`


</details>



🚸 Branch creation will be limited to maximum 2 per hour, so it doesn't
swamp any CI resources or overwhelm the project. See docs for
`prhourlylimit` for details.


---

 Got questions? Check out Renovate's
[Docs](https://docs.renovatebot.com/), particularly the Getting Started
section.
If you need any further assistance then you can also [request help
here](https://redirect.github.com/renovatebot/renovate/discussions).


---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cnpm/cnpmcore).


<!--renovate-config-hash:e80b4e42a3043bc12fa0640db4bac392d2bf770acf841360d7c8ceeeac2ec1a9-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 04:54:57 +00:00
semantic-release-bot
80663505cb Release 4.11.1
[skip ci]

## <small>4.11.1 (2025-10-22)</small>

* fix: improve TypeScript type definitions across codebase (#844) ([e5162f2](https://github.com/cnpm/cnpmcore/commit/e5162f2)), closes [#844](https://github.com/cnpm/cnpmcore/issues/844)
2025-10-22 17:01:17 +00:00
MK (fengmk2)
e5162f20aa fix: improve TypeScript type definitions across codebase (#844)
This commit enhances type safety and fixes type-related issues
throughout the project including:
- Updated type definitions in entities, repositories, and models
- Improved type annotations in services and controllers
- Fixed type issues in adapters and utilities
- Enhanced test file type definitions
- Added typings/index.d.ts for global type declarations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-23 00:58:59 +08:00
semantic-release-bot
b6c96defa4 Release 4.11.0
[skip ci]

## 4.11.0 (2025-10-15)

* feat: perpage config (#843) ([3e1dbd8](https://github.com/cnpm/cnpmcore/commit/3e1dbd8)), closes [#843](https://github.com/cnpm/cnpmcore/issues/843)
2025-10-15 16:02:23 +00:00
elrrrrrrr
3e1dbd819c feat: perpage config (#843) 2025-10-16 00:00:21 +08:00
semantic-release-bot
faee3b96f1 Release 4.10.0
[skip ci]

## 4.10.0 (2025-10-15)

* feat: Add time field to abbreviated metadata for pnpm time-based resolution (#834) ([2e51399](https://github.com/cnpm/cnpmcore/commit/2e51399)), closes [#834](https://github.com/cnpm/cnpmcore/issues/834)
* feat: mirror microsoft/ripgrep-prebuilt binary (#842) ([606c983](https://github.com/cnpm/cnpmcore/commit/606c983)), closes [#842](https://github.com/cnpm/cnpmcore/issues/842) [cnpm/cnpmcore#841](https://github.com/cnpm/cnpmcore/issues/841)
* docs: Add comprehensive internal API documentation for direct HTTP requests (#832) ([5b1da74](https://github.com/cnpm/cnpmcore/commit/5b1da74)), closes [#832](https://github.com/cnpm/cnpmcore/issues/832) [#709](https://github.com/cnpm/cnpmcore/issues/709)
* docs: Enhance Copilot instructions with DDD architecture [skip ci] (#838) ([cb93141](https://github.com/cnpm/cnpmcore/commit/cb93141)), closes [#838](https://github.com/cnpm/cnpmcore/issues/838) [cnpm/cnpmcore#837](https://github.com/cnpm/cnpmcore/issues/837)
* test: enable test shard (#839) ([324511d](https://github.com/cnpm/cnpmcore/commit/324511d)), closes [#839](https://github.com/cnpm/cnpmcore/issues/839)
* refactor: use tegg and egg v4 beta (#836) ([c7df471](https://github.com/cnpm/cnpmcore/commit/c7df471)), closes [#836](https://github.com/cnpm/cnpmcore/issues/836)
2025-10-15 08:15:50 +00:00
Copilot
606c983363 feat: mirror microsoft/ripgrep-prebuilt binary (#842)
- [x] Add `ripgrep-prebuilt` configuration to `config/binaries.ts`
- [x] Create test file for `ripgrep-prebuilt` binary
- [x] Create test fixture data for releases
- [x] Run and validate tests
- [x] Verify linting passes
- [x] Address code review feedback - use assert.equal instead of
assert.ok

<!-- START COPILOT CODING AGENT SUFFIX -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>mirror microsoft/ripgrep-prebuilt</issue_title>
>
<issue_description>https://github.com/microsoft/ripgrep-prebuilt/releases
> 
> part of
https://github.com/cnpm/binary-mirror-config/issues/57</issue_description>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> </comments>
> 


</details>

Fixes cnpm/cnpmcore#841

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 Share your feedback on Copilot coding agent for the chance to win a
$200 gift card! Click
[here](https://survey3.medallia.com/?EAHeSx-AP01bZqG0Ld9QLQ) to start
the survey.

---------

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@gmail.com>
2025-10-15 08:10:04 +00:00
Copilot
cb931417c1 docs: Enhance Copilot instructions with DDD architecture [skip ci] (#838)
## Overview

This PR significantly enhances the `.github/copilot-instructions.md`
file to provide comprehensive guidance for GitHub Copilot when assisting
with cnpmcore development. The instructions have been expanded from 281
lines to 564 lines, adding critical sections that align with GitHub's
best practices for coding agents.

## What Changed

### New Sections Added

**1. Code Style and Conventions**
- Detailed Oxlint and Prettier configuration rules
- TypeScript conventions (strict typing, avoiding `any`, ES modules)
- Testing conventions with naming patterns and mock usage
- Complete code examples for test structure

**2. Domain-Driven Design (DDD) Architecture**
- Visual layer architecture showing dependency flow
- Detailed responsibilities for each layer:
  - Controller: HTTP interface, validation, authentication
  - Service: Business logic orchestration
  - Repository: Data access and persistence
  - Entity: Domain models with business behavior
  - Model: ORM definitions
- Repository method naming conventions (`findX`, `saveX`, `removeX`,
`listXs`)
- Request validation trilogy workflow (params → auth → authorization)
- Database model modification guidelines (update all 3 locations)

**3. Infrastructure Adapters**
- Documentation of enterprise customization points
- Adapter types: NFSClientAdapter, QueueAdapter, AuthAdapter,
BinaryAdapter

**4. Semantic Commit Messages**
- Conventional commit format standards
- Real-world examples for feat, fix, docs, chore, test, refactor, perf

### Sections Enhanced

**Adding New Features** (7 lines → 69 lines)
- Step-by-step bottom-up implementation workflow
- Layer-by-layer implementation guidance
- Complete TypeScript controller code example with decorators:
  ```typescript
  @HTTPController()
  export class YourController extends AbstractController {
    @Inject()
    private readonly yourService: YourService;

    @HTTPMethod({ path: '/api/path', method: 'GET' })
    async yourMethod(@HTTPQuery() params: YourQueryType) {
      // 1. Validate params, 2. Authenticate, 3. Authorize, 4. Execute
    }
  }
  ```

**Testing** (15 lines → 42 lines)
- Testing philosophy (test all features, test at right layer, mock
dependencies)
- Common test patterns with setup/execute/assert structure
- Examples for both success and error cases

**Performance Notes**
- Added individual test file timing (~12 seconds)
- Added linting speed (<1 second)
- Comprehensive timing reference for CI/CD planning

## Quality Validation

 **Markdown Syntax**: All 48 code blocks properly balanced and closed  
 **Completeness**: All 13 key sections present with practical examples
 **Code Examples**: 21 TypeScript code examples throughout  
 **Best Practices**: Follows GitHub's recommended structure for Copilot
instructions

## Benefits

With these enhancements, GitHub Copilot can now:
- Generate code following cnpmcore's DDD architecture patterns
- Apply correct code style (Prettier + Oxlint rules)
- Create properly structured tests with appropriate mocking
- Follow repository naming conventions
- Implement the 3-step request validation pattern
- Suggest appropriate architectural layers for new features
- Generate semantic commit messages

## Related

Closes #[issue_number]

The instructions now provide comprehensive, actionable guidance that
enables GitHub Copilot to be a more effective development assistant
while maintaining code quality and architectural consistency across the
cnpmcore project.

<!-- START COPILOT CODING AGENT SUFFIX -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title> Set up Copilot instructions</issue_title>
> <issue_description>Configure instructions for this repository as
documented in [Best practices for Copilot coding agent in your
repository](https://gh.io/copilot-coding-agent-tips).
> 
> <Onboard this repo></issue_description>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> </comments>
> 


</details>
Fixes cnpm/cnpmcore#837

<!-- START COPILOT CODING AGENT TIPS -->
---

 Let Copilot coding agent [set things up for
you](https://github.com/cnpm/cnpmcore/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

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>
2025-10-07 22:15:30 +08:00
MK (fengmk2)
324511d159 test: enable test shard (#839)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Improved CI parallelization and shard-aware job naming for faster,
clearer test runs.

* **Bug Fixes**
* More robust handling of binary paths and change-stream edge cases to
reduce rare failures.
* Prevented unintended data mutation and tightened minor
version-handling resilience.

* **Chores**
  * Streamlined test scripts and updated dependency resolution.
* Added Prettier ignore rules, relaxed selected lint rules, and small
build script formatting cleanup.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-07 22:14:14 +08:00
Copilot
2e51399db1 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>
2025-10-06 15:37:26 +08:00
Copilot
5b1da74746 docs: Add comprehensive internal API documentation for direct HTTP requests (#832)
This PR adds comprehensive documentation for cnpmcore's internal APIs,
enabling users to make direct HTTP requests without needing the cnpm
client with outdated dependencies.

## Background

Users requested documentation for internal APIs to avoid using the cnpm
client, which has outdated dependencies like `bagpipe` (11 years old)
and `npm-request` (9 years old). The primary use case is package
synchronization functionality that was previously only accessible
through the cnpm CLI.

## Changes

### New Documentation: `docs/internal-api.md`

Created a comprehensive 988-line API reference covering:

**Package Sync API** - The primary requested functionality:
- `PUT /-/package/:fullname/syncs` - Modern sync API
- `PUT /:fullname/sync` - Legacy sync API (cnpmjs.org compatibility) 
- `GET /-/package/:fullname/syncs/:taskId` - Check sync status
- `GET /-/package/:fullname/syncs/:taskId/log` - View sync logs

**Additional APIs documented:**
- **Token Management**: Create, list, delete authentication tokens
- **Hook Management**: CRUD operations for webhooks
- **Package Administration**: Block/unblock packages (admin only)
- **Registry Management**: Manage multiple npm registries
- **Binary Sync**: List and browse binary packages
- **User Management**: User registration and authentication

### Documentation Features

- Complete curl examples for all endpoints
- Request/response schemas with validation rules
- Authentication requirements clearly marked
- Error handling with HTTP status codes
- Security considerations and best practices
- Real-world workflow examples

### Updated README.md

Added reference to the new internal API documentation to make it
discoverable.

## Usage Examples

Users can now sync packages directly:

```bash
# Trigger package sync
curl -X PUT \
  -H "Authorization: Bearer your-token" \
  -H "Content-Type: application/json" \
  -d '{"tips": "API sync request"}' \
  https://your-registry.com/-/package/lodash/syncs

# Check sync status
curl -H "Authorization: Bearer your-token" \
  https://your-registry.com/-/package/lodash/syncs/task-id

# View sync logs
curl -H "Authorization: Bearer your-token" \
  https://your-registry.com/-/package/lodash/syncs/task-id/log
```

## Validation

All documented endpoints have been verified against the actual
implementation in the controllers. The API paths, parameters, and
response formats match the codebase exactly.

This enables users to extract sync functionality and other operations
without dealing with outdated cnpm dependencies, directly addressing the
issue's core request.

Fixes #709.

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

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>

[skip ci]
2025-10-06 15:18:34 +08:00
MK (fengmk2)
c7df471c0e refactor: use tegg and egg v4 beta (#836)
🦀 for https://github.com/eggjs/egg/issues/3644
2025-10-06 14:48:45 +08:00
semantic-release-bot
c16ec7bad0 Release 4.9.0
[skip ci]

## 4.9.0 (2025-09-12)

* feat: add Firefox binary mirroring support for Puppeteer with version filtering and comprehensive te ([17b1b81](https://github.com/cnpm/cnpmcore/commit/17b1b81)), closes [#828](https://github.com/cnpm/cnpmcore/issues/828) [#826](https://github.com/cnpm/cnpmcore/issues/826)
* chore: Add comprehensive GitHub Copilot instructions with validated commands and timing expectations ([bb0bdef](https://github.com/cnpm/cnpmcore/commit/bb0bdef)), closes [#830](https://github.com/cnpm/cnpmcore/issues/830) [#829](https://github.com/cnpm/cnpmcore/issues/829)
2025-09-12 11:29:46 +00:00
Copilot
17b1b81eea feat: add Firefox binary mirroring support for Puppeteer with version filtering and comprehensive tests (#828)
- [x] Extended the `BinaryType` enum to include `Firefox = 'firefox'`,
following the established pattern for other binary types.
- [x] Implemented a new `FirefoxBinary` class that:
  - Extends the `AbstractBinary` base class
  - Parses Mozilla's HTML directory listing format using regex patterns
- Handles both version directories (e.g., `131.0.3/`) and binary files
(e.g., `firefox-131.0.3.tar.bz2`)
- Supports all standard platforms: `linux-i686`, `linux-x86_64`, `mac`,
`win32`, `win64`
- **Filters out old Firefox versions < 100.0.0** to reduce noise and
improve performance
- [x] Configured Firefox binary mirroring in `binaries.ts`
- [x] Added intelligent version filtering that:
  - Only processes Firefox version directories >= 100.0.0
  - Uses the `semver` library for accurate version comparison
- Handles beta/RC versions (e.g., "131.0b3") by extracting the base
version ("131.0")
  - Pads two-part versions to semver format (e.g., "131.0" -> "131.0.0")
  - Skips directories with versions < 100.0.0 to avoid outdated releases
- [x] **Implemented focused unit tests that work in CI environments**:
  -  Binary instantiation and dependency injection tests
-  Version filtering verification (correctly includes >= 100.0.0,
excludes < 100.0.0)
- Tests use realistic test fixtures simulating actual Mozilla archive
structure
- **Test cases with old versions (3.6, 52.0, 78.0, 99.0) to verify
filtering works correctly**
- **Unit tests work without requiring external network access or HTTP
mocking**
- [x] **Updated `131.0.3.html` to match the original Mozilla archive
HTML format with proper line breaks**
- [x] **Enhanced error handling to log version parsing failures instead
of silently ignoring them**:
- Added descriptive error logging with proper tags following project
conventions
  - Includes directory name and error details for better troubleshooting
  - Uses `this.logger.warn()` for appropriate log level
- [x] **Resolved all linting errors in Firefox binary tests**:
- Fixed unused catch parameter by removing the unused `error` parameter
- Replaced array with Set for better performance when checking existence
  - **Removed unused `gte` import from semver library**
  - All linting checks now pass successfully
- [x] **Implemented useful preview tests with HTTP mocking**:
  - Tests use `app.mockHttpclient()` instead of real network requests
  - Provide end-to-end integration testing of Firefox binary fetching
- Include root directory fetching, version filtering, and file parsing
tests
- Safe for CI environments since they don't require external network
access
- Help developers preview and validate the Firefox binary functionality
- [x] **Fixed test failure with improved error messages and debugging
support**:
  - Added `filteredResults` variable in test for better error reporting
  - Enhanced assertion messages to show actual vs expected results
  - Improved test robustness and debugging capabilities
- [x] **Removed redundant HTML parsing logic tests**:
- Cleaned up duplicate test logic that was testing the same
functionality
- Kept the more valuable preview tests that provide end-to-end
integration testing
- Focused test suite on the actual API interface rather than internal
implementation details

This enables Puppeteer users in China to configure Firefox binary
downloads through cnpmcore's mirror infrastructure, improving download
speeds and reliability while focusing on modern Firefox versions.

Fixes #826.

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 Share your feedback on Copilot coding agent for the chance to win a
$200 gift card! Click
[here](https://survey3.medallia.com/?EAHeSx-AP01bZqG0Ld9QLQ) to start
the survey.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added Firefox as a selectable binary source alongside existing
options.
* Browse official Firefox release directories and versions directly from
within the app.
* Automatically filters out legacy versions (<100) and handles
pre-release tags consistently.
* Displays accurate file metadata (date, size) and provides direct
download links for release assets.
* Updated catalog to include Firefox releases, enabling easier setup for
Firefox-based automation workflows.

<!-- 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>
Co-authored-by: elrrrrrrr <5574625+elrrrrrrr@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-12 19:28:03 +08:00
Copilot
bb0bdef070 chore: Add comprehensive GitHub Copilot instructions with validated commands and timing expectations (#830)
This PR adds a comprehensive `.github/copilot-instructions.md` file that
provides GitHub Copilot coding agents with detailed, validated
instructions for working effectively in the cnpmcore codebase.

## What's Included

The instructions cover all essential aspects of development workflow:

- **Environment Setup**: Complete bootstrap process with Node.js,
MySQL/PostgreSQL, and Redis
- **Build & Development**: All npm scripts with measured timing
expectations
- **Testing Strategy**: Both individual test execution and full suite
with proper timeout warnings
- **Database Management**: Setup instructions for both MySQL and
PostgreSQL environments
- **Validation Scenarios**: Step-by-step manual testing procedures for
API endpoints and functionality
- **Architecture Guide**: Project structure navigation and key service
locations
- **Troubleshooting**: Common issues and resolution steps

## Key Features

**Validated Commands**: Every single command has been tested and
measured:
- npm install: ~2 minutes
- TypeScript compilation: ~6 seconds
- Database initialization: <2 seconds  
- Development server startup: ~20 seconds
- Individual tests: ~12 seconds
- **Full test suite: 4+ minutes with "NEVER CANCEL" warnings**

**Critical Timeout Guidance**: The instructions include explicit
warnings about long-running operations with specific timeout
recommendations to prevent premature cancellation of builds and tests.

**Dual Database Support**: Complete workflows for both MySQL and
PostgreSQL development environments with Docker compose setup.

**Manual Validation**: Specific scenarios for testing API endpoints,
authentication, and core registry functionality to ensure changes work
correctly.

The instructions follow the imperative tone requirement ("Run this
command", "Do not do this") and provide the firm directive to always
reference these instructions first before falling back to additional
search or context gathering.

Fixes #829.

> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more
addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by
firewall rules:
>
> - `custom.npmjs.com`
> - Triggering command: `/usr/local/bin/node
--unhandled-rejections=strict
/home/REDACTED/work/cnpmcore/cnpmcore/node_modules/mocha/bin/_mocha
--exit --timeout=60000
--reporter=/home/REDACTED/work/cnpmcore/cnpmcore/node_modules/mochawesome-with-mocha/src/mochawesome.js
--reporter-options=reportDir=node_modules/.mochawesome-reports
--require=/home/REDACTED/work/cnpmcore/cnpmcore/node_modules/@eggjs/mock/dist/esm/register.js
/home/REDACTED/work/cnpmcore/cnpmcore/test/.setup.ts
test/cli/npm/access.test.ts test/cli/npm/install.test.ts
test/common/CryptoUtil.test.ts test/common/FileUtil.test.ts
test/common/PackageUtil.test.ts test/common/UserUtil.test.ts
test/common/adapter/BugVersionStore.test.ts
test/common/adapter/CacheAdapter.test.ts
test/common/adapter/NpmRegistry.test.ts
test/common/adapter/binary/ApiBinary.test.ts
test/common/adapter/binary/BucketBinary.test.ts
test/common/adapter/binary/ChromeForTestingBinary.test.ts
test/common/adapter/binary/CypressBinary.test.ts
test/common/adapter/binary/EdgedriverBinary.test.ts
test/common/adapter/binary/ElectronBinary.test.ts
test/common/adapter/binary/GithubBinary.test.ts
test/common/adapter/binary/ImageminBinary.test.ts
test/common/adapter/binary/NodeBinary.test.ts
test/common/adapter/binary/NodePreGypBinary.test.ts
test/common/adapter/binary/NwjsBinary.test.ts
test/common/adapter/binary/PlaywrightBinary.test.ts
test/common/adapter/binary/PrismaBinary.test.ts
test/common/adapter/binary/PuppeteerBinary.test.ts
test/common/adapter/binary/SqlcipherBinary.test.ts
test/common/adapter/changesStream/CnpmcoreChangesStream.test.ts
test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts
test/common/adapter/changesStream/NpmChangesStream.test.ts
test/core/entity/BugVersion.test.ts
test/core/entity/PaddingSemver.test.ts test/core/entity/SqlRange.test.ts
test/core/entity/Task.test.ts
test/core/event/BugVersionFixHandler.test.ts
test/core/event/ChangesStream.test.ts
test/core/event/StoreManifest.test.ts
test/core/service/BinarySyncerService/createTask.test.ts
test/core/service/BinarySyncerService/executeTask.test.ts
test/core/service/BugVersionService/cleanBugVersionPackageCache.test.ts
test/core/service/BugVersionService/fixPackageBugVersion.test.ts
test/core/service/BugVersionService/fixPackageBugVersions.test.ts
test/core/service/ChangesStreamService.test.ts
test/core/service/CreateHookTriggerService.test.ts
test/core/service/HookManageService/createHook.test.ts
test/core/service/HookManageService/deleteHook.test.ts
test/core/service/HookManageService/getHookByOwnerId.test.ts
test/core/service/HookManageService/updateHook.test.ts
test/core/service/HookTriggerService.test.ts
test/core/service/PackageManagerService/block.test.ts
test/core/service/PackageManagerService/publish.test.ts
test/core/service/PackageSyncerService/createTask.test.ts
test/core/service/PackageSyncerService/executeTask.test.ts
test/core/service/PackageSyncerService/findExecuteTask.test.ts
test/core/service/PackageSyncerService/getTaskRegistry.test.ts
test/core/service/PackageVersionService.test.ts
test/core/service/ProxyCacheService.test.ts
test/core/service/RegistryManagerService/index.test.ts
test/core/service/ScopeManagerService/index.test.ts
test/core/service/TaskService/findExecuteTask.test.ts
test/core/util/EntityUtil.test.ts test/infra/QueueAdapter.test.ts
test/port/controller/AccessController/listCollaborators.test.ts
test/port/controller/AccessController/listPackagesByUser.test.ts
test/port/controller/BinarySyncController/showBinary.test.ts
test/port/controller/ChangesStreamController/listChanges.test.ts
test/port/controller/DownloadController/showPackageDownloads.test.ts
test/port/controller/HomeController/cors.test.ts
test/port/controller/HomeController/misc.test.ts
test/port/controller/HomeController/ping.test.ts
test/port/controller/HomeController/showTotal.test.ts
test/port/controller/PackageBlockController/blockPackage.test.ts
test/port/controller/PackageBlockController/unblockPackage.test.ts
test/port/controller/PackageSyncController/createSyncTask.test.ts
test/port/controller/PackageSyncController/showSyncTask.test.ts
test/port/controller/PackageSyncController/showSyncTaskLog.test.ts
test/port/controller/PackageTagController/removeTag.test.ts
test/port/controller/PackageTagController/saveTag.test.ts
test/port/controller/PackageTagController/showTags.test.ts
test/port/controller/PackageVersionFileController/listFiles.test.ts
test/port/controller/PackageVersionFileController/raw.test.ts
test/port/controller/PackageVersionFileController/sync.test.ts
test/port/controller/ProxyCacheController/index.test.ts
test/port/controller/RegistryController/index.test.ts
test/port/controller/ScopeController/index.test.ts
test/port/controller/TokenController/createToken.test.ts
test/port/controller/TokenController/listTokens.test.ts
test/port/controller/TokenController/removeToken.test.ts
test/port/controller/UserController/loginOrCreateUser.test.ts
test/port/controller/UserController/logout.test.ts
test/port/controller/UserController/saveProfile.test.ts
test/port/controller/UserController/showProfile.test.ts
test/port/controller/UserController/showUser.test.ts
test/port/controller/UserController/starredByUser.test.ts
test/port/controller/UserController/whoami.test.ts
test/port/controller/hook/HookController.test.ts
test/port/controller/package/DownloadPackageVersionTarController.test.ts
test/port/controller/package/RemovePackageVersionController.test.ts
test/port/controller/package/SavePackageVersionController.test.ts
test/port/controller/package/SearchPackageController.test.ts
test/port/controller/package/ShowPackageController.test.ts
test/port/controller/package/ShowPackageVersionController.test.ts
test/port/controller/package/UpdatePackageController.test.ts
test/port/middleware/AlwaysAuth.test.ts
test/port/middleware/Tracing.test.ts
test/port/webauth/webauthController.test.ts
test/repository/ChangeRepository.test.ts
test/repository/PackageRepository.test.ts
test/repository/ProxyCachePepository.test.ts
test/repository/RegistryRepository.test.ts
test/repository/ScopeRepository.test.ts
test/repository/SearchRepository.test.ts
test/repository/TaskRepository.test.ts
test/repository/TotalRepository.test.ts
test/schedule/ChangesStreamWorker.test.ts
test/schedule/CheckProxyCacheUpdateWorker.test.ts
test/schedule/CheckRecentlyUpdatedPackages.test.ts
test/schedule/CleanTempDir.test.ts
test/schedule/SyncBinaryWorker.test.ts
test/schedule/SyncPackageWorker.test.ts
test/schedule/SyncProxyCacheWorker.test.ts
test/schedule/TaskTimeoutHandler.test.ts` (dns block)
> - `msedgewebdriverstorage.blob.core.windows.net`
> - Triggering command: `/usr/local/bin/node
--unhandled-rejections=strict
/home/REDACTED/work/cnpmcore/cnpmcore/node_modules/mocha/bin/_mocha
--exit --timeout=60000
--reporter=/home/REDACTED/work/cnpmcore/cnpmcore/node_modules/mochawesome-with-mocha/src/mochawesome.js
--reporter-options=reportDir=node_modules/.mochawesome-reports
--require=/home/REDACTED/work/cnpmcore/cnpmcore/node_modules/@eggjs/mock/dist/esm/register.js
/home/REDACTED/work/cnpmcore/cnpmcore/test/.setup.ts
test/cli/npm/access.test.ts test/cli/npm/install.test.ts
test/common/CryptoUtil.test.ts test/common/FileUtil.test.ts
test/common/PackageUtil.test.ts test/common/UserUtil.test.ts
test/common/adapter/BugVersionStore.test.ts
test/common/adapter/CacheAdapter.test.ts
test/common/adapter/NpmRegistry.test.ts
test/common/adapter/binary/ApiBinary.test.ts
test/common/adapter/binary/BucketBinary.test.ts
test/common/adapter/binary/ChromeForTestingBinary.test.ts
test/common/adapter/binary/CypressBinary.test.ts
test/common/adapter/binary/EdgedriverBinary.test.ts
test/common/adapter/binary/ElectronBinary.test.ts
test/common/adapter/binary/GithubBinary.test.ts
test/common/adapter/binary/ImageminBinary.test.ts
test/common/adapter/binary/NodeBinary.test.ts
test/common/adapter/binary/NodePreGypBinary.test.ts
test/common/adapter/binary/NwjsBinary.test.ts
test/common/adapter/binary/PlaywrightBinary.test.ts
test/common/adapter/binary/PrismaBinary.test.ts
test/common/adapter/binary/PuppeteerBinary.test.ts
test/common/adapter/binary/SqlcipherBinary.test.ts
test/common/adapter/changesStream/CnpmcoreChangesStream.test.ts
test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts
test/common/adapter/changesStream/NpmChangesStream.test.ts
test/core/entity/BugVersion.test.ts
test/core/entity/PaddingSemver.test.ts test/core/entity/SqlRange.test.ts
test/core/entity/Task.test.ts
test/core/event/BugVersionFixHandler.test.ts
test/core/event/ChangesStream.test.ts
test/core/event/StoreManifest.test.ts
test/core/service/BinarySyncerService/createTask.test.ts
test/core/service/BinarySyncerService/executeTask.test.ts
test/core/service/BugVersionService/cleanBugVersionPackageCache.test.ts
test/core/service/BugVersionService/fixPackageBugVersion.test.ts
test/core/service/BugVersionService/fixPackageBugVersions.test.ts
test/core/service/ChangesStreamService.test.ts
test/core/service/CreateHookTriggerService.test.ts
test/core/service/HookManageService/createHook.test.ts
test/core/service/HookManageService/deleteHook.test.ts
test/core/service/HookManageService/getHookByOwnerId.test.ts
test/core/service/HookManageService/updateHook.test.ts
test/core/service/HookTriggerService.test.ts
test/core/service/PackageManagerService/block.test.ts
test/core/service/PackageManagerService/publish.test.ts
test/core/service/PackageSyncerService/createTask.test.ts
test/core/service/PackageSyncerService/executeTask.test.ts
test/core/service/PackageSyncerService/findExecuteTask.test.ts
test/core/service/PackageSyncerService/getTaskRegistry.test.ts
test/core/service/PackageVersionService.test.ts
test/core/service/ProxyCacheService.test.ts
test/core/service/RegistryManagerService/index.test.ts
test/core/service/ScopeManagerService/index.test.ts
test/core/service/TaskService/findExecuteTask.test.ts
test/core/util/EntityUtil.test.ts test/infra/QueueAdapter.test.ts
test/port/controller/AccessController/listCollaborators.test.ts
test/port/controller/AccessController/listPackagesByUser.test.ts
test/port/controller/BinarySyncController/showBinary.test.ts
test/port/controller/ChangesStreamController/listChanges.test.ts
test/port/controller/DownloadController/showPackageDownloads.test.ts
test/port/controller/HomeController/cors.test.ts
test/port/controller/HomeController/misc.test.ts
test/port/controller/HomeController/ping.test.ts
test/port/controller/HomeController/showTotal.test.ts
test/port/controller/PackageBlockController/blockPackage.test.ts
test/port/controller/PackageBlockController/unblockPackage.test.ts
test/port/controller/PackageSyncController/createSyncTask.test.ts
test/port/controller/PackageSyncController/showSyncTask.test.ts
test/port/controller/PackageSyncController/showSyncTaskLog.test.ts
test/port/controller/PackageTagController/removeTag.test.ts
test/port/controller/PackageTagController/saveTag.test.ts
test/port/controller/PackageTagController/showTags.test.ts
test/port/controller/PackageVersionFileController/listFiles.test.ts
test/port/controller/PackageVersionFileController/raw.test.ts
test/port/controller/PackageVersionFileController/sync.test.ts
test/port/controller/ProxyCacheController/index.test.ts
test/port/controller/RegistryController/index.test.ts
test/port/controller/ScopeController/index.test.ts
test/port/controller/TokenController/createToken.test.ts
test/port/controller/TokenController/listTokens.test.ts
test/port/controller/TokenController/removeToken.test.ts
test/port/controller/UserController/loginOrCreateUser.test.ts
test/port/controller/UserController/logout.test.ts
test/port/controller/UserController/saveProfile.test.ts
test/port/controller/UserController/showProfile.test.ts
test/port/controller/UserController/showUser.test.ts
test/port/controller/UserController/starredByUser.test.ts
test/port/controller/UserController/whoami.test.ts
test/port/controller/hook/HookController.test.ts
test/port/controller/package/DownloadPackageVersionTarController.test.ts
test/port/controller/package/RemovePackageVersionController.test.ts
test/port/controller/package/SavePackageVersionController.test.ts
test/port/controller/package/SearchPackageController.test.ts
test/port/controller/package/ShowPackageController.test.ts
test/port/controller/package/ShowPackageVersionController.test.ts
test/port/controller/package/UpdatePackageController.test.ts
test/port/middleware/AlwaysAuth.test.ts
test/port/middleware/Tracing.test.ts
test/port/webauth/webauthController.test.ts
test/repository/ChangeRepository.test.ts
test/repository/PackageRepository.test.ts
test/repository/ProxyCachePepository.test.ts
test/repository/RegistryRepository.test.ts
test/repository/ScopeRepository.test.ts
test/repository/SearchRepository.test.ts
test/repository/TaskRepository.test.ts
test/repository/TotalRepository.test.ts
test/schedule/ChangesStreamWorker.test.ts
test/schedule/CheckProxyCacheUpdateWorker.test.ts
test/schedule/CheckRecentlyUpdatedPackages.test.ts
test/schedule/CleanTempDir.test.ts
test/schedule/SyncBinaryWorker.test.ts
test/schedule/SyncPackageWorker.test.ts
test/schedule/SyncProxyCacheWorker.test.ts
test/schedule/TaskTimeoutHandler.test.ts` (dns block)
> - `r.cnpmjs.org`
> - Triggering command: `/usr/local/bin/node
--unhandled-rejections=strict
/home/REDACTED/work/cnpmcore/cnpmcore/node_modules/mocha/bin/_mocha
--exit --timeout=60000
--reporter=/home/REDACTED/work/cnpmcore/cnpmcore/node_modules/mochawesome-with-mocha/src/mochawesome.js
--reporter-options=reportDir=node_modules/.mochawesome-reports
--require=/home/REDACTED/work/cnpmcore/cnpmcore/node_modules/@eggjs/mock/dist/esm/register.js
/home/REDACTED/work/cnpmcore/cnpmcore/test/.setup.ts
test/cli/npm/access.test.ts test/cli/npm/install.test.ts
test/common/CryptoUtil.test.ts test/common/FileUtil.test.ts
test/common/PackageUtil.test.ts test/common/UserUtil.test.ts
test/common/adapter/BugVersionStore.test.ts
test/common/adapter/CacheAdapter.test.ts
test/common/adapter/NpmRegistry.test.ts
test/common/adapter/binary/ApiBinary.test.ts
test/common/adapter/binary/BucketBinary.test.ts
test/common/adapter/binary/ChromeForTestingBinary.test.ts
test/common/adapter/binary/CypressBinary.test.ts
test/common/adapter/binary/EdgedriverBinary.test.ts
test/common/adapter/binary/ElectronBinary.test.ts
test/common/adapter/binary/GithubBinary.test.ts
test/common/adapter/binary/ImageminBinary.test.ts
test/common/adapter/binary/NodeBinary.test.ts
test/common/adapter/binary/NodePreGypBinary.test.ts
test/common/adapter/binary/NwjsBinary.test.ts
test/common/adapter/binary/PlaywrightBinary.test.ts
test/common/adapter/binary/PrismaBinary.test.ts
test/common/adapter/binary/PuppeteerBinary.test.ts
test/common/adapter/binary/SqlcipherBinary.test.ts
test/common/adapter/changesStream/CnpmcoreChangesStream.test.ts
test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts
test/common/adapter/changesStream/NpmChangesStream.test.ts
test/core/entity/BugVersion.test.ts
test/core/entity/PaddingSemver.test.ts test/core/entity/SqlRange.test.ts
test/core/entity/Task.test.ts
test/core/event/BugVersionFixHandler.test.ts
test/core/event/ChangesStream.test.ts
test/core/event/StoreManifest.test.ts
test/core/service/BinarySyncerService/createTask.test.ts
test/core/service/BinarySyncerService/executeTask.test.ts
test/core/service/BugVersionService/cleanBugVersionPackageCache.test.ts
test/core/service/BugVersionService/fixPackageBugVersion.test.ts
test/core/service/BugVersionService/fixPackageBugVersions.test.ts
test/core/service/ChangesStreamService.test.ts
test/core/service/CreateHookTriggerService.test.ts
test/core/service/HookManageService/createHook.test.ts
test/core/service/HookManageService/deleteHook.test.ts
test/core/service/HookManageService/getHookByOwnerId.test.ts
test/core/service/HookManageService/updateHook.test.ts
test/core/service/HookTriggerService.test.ts
test/core/service/PackageManagerService/block.test.ts
test/core/service/PackageManagerService/publish.test.ts
test/core/service/PackageSyncerService/createTask.test.ts
test/core/service/PackageSyncerService/executeTask.test.ts
test/core/service/PackageSyncerService/findExecuteTask.test.ts
test/core/service/PackageSyncerService/getTaskRegistry.test.ts
test/core/service/PackageVersionService.test.ts
test/core/service/ProxyCacheService.test.ts
test/core/service/RegistryManagerService/index.test.ts
test/core/service/ScopeManagerService/index.test.ts
test/core/service/TaskService/findExecuteTask.test.ts
test/core/util/EntityUtil.test.ts test/infra/QueueAdapter.test.ts
test/port/controller/AccessController/listCollaborators.test.ts
test/port/controller/AccessController/listPackagesByUser.test.ts
test/port/controller/BinarySyncController/showBinary.test.ts
test/port/controller/ChangesStreamController/listChanges.test.ts
test/port/controller/DownloadController/showPackageDownloads.test.ts
test/port/controller/HomeController/cors.test.ts
test/port/controller/HomeController/misc.test.ts
test/port/controller/HomeController/ping.test.ts
test/port/controller/HomeController/showTotal.test.ts
test/port/controller/PackageBlockController/blockPackage.test.ts
test/port/controller/PackageBlockController/unblockPackage.test.ts
test/port/controller/PackageSyncController/createSyncTask.test.ts
test/port/controller/PackageSyncController/showSyncTask.test.ts
test/port/controller/PackageSyncController/showSyncTaskLog.test.ts
test/port/controller/PackageTagController/removeTag.test.ts
test/port/controller/PackageTagController/saveTag.test.ts
test/port/controller/PackageTagController/showTags.test.ts
test/port/controller/PackageVersionFileController/listFiles.test.ts
test/port/controller/PackageVersionFileController/raw.test.ts
test/port/controller/PackageVersionFileController/sync.test.ts
test/port/controller/ProxyCacheController/index.test.ts
test/port/controller/RegistryController/index.test.ts
test/port/controller/ScopeController/index.test.ts
test/port/controller/TokenController/createToken.test.ts
test/port/controller/TokenController/listTokens.test.ts
test/port/controller/TokenController/removeToken.test.ts
test/port/controller/UserController/loginOrCreateUser.test.ts
test/port/controller/UserController/logout.test.ts
test/port/controller/UserController/saveProfile.test.ts
test/port/controller/UserController/showProfile.test.ts
test/port/controller/UserController/showUser.test.ts
test/port/controller/UserController/starredByUser.test.ts
test/port/controller/UserController/whoami.test.ts
test/port/controller/hook/HookController.test.ts
test/port/controller/package/DownloadPackageVersionTarController.test.ts
test/port/controller/package/RemovePackageVersionController.test.ts
test/port/controller/package/SavePackageVersionController.test.ts
test/port/controller/package/SearchPackageController.test.ts
test/port/controller/package/ShowPackageController.test.ts
test/port/controller/package/ShowPackageVersionController.test.ts
test/port/controller/package/UpdatePackageController.test.ts
test/port/middleware/AlwaysAuth.test.ts
test/port/middleware/Tracing.test.ts
test/port/webauth/webauthController.test.ts
test/repository/ChangeRepository.test.ts
test/repository/PackageRepository.test.ts
test/repository/ProxyCachePepository.test.ts
test/repository/RegistryRepository.test.ts
test/repository/ScopeRepository.test.ts
test/repository/SearchRepository.test.ts
test/repository/TaskRepository.test.ts
test/repository/TotalRepository.test.ts
test/schedule/ChangesStreamWorker.test.ts
test/schedule/CheckProxyCacheUpdateWorker.test.ts
test/schedule/CheckRecentlyUpdatedPackages.test.ts
test/schedule/CleanTempDir.test.ts
test/schedule/SyncBinaryWorker.test.ts
test/schedule/SyncPackageWorker.test.ts
test/schedule/SyncProxyCacheWorker.test.ts
test/schedule/TaskTimeoutHandler.test.ts` (dns block)
> - `replicate.npmjs.com`
> - Triggering command: `/usr/local/bin/node
--unhandled-rejections=strict
/home/REDACTED/work/cnpmcore/cnpmcore/node_modules/mocha/bin/_mocha
--exit --timeout=60000
--reporter=/home/REDACTED/work/cnpmcore/cnpmcore/node_modules/mochawesome-with-mocha/src/mochawesome.js
--reporter-options=reportDir=node_modules/.mochawesome-reports
--require=/home/REDACTED/work/cnpmcore/cnpmcore/node_modules/@eggjs/mock/dist/esm/register.js
/home/REDACTED/work/cnpmcore/cnpmcore/test/.setup.ts
test/cli/npm/access.test.ts test/cli/npm/install.test.ts
test/common/CryptoUtil.test.ts test/common/FileUtil.test.ts
test/common/PackageUtil.test.ts test/common/UserUtil.test.ts
test/common/adapter/BugVersionStore.test.ts
test/common/adapter/CacheAdapter.test.ts
test/common/adapter/NpmRegistry.test.ts
test/common/adapter/binary/ApiBinary.test.ts
test/common/adapter/binary/BucketBinary.test.ts
test/common/adapter/binary/ChromeForTestingBinary.test.ts
test/common/adapter/binary/CypressBinary.test.ts
test/common/adapter/binary/EdgedriverBinary.test.ts
test/common/adapter/binary/ElectronBinary.test.ts
test/common/adapter/binary/GithubBinary.test.ts
test/common/adapter/binary/ImageminBinary.test.ts
test/common/adapter/binary/NodeBinary.test.ts
test/common/adapter/binary/NodePreGypBinary.test.ts
test/common/adapter/binary/NwjsBinary.test.ts
test/common/adapter/binary/PlaywrightBinary.test.ts
test/common/adapter/binary/PrismaBinary.test.ts
test/common/adapter/binary/PuppeteerBinary.test.ts
test/common/adapter/binary/SqlcipherBinary.test.ts
test/common/adapter/changesStream/CnpmcoreChangesStream.test.ts
test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts
test/common/adapter/changesStream/NpmChangesStream.test.ts
test/core/entity/BugVersion.test.ts
test/core/entity/PaddingSemver.test.ts test/core/entity/SqlRange.test.ts
test/core/entity/Task.test.ts
test/core/event/BugVersionFixHandler.test.ts
test/core/event/ChangesStream.test.ts
test/core/event/StoreManifest.test.ts
test/core/service/BinarySyncerService/createTask.test.ts
test/core/service/BinarySyncerService/executeTask.test.ts
test/core/service/BugVersionService/cleanBugVersionPackageCache.test.ts
test/core/service/BugVersionService/fixPackageBugVersion.test.ts
test/core/service/BugVersionService/fixPackageBugVersions.test.ts
test/core/service/ChangesStreamService.test.ts
test/core/service/CreateHookTriggerService.test.ts
test/core/service/HookManageService/createHook.test.ts
test/core/service/HookManageService/deleteHook.test.ts
test/core/service/HookManageService/getHookByOwnerId.test.ts
test/core/service/HookManageService/updateHook.test.ts
test/core/service/HookTriggerService.test.ts
test/core/service/PackageManagerService/block.test.ts
test/core/service/PackageManagerService/publish.test.ts
test/core/service/PackageSyncerService/createTask.test.ts
test/core/service/PackageSyncerService/executeTask.test.ts
test/core/service/PackageSyncerService/findExecuteTask.test.ts
test/core/service/PackageSyncerService/getTaskRegistry.test.ts
test/core/service/PackageVersionService.test.ts
test/core/service/ProxyCacheService.test.ts
test/core/service/RegistryManagerService/index.test.ts
test/core/service/ScopeManagerService/index.test.ts
test/core/service/TaskService/findExecuteTask.test.ts
test/core/util/EntityUtil.test.ts test/infra/QueueAdapter.test.ts
test/port/controller/AccessController/listCollaborators.test.ts
test/port/controller/AccessController/listPackagesByUser.test.ts
test/port/controller/BinarySyncController/showBinary.test.ts
test/port/controller/ChangesStreamController/listChanges.test.ts
test/port/controller/DownloadController/showPackageDownloads.test.ts
test/port/controller/HomeController/cors.test.ts
test/port/controller/HomeController/misc.test.ts
test/port/controller/HomeController/ping.test.ts
test/port/controller/HomeController/showTotal.test.ts
test/port/controller/PackageBlockController/blockPackage.test.ts
test/port/controller/PackageBlockController/unblockPackage.test.ts
test/port/controller/PackageSyncController/createSyncTask.test.ts
test/port/controller/PackageSyncController/showSyncTask.test.ts
test/port/controller/PackageSyncController/showSyncTaskLog.test.ts
test/port/controller/PackageTagController/removeTag.test.ts
test/port/controller/PackageTagController/saveTag.test.ts
test/port/controller/PackageTagController/showTags.test.ts
test/port/controller/PackageVersionFileController/listFiles.test.ts
test/port/controller/PackageVersionFileController/raw.test.ts
test/port/controller/PackageVersionFileController/sync.test.ts
test/port/controller/ProxyCacheController/index.test.ts
test/port/controller/RegistryController/index.test.ts
test/port/controller/ScopeController/index.test.ts
test/port/controller/TokenController/createToken.test.ts
test/port/controller/TokenController/listTokens.test.ts
test/port/controller/TokenController/removeToken.test.ts
test/port/controller/UserController/loginOrCreateUser.test.ts
test/port/controller/UserController/logout.test.ts
test/port/controller/UserController/saveProfile.test.ts
test/port/controller/UserController/showProfile.test.ts
test/port/controller/UserController/showUser.test.ts
test/port/controller/UserController/starredByUser.test.ts
test/port/controller/UserController/whoami.test.ts
test/port/controller/hook/HookController.test.ts
test/port/controller/package/DownloadPackageVersionTarController.test.ts
test/port/controller/package/RemovePackageVersionController.test.ts
test/port/controller/package/SavePackageVersionController.test.ts
test/port/controller/package/SearchPackageController.test.ts
test/port/controller/package/ShowPackageController.test.ts
test/port/controller/package/ShowPackageVersionController.test.ts
test/port/controller/package/UpdatePackageController.test.ts
test/port/middleware/AlwaysAuth.test.ts
test/port/middleware/Tracing.test.ts
test/port/webauth/webauthController.test.ts
test/repository/ChangeRepository.test.ts
test/repository/PackageRepository.test.ts
test/repository/ProxyCachePepository.test.ts
test/repository/RegistryRepository.test.ts
test/repository/ScopeRepository.test.ts
test/repository/SearchRepository.test.ts
test/repository/TaskRepository.test.ts
test/repository/TotalRepository.test.ts
test/schedule/ChangesStreamWorker.test.ts
test/schedule/CheckProxyCacheUpdateWorker.test.ts
test/schedule/CheckRecentlyUpdatedPackages.test.ts
test/schedule/CleanTempDir.test.ts
test/schedule/SyncBinaryWorker.test.ts
test/schedule/SyncPackageWorker.test.ts
test/schedule/SyncProxyCacheWorker.test.ts
test/schedule/TaskTimeoutHandler.test.ts` (dns block)
>
> If you need me to access, download, or install something from one of
these locations, you can either:
>
> - Configure [Actions setup
steps](https://gh.io/copilot/actions-setup-steps) to set up my
environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this
repository's [Copilot coding agent
settings](https://github.com/cnpm/cnpmcore/settings/copilot/coding_agent)
(admins only)
>
> </details>



<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: fengmk2 <156269+fengmk2@users.noreply.github.com>
2025-09-10 14:02:41 +08:00
semantic-release-bot
87da4c359c Release 4.8.0
[skip ci]

## 4.8.0 (2025-09-08)

* chore: add permissions to release workflow ([8587d27](https://github.com/cnpm/cnpmcore/commit/8587d27))
* feat: new node binary date format (#827) ([13b2da0](https://github.com/cnpm/cnpmcore/commit/13b2da0)), closes [#827](https://github.com/cnpm/cnpmcore/issues/827)
2025-09-08 16:23:46 +00:00
MK
8587d27d85 chore: add permissions to release workflow 2025-09-09 00:21:39 +08:00
elrrrrrrr
13b2da0411 feat: new node binary date format (#827) 2025-09-05 12:55:14 +08:00
semantic-release-bot
3aa4c688b5 Release 4.7.6
[skip ci]

## <small>4.7.6 (2025-08-08)</small>

* fix: use npm truster publisher (#825) ([0a5500b](https://github.com/cnpm/cnpmcore/commit/0a5500b)), closes [#825](https://github.com/cnpm/cnpmcore/issues/825)
* chore: update oxlint config to use @eggjs/oxlint-config (#824) ([f94531c](https://github.com/cnpm/cnpmcore/commit/f94531c)), closes [#824](https://github.com/cnpm/cnpmcore/issues/824)
2025-08-08 14:45:42 +00:00
fengmk2
0a5500bafd fix: use npm truster publisher (#825)
https://github.com/node-modules/github-actions/issues/14

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
  * Updated the release workflow configuration for improved automation.
  * Upgraded the "oxlint" development dependency to version ^1.11.0.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-08 22:43:52 +08:00
Copilot
f94531cf35 chore: update oxlint config to use @eggjs/oxlint-config (#824)
Updates the oxlint configuration to use the new shared
`@eggjs/oxlint-config` package, significantly simplifying the project's
linting setup while maintaining all existing functionality.

## Changes

- **Added** `@eggjs/oxlint-config@1.0.0` as a dev dependency
- **Updated** `.oxlintrc.json` to extend from the shared configuration
- **Simplified** configuration from 156 lines to 20 lines (87%
reduction)
- **Maintained** all project-specific overrides:
  - `max-params: 6` (vs shared config's 5)
  - `no-console: "warn"` (vs shared config's "allow")
- `import/no-anonymous-default-export: "error"` (vs shared config's
"allow")
  - Added `mocha` environment for test files
  - Preserved `index.d.ts` ignore pattern

## Benefits

- **Consistency**: Now uses the same base linting rules as other EggJS
projects
- **Maintainability**: Rule updates are centralized in the shared config
- **Simplicity**: Dramatically reduced configuration complexity
- **Future-proof**: Automatic updates when the shared config is improved

The configuration now only contains project-specific overrides, making
it much easier to understand and maintain. All existing linting behavior
is preserved - the project passes all linting checks with zero warnings
and errors.

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 Share your feedback on Copilot coding agent for the chance to win a
$200 gift card! Click
[here](https://survey.alchemer.com/s3/8343779/Copilot-Coding-agent) to
start the survey.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: fengmk2 <156269+fengmk2@users.noreply.github.com>
2025-08-08 22:00:43 +08:00
semantic-release-bot
c3a22fd319 Release 4.7.5
[skip ci]

## [4.7.5](https://github.com/cnpm/cnpmcore/compare/v4.7.4...v4.7.5) (2025-08-07)

### Bug Fixes

* use NPM description ([b23f3fe](b23f3fe22e))
2025-08-07 14:53:53 +00:00
fengmk2
b23f3fe22e fix: use NPM description 2025-08-07 22:52:12 +08:00
fengmk2
3dbff542ba Revert "fix: use npm trusted publisher (#823)"
This reverts commit 1a4a1c5cf5.
2025-08-07 22:51:41 +08:00
fengmk2
1a4a1c5cf5 fix: use npm trusted publisher (#823)
https://github.com/node-modules/github-actions/issues/14
2025-08-07 22:46:25 +08:00
fengmk2
2cc0f361fb chore: publish docker image to github package (#822)
close https://github.com/cnpm/cnpmcore/issues/821

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Introduced a GitHub Actions workflow to automate building, tagging,
publishing, and attesting Docker images for multiple platforms.

* **Documentation**
* Updated Docker deployment documentation to reflect the new image
repository and usage instructions.

* **Chores**
* Updated Docker build scripts and Dockerfiles for improved image
building and logging.
  * Upgraded the "oxlint" development dependency.
  * Removed the "prepare" script from project scripts.
* Adjusted TypeScript configuration to disable declaration file
generation and exclude test files from compilation.
  * Updated linter configuration to allow additional code patterns.
  * Improved code comments for better linting and error suppression.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-07 22:34:33 +08:00
fengmk2
722a5d70b9 test: fix lint (#819)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
  * Updated linter configuration to relax certain code style rules.
* Upgraded the oxlint development dependency and corrected project URLs
in metadata.
* **Refactor**
* Simplified arrow function syntax in various controllers, services, and
type declarations for improved code readability.
* **Tests**
* Streamlined mock implementations in test files for more concise
syntax.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-07 13:30:52 +08:00
semantic-release-bot
64951041eb Release 4.7.4
[skip ci]

## [4.7.4](https://github.com/cnpm/cnpmcore/compare/v4.7.3...v4.7.4) (2025-06-27)

### Bug Fixes

* remove descending ([#815](https://github.com/cnpm/cnpmcore/issues/815)) ([1001e1f](1001e1ffa2))
2025-06-27 07:49:56 +00:00
elrrrrrrr
1001e1ffa2 fix: remove descending (#815)
> see https://github.com/orgs/community/discussions/152515
-------
* ♻️ 删除 `descending` 参数
------
* ♻️ remove `descending` args in changesStream


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Simplified the process for handling request URLs when fetching data,
removing the explicit addition of certain query parameters. This does
not affect user-facing functionality.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: fengmk2 <fengmk2@gmail.com>
2025-06-27 07:44:01 +00:00
semantic-release-bot
b4cf36e289 Release 4.7.3
[skip ci]

## [4.7.3](https://github.com/cnpm/cnpmcore/compare/v4.7.2...v4.7.3) (2025-06-26)

### Bug Fixes

* latest version maybe not exists ([#814](https://github.com/cnpm/cnpmcore/issues/814)) ([bed4778](bed4778cbc))
2025-06-26 09:50:36 +00:00
fengmk2
bed4778cbc fix: latest version maybe not exists (#814)
```bash
2025-06-26 15:12:05,133 ERROR 423473 [-/127.0.0.1/ea0a01e1-5a66-4191-92c7-f06aa5bb86ea/19.41ms GET /] nodejs.TypeError: [EventBus] process event PACKAGE_MAINTAINER_REMOVED for handler packageVersionAddedSyncESEvent failed: Cannot read properties of undefined (reading 'undefined')
at PackageSearchService.syncPackage (file:///home/admin/application/app/core/service/PackageSearchService.ts:93:45)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at PackageVersionAddedSyncESEvent.syncPackage (file:///home/admin/application/app/core/event/SyncESPackage.ts:29:5)
at PackageVersionAddedSyncESEvent.handle (file:///home/admin/application/app/core/event/SyncESPackage.ts:53:5)
at EventHandlerFactory.handle (file:///home/admin/application/node_modules/_@eggjs_tegg-eventbus-runtime@4.0.0-beta.4@@eggjs/tegg-eventbus-runtime/src/EventHandlerFactory.ts:49:7)
at file:///home/admin/application/node_modules/_@eggjs_tegg-eventbus-runtime@4.0.0-beta.4@@eggjs/tegg-eventbus-runtime/src/SingletonEventBus.ts:151:13
at async Promise.all (index 2)
at file:///home/admin/application/node_modules/_@eggjs_tegg-eventbus-runtime@4.0.0-beta.4@@eggjs/tegg-eventbus-runtime/src/SingletonEventBus.ts:149:9
at EggContextHandler.run (file:///home/admin/application/node_modules/_@eggjs_tegg-plugin@4.0.0-beta.4@@eggjs/tegg-plugin/lib/EggContextHandler.ts:19:12)
at Function.ContextHandler.runInContextCallback (file:///home/admin/application/node_modules/_@eggjs_tegg-plugin@4.0.0-beta.4@@eggjs/tegg-plugin/lib/EggContextHandler.ts:27:14)
```



#### PR Dependency Tree


* **PR #814** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved error handling when package version information is missing,
reducing the chance of runtime errors and providing clearer warning
messages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-06-26 09:44:49 +00:00
fengmk2
5b96443cf1 test: fix lint (#813)
#### PR Dependency Tree


* **PR #813** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Chores**
  - Updated linting configuration to explicitly allow import extensions.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-06-20 07:50:18 +00:00
fengmk2
e4cd535486 chore: enable auto merge 2025-06-20 15:32:41 +08:00
semantic-release-bot
5b98c9dd2b Release 4.7.2
[skip ci]

## [4.7.2](https://github.com/cnpm/cnpmcore/compare/v4.7.1...v4.7.2) (2025-06-18)

### Bug Fixes

* npm stream descending ([#812](https://github.com/cnpm/cnpmcore/issues/812)) ([e932624](e932624c6f))
2025-06-18 09:19:28 +00:00
elrrrrrrr
e932624c6f fix: npm stream descending (#812)
> https://github.com/orgs/community/discussions/152515

* ⚙️ Added descending parameter to npm changesStream

--------

* ⚙️ 为 npm changesStream 添加 descending 参数





<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Chores**
- Updated internal request behavior to fetch changes in descending
order.
- Locked the version of Mocha to 11.6.0 for improved dependency
management.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-06-18 17:18:10 +08:00
fengmk2
69ef574527 chore: use oxlint v1 (#811)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Chores**
  - Updated the development dependency "oxlint" to the latest version.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-06-10 19:42:31 +08:00
semantic-release-bot
5a42764806 Release 4.7.1
[skip ci]

## [4.7.1](https://github.com/cnpm/cnpmcore/compare/v4.7.0...v4.7.1) (2025-06-10)

### Bug Fixes

* should change npm package version ([eaed6fe](eaed6fe478))
2025-06-10 00:59:12 +00:00
fengmk2
eaed6fe478 fix: should change npm package version 2025-06-10 08:56:53 +08:00
semantic-release-bot
8b341d2299 Release 4.7.0
[skip ci]

## [4.7.0](https://github.com/cnpm/cnpmcore/compare/v4.6.3...v4.7.0) (2025-06-10)

### Features

* add shouldNotMerge for task data for skip task merge ([#807](https://github.com/cnpm/cnpmcore/issues/807)) ([490dce3](490dce3ad2))

### Bug Fixes

* content type check ([#809](https://github.com/cnpm/cnpmcore/issues/809)) ([b8c7c06](b8c7c06536)), closes [#693](https://github.com/cnpm/cnpmcore/issues/693)
* order binary by date ([#808](https://github.com/cnpm/cnpmcore/issues/808)) ([12aa425](12aa425c26))
2025-06-10 00:52:26 +00:00
fengmk2
87b6cbedef chore: use oxlint 0.18 (#810)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Chores**
  - Updated the development dependency "oxlint" to the latest version.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-06-10 08:50:55 +08:00
semantic-release-bot
3e395405c1 Release 4.7.0
[skip ci]

## [4.7.0](https://github.com/cnpm/cnpmcore/compare/v4.6.3...v4.7.0) (2025-06-09)

### Features

* add shouldNotMerge for task data for skip task merge ([#807](https://github.com/cnpm/cnpmcore/issues/807)) ([490dce3](490dce3ad2))

### Bug Fixes

* content type check ([#809](https://github.com/cnpm/cnpmcore/issues/809)) ([b8c7c06](b8c7c06536)), closes [#693](https://github.com/cnpm/cnpmcore/issues/693)
* order binary by date ([#808](https://github.com/cnpm/cnpmcore/issues/808)) ([12aa425](12aa425c26))
2025-06-09 12:50:45 +00:00
elrrrrrrr
b8c7c06536 fix: content type check (#809)
> Fix the content-type display logic of showFileController, close #693 

* 🌐 HTML/XML files now render directly (no forced attachment)
* ⚠️ Note: No changes to actual file storage - only affects presentation
layer
* ♻️ New content automatically converts to standardized formats

-------
> 修复 showFileController 相关接口 content-type 展示逻辑, close #693 

* 对于 html,xml 不再默认返回 attachment,改为直接渲染 
* 增量部分默认进行格式转换
* 存量部分在 controller 查看时统一返回




<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Files with `.xml` and `.html` extensions are now served with a
`text/plain` content type instead of their previous MIME types,
improving content handling and consistency.
- The download behavior for HTML and XML files has been updated; these
files are no longer forced as attachments in the browser.
- **Tests**
- Updated tests to reflect the new content type handling for `.xml` and
`.html` files.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-06-09 20:49:29 +08:00
semantic-release-bot
76e3f267c1 Release 4.7.0
[skip ci]

## [4.7.0](https://github.com/cnpm/cnpmcore/compare/v4.6.3...v4.7.0) (2025-05-31)

### Features

* add shouldNotMerge for task data for skip task merge ([#807](https://github.com/cnpm/cnpmcore/issues/807)) ([490dce3](490dce3ad2))

### Bug Fixes

* order binary by date ([#808](https://github.com/cnpm/cnpmcore/issues/808)) ([12aa425](12aa425c26))
2025-05-31 11:39:42 +00:00
killa
12aa425c26 fix: order binary by date (#808)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Improved accuracy in retrieving the latest binary directory by
updating the sorting criterion.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-05-31 19:38:07 +08:00
semantic-release-bot
01f393eb94 Release 4.7.0
[skip ci]

## [4.7.0](https://github.com/cnpm/cnpmcore/compare/v4.6.3...v4.7.0) (2025-05-31)

### Features

* add shouldNotMerge for task data for skip task merge ([#807](https://github.com/cnpm/cnpmcore/issues/807)) ([490dce3](490dce3ad2))
2025-05-31 11:05:43 +00:00
killa
490dce3ad2 feat: add shouldNotMerge for task data for skip task merge (#807)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added support for a new task property to prevent certain tasks from
being merged when waiting, providing more granular control over task
handling.
- **Bug Fixes**
- Improved logic for task merging to consider both task type and a new
optional flag, ensuring correct behavior for historical task
compensation scenarios.
- **Tests**
- Introduced new tests to verify the updated task merging logic and the
effect of the new property.
- **Chores**
- Limited the number of items returned per request when fetching
platform-specific binaries to improve data retrieval efficiency.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-05-31 19:04:13 +08:00
semantic-release-bot
cb3768c82f Release 4.6.3
[skip ci]

## [4.6.3](https://github.com/cnpm/cnpmcore/compare/v4.6.2...v4.6.3) (2025-05-29)

### Bug Fixes

* set puppeteer loop only once ([#805](https://github.com/cnpm/cnpmcore/issues/805)) ([54e3335](54e3335abd))
2025-05-29 13:27:20 +00:00
killa
54e3335abd fix: set puppeteer loop only once (#805) 2025-05-29 21:26:00 +08:00
semantic-release-bot
a1a3859d75 Release 4.6.2
[skip ci]

## [4.6.2](https://github.com/cnpm/cnpmcore/compare/v4.6.1...v4.6.2) (2025-05-29)

### Bug Fixes

* only set lastSyncTime if lastData.lastSyncTime is undefined ([#804](https://github.com/cnpm/cnpmcore/issues/804)) ([1239dc9](1239dc9f49))
2025-05-29 08:00:36 +00:00
killa
1239dc9f49 fix: only set lastSyncTime if lastData.lastSyncTime is undefined (#804)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Improved synchronization logic to prevent overwriting the last sync
time for Chromium browser snapshots if it is already set.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-05-29 15:59:12 +08:00
semantic-release-bot
9efd2932e8 Release 4.6.1
[skip ci]

## [4.6.1](https://github.com/cnpm/cnpmcore/compare/v4.6.0...v4.6.1) (2025-05-29)

### Bug Fixes

* lastSyncTime is string should not stringify again ([#803](https://github.com/cnpm/cnpmcore/issues/803)) ([f6f5314](f6f53149a1))
2025-05-29 07:28:28 +00:00
killa
f6f53149a1 fix: lastSyncTime is string should not stringify again (#803)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Improved handling of the `lastSyncTime` parameter when fetching binary
data to ensure more accurate synchronization.

- **Tests**
- Added a new test case to verify fetching binary data with a provided
`lastSyncTime`.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-05-29 15:27:05 +08:00
semantic-release-bot
2e90486454 Release 4.6.0
[skip ci]

## [4.6.0](https://github.com/cnpm/cnpmcore/compare/v4.5.3...v4.6.0) (2025-05-29)

### Features

* add since for api sync binary ([#802](https://github.com/cnpm/cnpmcore/issues/802)) ([297bd7a](297bd7a745))
2025-05-29 00:12:33 +00:00
killa
297bd7a745 feat: add since for api sync binary (#802) 2025-05-29 08:11:05 +08:00
fengmk2
b8f2ac5f85 test: run es tests on github action (#796)
<!--- SUMMARY_MARKER --->
## Sweep Summary <sub><a href="https://app.sweep.dev"><img
src="https://raw.githubusercontent.com/sweepai/sweep/main/.assets/sweep-square.png"
width="25" alt="Sweep"></a></sub>

Adds Elasticsearch setup to GitHub Actions workflow to enable running
Elasticsearch-dependent tests in CI.

- Added system configuration steps for Elasticsearch in the GitHub
Actions workflow, including swap and sysctl settings.
- Integrated the official Elastic GitHub Action to run Elasticsearch
8.18.0 during CI tests.
- Implemented a wait mechanism to ensure Elasticsearch is fully ready
before proceeding with tests.

---
[Ask Sweep AI questions about this PR](https://app.sweep.dev)
<!--- SUMMARY_MARKER --->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Chores**
- Enhanced CI workflow to configure system limits and start an
Elasticsearch service before running tests. The workflow now waits for
Elasticsearch to be fully ready before proceeding.
- Improved test diagnostics by adding detailed response data to
assertion messages for better debugging.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-05-16 18:15:13 +08:00
semantic-release-bot
c5f1642a1f Release 4.5.3
[skip ci]

## [4.5.3](https://github.com/cnpm/cnpmcore/compare/v4.5.2...v4.5.3) (2025-05-14)

### Bug Fixes

* use new changestream endpoint ([#800](https://github.com/cnpm/cnpmcore/issues/800)) ([2d8bad3](2d8bad3ca2))
2025-05-14 03:52:33 +00:00
fengmk2
2d8bad3ca2 fix: use new changestream endpoint (#800)
should be `https://replicate.npmjs.com/registry` not
`https://replicate.npmjs.com`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Chores**
  - Updated the changes stream registry endpoint configuration.
- Standardized assertion methods in tests to use `assert.ok()` for
improved clarity and consistency across the codebase.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: fengmk2 <fengmk2@gmail.com>
Co-authored-by: elrrrrrrr <elrrrrrrr@gmail.com>
2025-05-14 11:51:10 +08:00
elrrrrrrr
81620e3ed5 refactor: doc_count & doc_version_count perf (#797)
<!--- SUMMARY_MARKER --->
## Sweep Summary <sub><a href="https://app.sweep.dev"><img
src="https://raw.githubusercontent.com/sweepai/sweep/main/.assets/sweep-square.png"
width="25" alt="Sweep"></a></sub>

Improves database performance by replacing expensive count queries with
a dedicated totals table that's updated asynchronously via events.

- Created a new `totals` table in `app/repository/model/Total.ts` to
store package and version counts instead of running expensive SQL count
queries.
- Implemented `TotalRepository` in `app/repository/TotalRepository.ts`
with methods to increment and retrieve count values.
- Added event handlers in `app/core/event/TotalHandler.ts` that listen
for package and version additions to update counts asynchronously.
- Modified `PackageRepository.queryTotal()` to fetch counts from the
totals table instead of executing direct SQL count queries.
- Added migration scripts in `sql/mysql/4.3.0.sql` and
`sql/postgresql/4.3.0.sql` to create the totals table and populate it
with existing data.

---
[Ask Sweep AI questions about this PR](https://app.sweep.dev)
<!--- SUMMARY_MARKER --->

> Fix database performance issues caused by doc_count and
doc_version_count queries

1. 💽 Add a corresponding totals table to record statistical information
2.  Add a `PACKAGE_ADDED` event and the original
`PACKAGE_VERSION_ADDED` event to asynchronously update records in the
table
3. ♻️ Add a new existing data migration script to migrate the original
statistical information to the totals table

-----------

> 修复 doc_count 和 doc_version_count 查询导致的数据库性能问题

1. 💽 新增对应 totals 表,用来记录统计信息
2.  新增 `PACKAGE_ADDED` 事件,和原有 `PACKAGE_VERSION_ADDED` 事件,异步更新表内记录
3. ♻️ 新增存量数据迁移脚本,迁移原有的统计信息到 totals 表

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced persistent tracking of total package and package version
counts, with real-time updates when new packages or versions are added.
- Added new data models and repository methods to manage and retrieve
these total counts.
- Emitted events upon new package creation to update totals
automatically.

- **Database**
- Added a new "totals" table to both MySQL and PostgreSQL databases for
storing aggregate counts initialized from existing data.

- **Bug Fixes**
- Ensured total counts are always returned as numbers in scheduled data
updates.

- **Tests**
- Added and updated tests to verify correct behavior of total count
tracking, incrementing, resetting, and retrieval.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-04-24 14:23:03 +08:00
semantic-release-bot
de3a6153b0 Release 4.5.2
[skip ci]

## [4.5.2](https://github.com/cnpm/cnpmcore/compare/v4.5.1...v4.5.2) (2025-04-17)

### Bug Fixes

* init release ([#795](https://github.com/cnpm/cnpmcore/issues/795)) ([3203c64](3203c64c48)), closes [/github.com/cnpm/cnpm/issues/459#issue-2998106947](https://github.com/cnpm//github.com/cnpm/cnpm/issues/459/issues/issue-2998106947)
2025-04-17 06:38:18 +00:00
elrrrrrrr
3203c64c48 fix: init release (#795)
> close https://github.com/cnpm/cnpm/issues/459#issue-2998106947
1. Correct the initialization of initRelease to ensure the fetch process
is triggered as expected.
------

> close https://github.com/cnpm/cnpm/issues/459#issue-2998106947
1. 🐛 修复 initRelease 时,被错误初始化为 [] 空数组,导致不会触发 fetch 

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Refactor**
- Improved handling of uninitialized or absent release data, ensuring
clearer distinction between missing and empty release lists.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-04-17 14:37:21 +08:00
semantic-release-bot
d6c243cf6b Release 4.5.1
[skip ci]

## [4.5.1](https://github.com/cnpm/cnpmcore/compare/v4.5.0...v4.5.1) (2025-04-15)

### Bug Fixes

* disable information_schema.tables count ([#794](https://github.com/cnpm/cnpmcore/issues/794)) ([0a6eab3](0a6eab325e))
2025-04-15 04:01:05 +00:00
elrrrrrrr
0a6eab325e fix: disable information_schema.tables count (#794)
> Fix potential performance issues caused by using
`information_schema.tables` in distributed databases

1. 📊 `information_schema.tables` has no index, consumes large amounts of
memory, and requires aggregation after being generated per instance.
2. 🚚 Execution plans involve multi-table joins, distributed operations,
GROUP BY, and similar operations.
3. ♻️ Consistently use `SELECT COUNT` with index.
----------

> 修复在分布式 db 中,使用 `information_schema.tables` 可能导致的性能问题
1. 📊 `information_schema.tables` 内部无索引,占用大量内存,需要根据实例数生成后聚合
2. 🚚 执行计划涉及多表连接、分布式操作、GROUP BY 等操作
3. ♻️ 统一使用 `select count`,通过索引计算

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Refactor**
- Unified the method for retrieving total row counts across all
databases for improved consistency.
- **Chores**
  - Updated linting configuration to allow grouped exports.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-04-15 11:59:39 +08:00
elrrrrrrr
a17aed8fb1 chore: custom registry (#793) 2025-04-11 23:05:46 +08:00
semantic-release-bot
770fc7fdaf Release 4.5.0
[skip ci]

## [4.5.0](https://github.com/cnpm/cnpmcore/compare/v4.4.0...v4.5.0) (2025-04-11)

### Features

* use npm new replication api ([#792](https://github.com/cnpm/cnpmcore/issues/792)) ([de97428](de97428ffd))
2025-04-11 13:05:02 +00:00
fengmk2
de97428ffd feat: use npm new replication api (#792)
closes https://github.com/cnpm/cnpmcore/issues/791

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
  - Added an HTTP header to indicate replication opt-in.
- Enhanced logging now shows the results count and last sequence
identifier.

- **Refactor**
- Streamlined the change-fetching process by directly iterating over
response data.

- **Tests**
- Updated test cases with refined assertions and a simplified mocking
strategy to align expectations with the new response format.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-04-11 21:03:36 +08:00
semantic-release-bot
22da5c7f70 Release 4.4.0
[skip ci]

## [4.4.0](https://github.com/cnpm/cnpmcore/compare/v4.3.1...v4.4.0) (2025-04-09)

### Features

* add onnxruntime binary mirror ([#790](https://github.com/cnpm/cnpmcore/issues/790)) ([f4f09c2](f4f09c2d66))
2025-04-09 09:02:40 +00:00
Feng Yu
f4f09c2d66 feat: add onnxruntime binary mirror (#790) 2025-04-09 17:01:12 +08:00
semantic-release-bot
3dce867d3a Release 4.3.1
[skip ci]

## [4.3.1](https://github.com/cnpm/cnpmcore/compare/v4.3.0...v4.3.1) (2025-04-08)

### Bug Fixes

* execute SyncBinaryTask should with data ([#788](https://github.com/cnpm/cnpmcore/issues/788)) ([af3672e](af3672ebdb))
2025-04-08 05:55:10 +00:00
killa
af3672ebdb fix: execute SyncBinaryTask should with data (#788)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Enhanced the task management system to support binary data
synchronization tasks, enabling more precise handling of binary workflow
processes.
- Improved support for binary task execution and logging to ensure data
accuracy during synchronization.

- **Tests**
- Added new test cases to validate the proper execution and data
handling for binary synchronization tasks.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-04-08 13:53:45 +08:00
semantic-release-bot
e89d8528df Release 4.3.0
[skip ci]

## [4.3.0](https://github.com/cnpm/cnpmcore/compare/v4.2.0...v4.3.0) (2025-04-07)

### Features

* mirror astral-sh/python-build-standalone ([#787](https://github.com/cnpm/cnpmcore/issues/787)) ([58a1a9c](58a1a9c2af))
2025-04-07 06:28:18 +00:00
fengmk2
58a1a9c2af feat: mirror astral-sh/python-build-standalone (#787)
closes https://github.com/cnpm/cnpmcore/issues/784

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced a new option for obtaining redistributable Python builds,
making it easier for users to access pre-packaged releases via GitHub.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-04-07 14:26:54 +08:00
semantic-release-bot
ce0fd9d9c2 Release 4.2.0
[skip ci]

## [4.2.0](https://github.com/cnpm/cnpmcore/compare/v4.1.6...v4.2.0) (2025-04-03)

### Features

* **sql:** add gmt_create for binaries index idx_category_parent_gmt_create ([#786](https://github.com/cnpm/cnpmcore/issues/786)) ([080035f](080035f7bc))
2025-04-03 06:48:31 +00:00
killa
080035f7bc feat(sql): add gmt_create for binaries index idx_category_parent_gmt_create (#786)
Include sql change.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Enhanced data processing with additional safeguards to limit
iterations during data retrieval, improving stability and preventing
resource overuse.

- **Chores**
- Refined database indexing by removing an old index and adding a new
one, optimizing query performance for faster data access and a smoother
overall experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-04-03 14:47:12 +08:00
semantic-release-bot
ffd90473f7 Release 4.1.6
[skip ci]

## [4.1.6](https://github.com/cnpm/cnpmcore/compare/v4.1.5...v4.1.6) (2025-04-02)

### Bug Fixes

* iterator chromium bucket to get all versions ([#785](https://github.com/cnpm/cnpmcore/issues/785)) ([facb26d](facb26d192))
2025-04-02 14:46:56 +00:00
killa
facb26d192 fix: iterator chromium bucket to get all versions (#785)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced an admin-protected endpoint for binary synchronization with
support for additional data.
- Added enhanced logic to dynamically retrieve and update the latest
binary versions for multiple platforms.
  
- **Improvements**
- Optimized binary data fetching by migrating to a more dynamic,
cloud-based data source.
  
- **Dependency Management**
- Updated project dependencies to improve XML parsing capabilities while
removing outdated tools.
  
- **Tests**
- Revised test workflows to verify the new synchronization parameters
and data structures.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-04-02 22:45:18 +08:00
zhangyuantao
53060c817a test: vscode test config error (#783) 2025-03-29 19:40:59 +08:00
semantic-release-bot
9f4b8ebb00 Release 4.1.5
[skip ci]

## [4.1.5](https://github.com/cnpm/cnpmcore/compare/v4.1.4...v4.1.5) (2025-03-27)

### Bug Fixes

* 🐛 oss config assert ([#780](https://github.com/cnpm/cnpmcore/issues/780)) ([5c132b8](5c132b882b)), closes [#779](https://github.com/cnpm/cnpmcore/issues/779)
2025-03-27 11:23:53 +00:00
AN Meng
5c132b882b fix: 🐛 oss config assert (#780)
 Closes: #779

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Chores**
- Updated configuration validation to require the OSS bucket setting
instead of a CDN URL, with no changes to public interfaces.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: ANMeng <littleliar.im@qq.com>
2025-03-27 19:22:26 +08:00
fengmk2
a1e1072d1a test: enable more oxlint rules (#778)
- unicorn/prefer-number-properties
- typescript/no-import-type-side-effects
- unicorn/no-array-for-each
- typescript/no-dynamic-delete
- no-empty-function
- import/unambiguous
- max-depth 6
- unicorn/prefer-array-some
- unicorn/prefer-string-slice
- no-lonely-if
- max-nested-callbacks 5
- unicorn/explicit-length-check
- unicorn/no-lonely-if
- no-negated-condition
- no-else-return
- unicorn/prefer-date-now
- typescript/prefer-ts-expect-error
- typescript/ban-ts-comment
- no-throw-literal
- typescript/prefer-enum-initializers
- unicorn/no-typeof-undefined

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Chores**  
- Upgraded dependency version of `oxlint` to `^0.16.0` for improved code
quality.

- **Refactor**  
- Streamlined internal logic for error handling, string manipulation,
and data parsing, enhancing maintainability and performance.
- Simplified conditional logic and iteration methods in various services
and controllers for improved clarity.
- Updated import statements for type clarity and consistency across the
codebase.

- **Tests**  
- Improved test assertions and iteration methods to provide clearer
verification of system reliability.
- Enhanced logging functionality in test cases for better visibility of
output and error handling.
- Modified assertions to ensure more precise requirements in test cases.

These behind-the-scenes enhancements contribute to a more robust and
stable application, ensuring a smoother experience for end-users.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-03-18 23:48:00 +08:00
fengmk2
5d5f40ba26 chore: more oxlint rules (#774) 2025-03-18 00:38:32 +08:00
semantic-release-bot
b322f2c9ec Release 4.1.4
[skip ci]

## [4.1.4](https://github.com/cnpm/cnpmcore/compare/v4.1.3...v4.1.4) (2025-03-16)

### Bug Fixes

* use tegg beta ([#776](https://github.com/cnpm/cnpmcore/issues/776)) ([dd5ee4f](dd5ee4ff30))
2025-03-16 02:50:34 +00:00
fengmk2
dd5ee4ff30 fix: use tegg beta (#776)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Chores**
- Updated dependency versioning where specific beta numbers were
replaced with a broader beta designation. This change streamlines
dependency management and supports smoother integrations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-03-16 10:49:07 +08:00
semantic-release-bot
0c9a515874 Release 4.1.3
[skip ci]

## [4.1.3](https://github.com/cnpm/cnpmcore/compare/v4.1.2...v4.1.3) (2025-03-16)

### Performance Improvements

* refactor AsyncTimer advice to Singleton ([#775](https://github.com/cnpm/cnpmcore/issues/775)) ([884ff50](884ff50a2f))
2025-03-16 01:50:28 +00:00
killa
884ff50a2f perf: refactor AsyncTimer advice to Singleton (#775)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Refactor**
- Streamlined asynchronous operation tracking for improved performance
monitoring.
- Enhanced event handling configuration to provide more consistent and
reliable system behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-03-16 09:48:56 +08:00
fengmk2
1517750513 test: disable oss tests 2025-03-14 20:53:57 +08:00
semantic-release-bot
64beee1ba1 Release 4.1.2
[skip ci]

## [4.1.2](https://github.com/cnpm/cnpmcore/compare/v4.1.1...v4.1.2) (2025-03-14)

### Bug Fixes

* use tsconfig.json for tsc ([#773](https://github.com/cnpm/cnpmcore/issues/773)) ([acb988b](acb988b553))
2025-03-14 12:09:48 +00:00
fengmk2
acb988b553 fix: use tsconfig.json for tsc (#773)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Chores**
- Updated the build command to use revised TypeScript configuration
settings, ensuring a streamlined and consistent build process.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-03-14 20:08:49 +08:00
fengmk2
ffe723e65f refactor: use oxlint instead of eslint (#772)
say goodbye to eslint
2025-03-13 23:31:13 +08:00
semantic-release-bot
ed4d5d07ad Release 4.1.1
[skip ci]

## [4.1.1](https://github.com/cnpm/cnpmcore/compare/v4.1.0...v4.1.1) (2025-03-11)

### Bug Fixes

* don't block tgz ([#770](https://github.com/cnpm/cnpmcore/issues/770)) ([b92354d](b92354d280))
2025-03-11 14:42:50 +00:00
fengmk2
b92354d280 fix: don't block tgz (#770)
revert https://github.com/cnpm/cnpmcore/pull/763

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Refactor**
- Streamlined the package download process by removing outdated package
blocking checks, leading to a smoother and more direct download
experience.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-03-11 22:41:16 +08:00
semantic-release-bot
f51f6028ac Release 4.1.0
[skip ci]

## [4.1.0](https://github.com/cnpm/cnpmcore/compare/v4.0.3...v4.1.0) (2025-03-09)

### Features

* remove codeql-analysis.yml ([3f3751b](3f3751b2d5))
* use tegg v4 ([#769](https://github.com/cnpm/cnpmcore/issues/769)) ([7938919](7938919d81))
2025-03-09 09:30:39 +00:00
fengmk2
3f3751b2d5 feat: remove codeql-analysis.yml 2025-03-09 17:28:49 +08:00
fengmk2
7938919d81 feat: use tegg v4 (#769)
[skip ci]

---------

Co-authored-by: semantic-release-bot <semantic-release-bot@martynus.net>
2025-03-09 17:20:44 +08:00
fengmk2
52c2494355 test: fix unstable tests 2025-03-09 17:11:20 +08:00
fengmk2
0d4fec90dd test: remove oxlint quiet 2025-03-09 16:53:52 +08:00
fengmk2
b8d055a74e test: remove test/common/EnvUtil.test.ts 2025-03-09 16:50:12 +08:00
fengmk2
5c8b024a0a Merge branch 'next' into merge-next 2025-03-09 16:45:43 +08:00
semantic-release-bot
914aee2560 Release 3.75.1
[skip ci]

## [3.75.1](https://github.com/cnpm/cnpmcore/compare/v3.75.0...v3.75.1) (2025-03-09)

### Bug Fixes

* only get packageId from database ([#768](https://github.com/cnpm/cnpmcore/issues/768)) ([bc068d1](bc068d165c))
2025-03-09 02:10:36 +00:00
fengmk2
bc068d165c fix: only get packageId from database (#768)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Refactor**
- Enhanced the internal process for retrieving package version details,
resulting in more accurate and reliable version information.
- Streamlined the handling of missing package data to reduce unexpected
issues.
- Optimized overall data processing, contributing to improved stability
and a smoother user experience.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-03-09 10:09:03 +08:00
semantic-release-bot
9ffb09eaa8 Release 3.75.0
[skip ci]

## [3.75.0](https://github.com/cnpm/cnpmcore/compare/v3.74.2...v3.75.0) (2025-03-09)

### Features

* mirror node-pty-prebuilt-multiarch ([#767](https://github.com/cnpm/cnpmcore/issues/767)) ([cbefb5c](cbefb5c6d0))
2025-03-09 01:56:37 +00:00
时瑾
cbefb5c6d0 feat: mirror node-pty-prebuilt-multiarch (#767)
closes https://github.com/cnpm/cnpmcore/issues/766

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Expanded our binary support to include prebuilt binaries for the
node-pty package, enhancing integration possibilities and deployment
options for users relying on these binaries.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-03-09 09:55:02 +08:00
fengmk2
0a64698ec0 refactor: use tegg v4 (#765) 2025-03-06 00:04:09 +08:00
elrrrrrrr
1922bf2f76 chore: disbale public registration by default (#764)
> The 'allowPublicRegistration' is enabled by default, which my cause
unexpected users registering arbitrarily
1. ⚙ Modify the default configuration 'allowPublicRegistration' to
'false'`

-------------
> 目前默认开启了 `allowPublicRegistration` 配置,公网部署可能会导致预期外的用户任意注册

1. ⚙ 修改默认配置 `allowPublicRegistration` 为 `false`


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Public registration can now be enabled through a new configuration
option, allowing for more flexible user onboarding.
  
- **Bug Fixes**
- Updated the configuration to disallow public registration by default,
ensuring only administrators can log in unless changed.

- **Documentation**
- Added an informational note in the developer documentation regarding
public registration settings.

- **Tests**
- Introduced a setup method to enable public registration before each
test case runs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-02-28 09:34:11 +08:00
semantic-release-bot
47da2f40cf Release 3.74.2
[skip ci]

## [3.74.2](https://github.com/cnpm/cnpmcore/compare/v3.74.1...v3.74.2) (2025-02-27)

### Bug Fixes

* block tgz ([#763](https://github.com/cnpm/cnpmcore/issues/763)) ([3054577](305457777e))
2025-02-27 06:44:47 +00:00
elrrrrrrr
305457777e fix: block tgz (#763)
> The tgz download interface does not check if the package is blocked,
which may pose additional risks for parsing package-lock.json or other
lock files.
[exp](https://registry.npmmirror.com/joker-su/-/joker-su-1.0.0.tgz)
1. 🛡️ Add validation logic for
DownloadPackageVersionTarController#download to check if the package is
allowed to be downloaded.
2. 🧶 Add PackageVersionService#findBlockInfo to check if the
corresponding package is blocked.
3. ♻️ When a single version is blocked, skip check as per the current
manifest logic.

---------

> tgz 下载接口没有判断包是否被 block,对于 package-lock.json
或者其他依赖锁文件解析可能会有额外风险,[exp](https://registry.npmmirror.com/joker-su/-/joker-su-1.0.0.tgz)

1. 🛡️ `DownloadPackageVersionTarController#download` 接口新增校验逻辑,判断是否允许下载
2. 🧶 新增 PackageVersionService#findBlockInfo 判断对应包是否被全局拦截
3. ♻️ 单版本被 block 时,考虑到误封场景,按目前 manifest 逻辑,不在 tgz 下载时进行拦截操作

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Enhanced the package download process with an additional block check.
Now, if a package is flagged, the download will be halted and a clear
error response is returned to inform users of the block.
- Introduced a method to retrieve block information related to package
versions, improving the service's capabilities.

- **Tests**
- Added new test cases to verify the blocking functionality for package
downloads, ensuring the application correctly handles requests for
blocked packages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-02-27 06:43:28 +00:00
Kevin Cui
ae88145317 docs(elk): add missing version field (#760) 2025-02-26 18:01:05 +08:00
semantic-release-bot
069afb98cc Release 4.0.2
[skip ci]

## [4.0.2](https://github.com/cnpm/cnpmcore/compare/v4.0.1...v4.0.2) (2025-02-10)

### Bug Fixes

* should return default value when env is empty string ([#759](https://github.com/cnpm/cnpmcore/issues/759)) ([b6c781e](b6c781ec25))
2025-02-10 11:47:32 +00:00
fengmk2
b6c781ec25 fix: should return default value when env is empty string (#759)
pick from https://github.com/cnpm/cnpmcore/pull/754
2025-02-10 19:46:06 +08:00
semantic-release-bot
05b3b798b6 Release 3.74.1
[skip ci]

## [3.74.1](https://github.com/cnpm/cnpmcore/compare/v3.74.0...v3.74.1) (2025-02-10)

### Bug Fixes

* should return default value when env is empty string ([#758](https://github.com/cnpm/cnpmcore/issues/758)) ([e72e396](e72e396e3c))
2025-02-10 01:28:21 +00:00
fengmk2
e72e396e3c fix: should return default value when env is empty string (#758)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- Refactor
- Enhanced environment variable handling to trim extra whitespace and
improve default value checks for more robust configuration processing.
  
- Tests
- Expanded test coverage to validate default behavior, type conversions,
and error handling for various environment variable scenarios.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-02-10 09:26:47 +08:00
fengmk2
d095d3f48c chore: fix docker demo url 2025-02-09 23:25:05 +08:00
fengmk2
b0f7bf0967 chore: enable elasticsearch on local dev env (#757) 2025-02-09 23:21:49 +08:00
fengmk2
02a1ee35d7 chore: support alpine docker image (#755) 2025-02-09 21:30:37 +08:00
semantic-release-bot
1e9d710b0f Release 4.0.1
[skip ci]

## [4.0.1](https://github.com/cnpm/cnpmcore/compare/v4.0.0...v4.0.1) (2025-02-09)

### Bug Fixes

* remove npm-cli-login for ssri security ([#754](https://github.com/cnpm/cnpmcore/issues/754)) ([d18981e](d18981e658))
2025-02-09 08:40:22 +00:00
fengmk2
d18981e658 fix: remove npm-cli-login for ssri security (#754)
https://github.com/advisories/GHSA-vx3p-948g-6vhq

![image](https://github.com/user-attachments/assets/4eaf45a2-f3da-45ba-9613-c3bcb62f31f2)

https://hub.docker.com/repository/docker/fengmk2/cnpmcore/tags/v4/sha256-38b310438907eec1f00da6f110f6f24c81c85e983833fddd08a31598510154f3
2025-02-09 16:38:55 +08:00
semantic-release-bot
5103409f40 Release 4.0.0
[skip ci]

## [4.0.0](https://github.com/cnpm/cnpmcore/compare/v3.74.0...v4.0.0) (2025-02-09)

### ⚠ BREAKING CHANGES

* only support egg >= 4.0.0

the first app on egg v4

https://github.com/eggjs/egg/issues/3644

### Features

* use egg v4 ([#747](https://github.com/cnpm/cnpmcore/issues/747)) ([4427a4f](4427a4fca5))
2025-02-09 07:48:34 +00:00
fengmk2
ea4823c017 chore: enable auto release on next branch 2025-02-09 15:46:49 +08:00
fengmk2
4427a4fca5 feat: use egg v4 (#747)
BREAKING CHANGE: only support egg >= 4.0.0

the first app on egg v4

https://github.com/eggjs/egg/issues/3644
2025-02-09 15:43:24 +08:00
fengmk2
455fc3a444 chore: start next branch 2025-02-09 15:37:07 +08:00
semantic-release-bot
386974272d Release 3.74.0
[skip ci]

## [3.74.0](https://github.com/cnpm/cnpmcore/compare/v3.73.1...v3.74.0) (2025-02-09)

### Features

* allow to set sourceRegistry by CNPMCORE_CONFIG_SOURCE_REGISTRY ([#753](https://github.com/cnpm/cnpmcore/issues/753)) ([9f4f1f1](9f4f1f1e28))
2025-02-09 03:16:57 +00:00
fengmk2
9f4f1f1e28 feat: allow to set sourceRegistry by CNPMCORE_CONFIG_SOURCE_REGISTRY (#753)
Improve the local development process based on docker-compose

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced a centralized configuration utility that validates
environment variable types and provides fallback defaults.
- **Refactor**
- Standardized environment variable handling across configuration files,
improving maintainability and consistency in system setup.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-02-09 11:15:28 +08:00
semantic-release-bot
aba2b36291 Release 3.73.1
[skip ci]

## [3.73.1](https://github.com/cnpm/cnpmcore/compare/v3.73.0...v3.73.1) (2025-02-08)

### Bug Fixes

* webauth no need to validate hostname on cli request ([#752](https://github.com/cnpm/cnpmcore/issues/752)) ([f1fc249](f1fc2492b6))
2025-02-08 09:34:38 +00:00
fengmk2
f1fc2492b6 fix: webauth no need to validate hostname on cli request (#752) 2025-02-08 17:33:12 +08:00
semantic-release-bot
c23a6699f3 Release 3.73.0
[skip ci]

## [3.73.0](https://github.com/cnpm/cnpmcore/compare/v3.72.1...v3.73.0) (2025-02-07)

### Features

* update playwright DOWNLOAD_PATHS ([#751](https://github.com/cnpm/cnpmcore/issues/751)) ([1850c8b](1850c8b2d4))
2025-02-07 16:09:19 +00:00
fengmk2
1850c8b2d4 feat: update playwright DOWNLOAD_PATHS (#751)
closes https://github.com/cnpm/cnpmcore/issues/750

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Expanded support with additional platform configurations for Ubuntu
24.04 and macOS 15 (including ARM variants).
	- Introduced a new Windows configuration for improved compatibility.
- Updated browser configurations to deliver the latest versions across
Chromium, Firefox, Webkit, FFmpeg, and Android.
- **Tests**
- Refined validation checks and error reporting to ensure consistent and
reliable browser setups.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-02-08 00:08:03 +08:00
semantic-release-bot
c70fdccc04 Release 3.72.1
[skip ci]

## [3.72.1](https://github.com/cnpm/cnpmcore/compare/v3.72.0...v3.72.1) (2025-02-07)

### Bug Fixes

* chromium-headless-shell should under the chromium dir ([#749](https://github.com/cnpm/cnpmcore/issues/749)) ([a580b05](a580b05004))
2025-02-07 15:03:41 +00:00
fengmk2
a580b05004 fix: chromium-headless-shell should under the chromium dir (#749)
closes https://github.com/cnpm/cnpmcore/issues/742

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- New Features
- Improved organization for browser downloads: Secondary browser
components are now integrated under the primary browser category,
resulting in a more intuitive and streamlined binary structure for
users.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-02-07 23:02:11 +08:00
semantic-release-bot
43636bd80b Release 3.72.0
[skip ci]

## [3.72.0](https://github.com/cnpm/cnpmcore/compare/v3.71.3...v3.72.0) (2025-02-07)

### Features

* **mirror:** add chromium-headless-shell ([#748](https://github.com/cnpm/cnpmcore/issues/748)) ([3a3aa81](3a3aa818a3)), closes [#742](https://github.com/cnpm/cnpmcore/issues/742)
2025-02-07 14:12:55 +00:00
Beace
3a3aa818a3 feat(mirror): add chromium-headless-shell (#748)
在内部也遇到了,感觉直接加就行,可以试试看看 @fengmk2 

close #742 

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced enhanced support for the chromium-headless-shell binary
with updated download options across multiple operating systems,
including popular Linux distributions, macOS (with arm64 support), and
Windows. This improvement ensures smoother integration and broader
compatibility for users running different platforms.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: fengmk2 <fengmk2@gmail.com>
2025-02-07 22:11:25 +08:00
semantic-release-bot
4012f584ba Release 3.71.3
[skip ci]

## [3.71.3](https://github.com/cnpm/cnpmcore/compare/v3.71.2...v3.71.3) (2024-12-23)

### Bug Fixes

* incorrect request headers in proxy mode and deleted unparsable cached data ([#719](https://github.com/cnpm/cnpmcore/issues/719)) ([2780c53](2780c532e1))
2024-12-23 06:43:23 +00:00
hezhengxu2018
2780c532e1 fix: incorrect request headers in proxy mode and deleted unparsable cached data (#719)
proxy时因为一个低级的拼写错误没有正确的携带请求头,导致代理模式时返回的数据不正确。但是现在用户发起的请求中的user-agent和x-forwarded等头部信息也没有正确的携带。虽然影响不大但还是想和跑批时更新的请求做一下区分。


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Bug Fixes**
- Enhanced error handling and logging for task execution, improving
traceability.
  
- **Improvements**
- Updated HTTP header access method for better alignment with context
structure.
- Clarified logic for manifest retrieval based on file type, ensuring
correct API usage.
- Streamlined cache handling and response generation logic in package
management.
- Improved method visibility and organization within the cache service
and controller.
- Simplified task creation logic and cache removal processes in the
controller.
- Updated expected outcomes for cache-related operations in the test
cases.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-12-23 14:41:56 +08:00
semantic-release-bot
638a3da767 Release 3.71.2
[skip ci]

## [3.71.2](https://github.com/cnpm/cnpmcore/compare/v3.71.1...v3.71.2) (2024-12-18)

### Bug Fixes

* **search:** author is `???` in result ([#741](https://github.com/cnpm/cnpmcore/issues/741)) ([acffb14](acffb14ea0)), closes [/github.com/npm/cli/pull/7407/files#diff-4bc15933c685fc9a9ce8be0c13a2f067f5e2b3334bacd6664bdfa7ddc46aedb6L58](https://github.com/cnpm//github.com/npm/cli/pull/7407/files/issues/diff-4bc15933c685fc9a9ce8be0c13a2f067f5e2b3334bacd6664bdfa7ddc46aedb6L58) [/github.com/npm/cli/pull/7407/files#diff-4bc15933c685fc9a9ce8be0c13a2f067f5e2b3334bacd6664bdfa7ddc46aedb6R162](https://github.com/cnpm//github.com/npm/cli/pull/7407/files/issues/diff-4bc15933c685fc9a9ce8be0c13a2f067f5e2b3334bacd6664bdfa7ddc46aedb6R162)
2024-12-18 12:47:03 +00:00
Kevin Cui
acffb14ea0 fix(search): author is ??? in result (#741)
### 改动原因

在今年 4 月份,npm cli 对 search 做了[一些改动],在 4 月份以前(npm cli < `v10.6.0`),采用的是
`maintainers` 字段,而在 https://github.com/npm/cli/pull/7407 后,改为使用:
`publisher` 字段。

导致当 npm 版本大于等于 `v10.6.0` 后,search 结果中的 `author` 将变成 `???`,如图:

![CleanShot 2024-12-18 at 16 46
58@2x](https://github.com/user-attachments/assets/4b97bf63-78cb-4720-8c00-469eeff3e92f)

预期的结果应该为:

![CleanShot 2024-12-18 at 16 47
33@2x](https://github.com/user-attachments/assets/2d7fca89-4383-42bd-8b83-66257efe65e8)

### 技术细节说明

当前改动没有为 `es` 增加新的索引,原因是处于以下考虑:

1. es 的 `mapping` 一旦创建,就无法修改(虽然使用了 `dynamic: true` 但无法细粒度的进行控制)
2. 源数据中的 `_npmUser` 已经有相关信息了,没有必要为此浪费额外的磁盘空间
3. 如果想对以前的数据进行更新会比较麻烦,性价比较低

npm cli 老版本:
https://github.com/npm/cli/pull/7407/files#diff-4bc15933c685fc9a9ce8be0c13a2f067f5e2b3334bacd6664bdfa7ddc46aedb6L58
npm cli 新版本:
https://github.com/npm/cli/pull/7407/files#diff-4bc15933c685fc9a9ce8be0c13a2f067f5e2b3334bacd6664bdfa7ddc46aedb6R162

### 其他

相关 PR: https://github.com/cnpm/cnpmcore/pull/513

PTAL @Beace @fengmk2 @elrrrrrrr

[一些改动]: https://github.com/npm/cli/pull/7407

Signed-off-by: Kevin Cui <bh@bugs.cc>
2024-12-18 20:45:32 +08:00
semantic-release-bot
feba680795 Release 3.71.1
[skip ci]

## [3.71.1](https://github.com/cnpm/cnpmcore/compare/v3.71.0...v3.71.1) (2024-12-09)

### Bug Fixes

* ignore npm registry 404 status response on sync process ([#740](https://github.com/cnpm/cnpmcore/issues/740)) ([57226c5](57226c57a6))
2024-12-09 11:29:42 +00:00
fengmk2
57226c57a6 fix: ignore npm registry 404 status response on sync process (#740)
closes https://github.com/cnpm/cnpmcore/issues/739

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Enhanced error handling for package synchronization, including
specific logging for package not found scenarios.
	- Simplified criteria for identifying removed packages.

- **Bug Fixes**
	- Corrected documentation for the `syncMode` property.

- **Chores**
	- Updated dependency versions in `package.json`.

- **Tests**
- Added new test cases and refined existing assertions to improve
logging and error handling verification.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-12-09 19:28:08 +08:00
semantic-release-bot
167e37c241 Release 3.71.0
[skip ci]

## [3.71.0](https://github.com/cnpm/cnpmcore/compare/v3.70.0...v3.71.0) (2024-11-30)

### Features

* sync chrome-for-testing json files ([#737](https://github.com/cnpm/cnpmcore/issues/737)) ([9bb12fd](9bb12fde12))
2024-11-30 14:31:33 +00:00
fengmk2
9bb12fde12 feat: sync chrome-for-testing json files (#737)
closes https://github.com/cnpm/cnpmcore/issues/730

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Enhanced data fetching for Chrome for Testing with new JSON entries
for version management.
  
- **Bug Fixes**
- Improved data validation in tests to ensure correct structure and
content of fetched data.

- **Tests**
- Added assertions to validate properties of fetched items in the
ChromeForTestingBinary tests.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-11-30 22:29:55 +08:00
semantic-release-bot
1d128e280c Release 3.70.0
[skip ci]

## [3.70.0](https://github.com/cnpm/cnpmcore/compare/v3.69.0...v3.70.0) (2024-11-30)

### Features

* support PostgreSQL ([#733](https://github.com/cnpm/cnpmcore/issues/733)) ([f240799](f240799fa2))
2024-11-30 13:58:14 +00:00
fengmk2
f240799fa2 feat: support PostgreSQL (#733)
closes https://github.com/cnpm/cnpmcore/issues/731

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

- **New Features**
- Added support for PostgreSQL alongside MySQL, including a new database
setup script and comprehensive documentation for local development.
- Introduced a new CI job for PostgreSQL testing in the GitHub Actions
workflow.
- Enhanced the `README.md` and `DEVELOPER.md` files to provide clearer
instructions for using both database systems.
- Added new environment variable configurations for PostgreSQL in the
Docker deployment documentation.

- **Bug Fixes**
- Improved error handling in tests for duplicate entries to accommodate
both MySQL and PostgreSQL error messages.

- **Documentation**
- Updated setup instructions for PostgreSQL and clarified MySQL setup in
the documentation.
	- Enhanced contributor information in the README.
- Expanded instructions for setting up Elasticsearch and Kibana,
including environment variable configurations.

- **Chores**
- Updated package dependencies to include PostgreSQL client libraries
and modified scripts to support both databases.
	- Changed the base image in the Dockerfile to a newer Node.js version.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-11-30 21:56:45 +08:00
semantic-release-bot
dd15b08fa2 Release 3.69.0
[skip ci]

## [3.69.0](https://github.com/cnpm/cnpmcore/compare/v3.68.0...v3.69.0) (2024-11-30)

### Features

* mirror deno ([#736](https://github.com/cnpm/cnpmcore/issues/736)) ([6de0876](6de0876d35))
2024-11-30 08:36:07 +00:00
fengmk2
6de0876d35 feat: mirror deno (#736)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced support for the Deno runtime, providing users with a modern
runtime for JavaScript and TypeScript.
- **Documentation**
- Updated configuration to include Deno, ensuring users can easily
access and utilize this new option.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-11-30 16:34:36 +08:00
semantic-release-bot
63a8473af7 Release 3.68.0
[skip ci]

## [3.68.0](https://github.com/cnpm/cnpmcore/compare/v3.67.1...v3.68.0) (2024-11-30)

### Features

* enable allowH2 by default and require Node.js >= 18.20.0 ([#734](https://github.com/cnpm/cnpmcore/issues/734)) ([9b01383](9b01383210))
2024-11-30 07:54:28 +00:00
fengmk2
9b01383210 feat: enable allowH2 by default and require Node.js >= 18.20.0 (#734)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

- **New Features**
- Enhanced error handling across various services by introducing a
centralized timeout error checking function.
	- HTTP/2 support enabled in the HTTP client configuration.

- **Bug Fixes**
	- Corrected a typographical error in comments for better clarity.

- **Documentation**
	- Updated Node.js version requirements in the project configuration.

- **Tests**
- Improved test cases for `NpmChangesStream` and `TaskRepository` to
ensure accurate behavior and performance.

- **Chores**
	- Updated Node.js version in CI workflow for more precise testing.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-11-30 15:53:00 +08:00
fengmk2
b808ebcd60 chore: security support >= 3.0.0 (#732)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Documentation**
- Updated the "Supported Versions" section in the SECURITY.md file to
reflect that security updates are now provided for versions 3.0.0 and
above.
- Minor formatting adjustments made in the "Disclosure Policy" section
for consistency.
- **Chores**
- Simplified debugger configurations by removing explicit protocol and
port settings from the `.vscode/launch.json` file.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: elrrrrrrr <elrrrrrrr@gmail.com>
2024-11-24 12:59:15 +08:00
semantic-release-bot
71cc3381d7 Release 3.67.1
[skip ci]

## [3.67.1](https://github.com/cnpm/cnpmcore/compare/v3.67.0...v3.67.1) (2024-11-14)

### Bug Fixes

* adapter new html format on nodejs.org/dist ([#728](https://github.com/cnpm/cnpmcore/issues/728)) ([914b59c](914b59c7ef))
2024-11-14 15:14:40 +00:00
fengmk2
914b59c7ef fix: adapter new html format on nodejs.org/dist (#728)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Enhanced file fetching capabilities with updated logic to handle a
broader range of file formats and attributes.
- Introduced a new index HTML page for Node.js distribution version
18.15.0, listing downloadable files with metadata.

- **Bug Fixes**
- Improved regex for matching HTML anchor tags to accurately capture
additional file types and structures.

- **Tests**
- Added new test cases for the `fetch()` method to verify functionality
against the Node.js distribution version 18.15.0.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-11-14 23:13:03 +08:00
semantic-release-bot
ac4709a7be Release 3.67.0
[skip ci]

## [3.67.0](https://github.com/cnpm/cnpmcore/compare/v3.66.0...v3.67.0) (2024-11-09)

### Features

* say goodbye to alinode ([#726](https://github.com/cnpm/cnpmcore/issues/726)) ([99a5ef1](99a5ef1715))
2024-11-09 07:09:12 +00:00
fengmk2
99a5ef1715 feat: say goodbye to alinode (#726)
> getaddrinfo ENOTFOUND alinode.aliyun.com

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced a new property to disable the `alinode` binary, affecting
its availability in the application.

- **Improvements**
- Updated the `ignoreDownloadStatuses` for the `python` binary to
enhance type safety, ensuring proper adherence to expected data types.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-11-09 15:07:44 +08:00
semantic-release-bot
2edbec6008 Release 3.66.0
[skip ci]

## [3.66.0](https://github.com/cnpm/cnpmcore/compare/v3.65.0...v3.66.0) (2024-11-03)

### Features

* compatible verdaccio path style ([#723](https://github.com/cnpm/cnpmcore/issues/723)) ([7158e66](7158e66c9f))
2024-11-03 12:37:16 +00:00
ZhengJin
7158e66c9f feat: compatible verdaccio path style (#723)
兼容Verdaccio下载地址风格,镜像库从Verdaccio切换至cnpmcore后无需大面积调整lock文件

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced a comprehensive test suite for the download functionality,
ensuring robust behavior for various package retrieval scenarios.
- Enhanced validation for CORS requests and CDN redirection during
package downloads.
- Added new test cases for downloading version tarballs with scoped
package handling.

- **Bug Fixes**
- Improved error handling for non-existent packages and versions,
providing clearer error messages.
- Ensured proper handling of deprecated download paths and
scoped/non-scoped package names.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-11-03 20:35:45 +08:00
semantic-release-bot
4facf90ae0 Release 3.65.0
[skip ci]

## [3.65.0](https://github.com/cnpm/cnpmcore/compare/v3.64.0...v3.65.0) (2024-10-26)

### Features

* strict validate deps ([#720](https://github.com/cnpm/cnpmcore/issues/720)) ([12650ac](12650acf72))
2024-10-26 11:59:49 +00:00
elrrrrrrr
12650acf72 feat: strict validate deps (#720)
> During the sync process, dependency installation errors may occur due
to incorrect timing or incorrect declaration.
1. ⚙️ Added `strictValidatePackageDeps` configuration, disabled by
default.
2. 🔄 In publish process, if the dependencies for the current version do
not exist, it will be interrupted.
3. 📦 In synch process, will automatically enter the queue to wait for
the next synchronization attempt (up to 3 retries).
4. ♻️ Packages that have already been published or synchronized will not
be affected.

-----------

> 在版本同步时,由于同步时机或自身依赖声明错误,导致依赖安装报错。
1. ⚙️ 新增 `strictValidatePackageDeps` 配置,默认关闭
2. 🔄 在包发布时,如果当前版本的 `dependencies` 不存在,则中断发布
3. 📦 在包同步时,如果校验未通过,则中断发布流程,自动进入队列等待下次同步(最多重试3次)
4. ♻️ 已发布、同步的包不受影响

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

- **New Features**
- Introduced strict validation for package dependencies during
publishing and synchronization.
- Added a new configuration option for enforcing dependency validation.

- **Bug Fixes**
- Enhanced error handling for dependency validation failures, allowing
for task retries.

- **Tests**
- Added new test cases to ensure proper validation of package
dependencies under strict settings.
- Created a new test suite for handling invalid dependencies in package
synchronization.

- **Chores**
- Updated logging for package synchronization processes to improve
clarity and error tracking.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-10-26 19:58:07 +08:00
semantic-release-bot
2b812a161e Release 3.64.0
[skip ci]

## [3.64.0](https://github.com/cnpm/cnpmcore/compare/v3.63.1...v3.64.0) (2024-10-18)

### Features

* mirror protobuf binary ([#717](https://github.com/cnpm/cnpmcore/issues/717)) ([d6f0e1d](d6f0e1d866))
2024-10-18 15:35:28 +00:00
akitaSummer
d6f0e1d866 feat: mirror protobuf binary (#717)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added support for Protocol Buffers with a new binary entry in the
configuration.

- **Tests**
- Introduced a new test case to verify the fetching of release data for
the Protocol Buffers repository from GitHub.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: shixia.ly <shixia.ly@antgroup.com>
2024-10-18 23:33:39 +08:00
semantic-release-bot
efac8a97e2 Release 3.63.1
[skip ci]

## [3.63.1](https://github.com/cnpm/cnpmcore/compare/v3.63.0...v3.63.1) (2024-10-13)

### Bug Fixes

* change skia-canvas to github release ([#715](https://github.com/cnpm/cnpmcore/issues/715)) ([99a8660](99a86600db))
2024-10-13 03:22:57 +00:00
fengmk2
99a86600db fix: change skia-canvas to github release (#715)
closes https://github.com/cnpm/cnpmcore/issues/710

pick from https://github.com/cnpm/cnpmcore/pull/712

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Updated the `binaries` configuration to include new entries and
modified existing ones, enhancing the variety and sources of available
binaries.
  
- **Bug Fixes**
- Improved the test coverage for the `GithubBinary` class with a new
test case to ensure proper fetching of `skia-canvas` release data from
GitHub.

- **Refactor**
- Removed outdated test cases related to the `skia-canvas` package from
the `NodePreGypBinary` test suite, streamlining the testing process.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: ltxhhz <ltxhhz@qq.com>
2024-10-13 11:21:31 +08:00
fengmk2
b0cd0ba387 chore: change 3.63.0.sql (#714) 2024-10-13 10:41:07 +08:00
semantic-release-bot
d987bf4a55 Release 3.63.0
[skip ci]

## [3.63.0](https://github.com/cnpm/cnpmcore/compare/v3.62.2...v3.63.0) (2024-10-13)

### Features

* proxy mode [sql changed] ([#571](https://github.com/cnpm/cnpmcore/issues/571)) ([91aea0f](91aea0f106))
2024-10-13 02:23:31 +00:00
hezhengxu2018
91aea0f106 feat: proxy mode [sql changed] (#571)
closes https://github.com/cnpm/cnpmcore/issues/366

开启代理模式时如果找不到依赖会直接返回上游仓库的manifest信息并缓存于nfs,当请求的tgz文件不存在时从上游仓库获取并返回,同时创建对应版本的同步任务。每小时检查更新已缓存的manifest文件保证上游仓库发布新版本时不会因为缓存落后而404。

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced proxy cache management for package manifests and versions.
  - Added new HTTP methods for managing proxy caches.
- Implemented scheduled workers for updating and synchronizing proxy
cache.

- **Updates**
  - Expanded `SyncMode` enum to include a new value `proxy`.
- Updated constants with `PROXY_CACHE_DIR_NAME` and
`ABBREVIATED_META_TYPE`.

- **Tests**
- Added comprehensive test cases for `ProxyCacheService`,
`ProxyCacheRepository`, and related controllers.
- Verified functionality of scheduled workers for proxy cache updates
and synchronization.
- Enhanced testing coverage for handling package downloads in proxy
mode.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: fengmk2 <suqian.yf@antgroup.com>
2024-10-13 10:21:57 +08:00
fengmk2
75d3a66b5c chore: brew install mysql v9 (#713)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Documentation**
- Updated setup instructions to reflect the requirement for MySQL 9,
removing references to MySQL 5.7 and 8.
- Removed outdated troubleshooting information related to MySQL 8
authentication issues.

- **Chores**
- Updated `.gitignore` to include `.egg/` and improved formatting for
`.DS_Store`.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-10-12 09:48:46 +08:00
semantic-release-bot
44ca113931 Release 3.62.2
[skip ci]

## [3.62.2](https://github.com/cnpm/cnpmcore/compare/v3.62.1...v3.62.2) (2024-10-10)

### Bug Fixes

* dup clz name ([#711](https://github.com/cnpm/cnpmcore/issues/711)) ([f7c49e5](f7c49e55fa))
2024-10-10 11:18:41 +00:00
elrrrrrrr
f7c49e55fa fix: dup clz name (#711)
> https://github.com/eggjs/tegg/pull/242/files 
* 🤖 Updated the duplicate class name, fix the error when dev.
------
> https://github.com/eggjs/tegg/pull/242/files 
* 🤖 修改同名 class 名称定义,修复本地启动报错 

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Enhanced clarity in event handling by renaming various package-related
event classes for better understanding.

- **Bug Fixes**
- Updated test cases to reflect the renamed event classes, ensuring
accurate functionality in the testing framework.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-10-10 17:04:59 +08:00
semantic-release-bot
e6ed2215a4 Release 3.62.1
[skip ci]

## [3.62.1](https://github.com/cnpm/cnpmcore/compare/v3.62.0...v3.62.1) (2024-09-25)

### Reverts

* Revert "feat: set allowH2 to true and require Node.js >= 18 (#705)" (#707) ([526b66a](526b66a93c)), closes [#705](https://github.com/cnpm/cnpmcore/issues/705) [#707](https://github.com/cnpm/cnpmcore/issues/707)
2024-09-25 10:37:30 +00:00
fengmk2
526b66a93c Revert "feat: set allowH2 to true and require Node.js >= 18 (#705)" (#707)
This reverts commit 9a7994090b.


![image](https://github.com/user-attachments/assets/eeb9ea95-60ec-4bcf-a695-60be303e2f5f)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
	- Updated HTTP client configuration to enhance compatibility.
	- Adjusted minimum Node.js version requirement for broader support.

- **Bug Fixes**
	- Removed HTTP/2 support from the HTTP client configuration.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-09-25 18:36:04 +08:00
semantic-release-bot
0a0c4e72ae Release 3.62.0
[skip ci]

## [3.62.0](https://github.com/cnpm/cnpmcore/compare/v3.61.6...v3.62.0) (2024-09-22)

### Features

* set allowH2 to true and require Node.js >= 18 ([#705](https://github.com/cnpm/cnpmcore/issues/705)) ([9a79940](9a7994090b))
2024-09-22 15:49:18 +00:00
fengmk2
9a7994090b feat: set allowH2 to true and require Node.js >= 18 (#705)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced a new configuration option allowing the use of HTTP/2 in
the HTTP client.
  
- **Updates**
- Updated the minimum required Node.js version to 18.20.0 for improved
performance and compatibility.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-09-22 23:47:37 +08:00
semantic-release-bot
c1de249445 Release 3.61.6
[skip ci]

## [3.61.6](https://github.com/cnpm/cnpmcore/compare/v3.61.5...v3.61.6) (2024-07-11)

### Bug Fixes

* ignore hook when disable ([#702](https://github.com/cnpm/cnpmcore/issues/702)) ([bd49917](bd49917b86))
2024-07-11 13:28:17 +00:00
elrrrrrrr
bd49917b86 fix: ignore hook when disable (#702)
> Currently, `triggerHookWorkerMaxConcurrentTasks` is 10 by defualt,
which can lead to some redis queries even hookEnable is not activated.
* ♻️ Follow `CreateTriggerHookWorker`, when hookEnable is not activated,
do not query task queue.
-------

> 目前 triggerHookWorkerMaxConcurrentTasks 默认为 10,在未开启 hookEnable
时会带来一些冗余的 redis 查询
* ♻️ 参照 `CreateTriggerHookWorker` 逻辑,hookEnable 关闭时,不进行存量任务轮训

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Added a check to ensure hooks are enabled before proceeding, improving
reliability and preventing errors when hooks are disabled.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-07-11 21:26:52 +08:00
semantic-release-bot
381a10cd6e Release 3.61.5
[skip ci]

## [3.61.5](https://github.com/cnpm/cnpmcore/compare/v3.61.4...v3.61.5) (2024-06-25)

### Bug Fixes

* auto remove blocked package on sync process ([#700](https://github.com/cnpm/cnpmcore/issues/700)) ([ca6ce4e](ca6ce4e860))
2024-06-25 12:30:37 +00:00
elrrrrrrr
ca6ce4e860 fix: auto remove blocked package on sync process (#700)
> remove es index when the target pkg has been blocked or deleted.
1.  update `SyncESPackage` logic for event register
2. ♻️ do the block check when `syncPackage` 
---------
> 包删除或被 block 时,es 索引同步删除
1.  修改 `SyncESPackage` 逻辑,更新事件注册
2. ♻️ `syncPackage` 时,重新判断包是否被 block,用于清理

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced event types `PACKAGE_BLOCKED` and `PACKAGE_UNBLOCKED` for
better package management.

- **Bug Fixes**
- Improved logic to handle package blocks before syncing, ensuring
smoother operations.

- **Tests**
- Added test scenarios to cover new package blocking and unblocking
features, enhancing reliability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-06-25 20:28:55 +08:00
semantic-release-bot
571d265065 Release 3.61.4
[skip ci]

## [3.61.4](https://github.com/cnpm/cnpmcore/compare/v3.61.3...v3.61.4) (2024-06-14)

### Bug Fixes

* perf diff ([#699](https://github.com/cnpm/cnpmcore/issues/699)) ([753e519](753e519f17)), closes [#698](https://github.com/cnpm/cnpmcore/issues/698)
2024-06-14 01:43:06 +00:00
elrrrrrrr
753e519f17 fix: perf diff (#699)
> optimize binary sync perf , closes #698 
* ♻️ calculate the latestItem only once for the same fetchItems.
--------
> binary 同步性能优化,close #698

* ♻️ binary 最新版本比对时,相同 fetchItems 仅计算一次。

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Bug Fixes**
- Improved logic for identifying the latest item in synchronization
processes, ensuring more accurate date comparisons.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-06-14 09:41:43 +08:00
elrrrrrrr
038736dd60 chore: trends api (#697)
* 📚 add doc for the trends API.
---------
* 📚 添加 trends api 相关文档


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Documentation**
- Added sections for `[Token]` and `[Trend]` in the Registry API
documentation.
- Introduced a new endpoint `GET /downloads/range/:start::end/:pkgName`
for fetching package download trends.
	- Included sample response data for the `Trend` endpoint.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-06-03 22:51:46 +08:00
semantic-release-bot
317e24da55 Release 3.61.3
[skip ci]

## [3.61.3](https://github.com/cnpm/cnpmcore/compare/v3.61.2...v3.61.3) (2024-05-30)

### Bug Fixes

* force detect unpkg-white-list ([#695](https://github.com/cnpm/cnpmcore/issues/695)) ([9664504](9664504151))
2024-05-30 06:22:04 +00:00
fengmk2
9664504151 fix: force detect unpkg-white-list (#695) 2024-05-30 14:20:48 +08:00
semantic-release-bot
dcc5509dac Release 3.61.2
[skip ci]

## [3.61.2](https://github.com/cnpm/cnpmcore/compare/v3.61.1...v3.61.2) (2024-05-30)

### Bug Fixes

* support + wildcast ([#694](https://github.com/cnpm/cnpmcore/issues/694)) ([c8f5ee8](c8f5ee82f1)), closes [#692](https://github.com/cnpm/cnpmcore/issues/692)
2024-05-30 03:53:37 +00:00
elrrrrrrr
c8f5ee82f1 fix: support + wildcast (#694)
> Update #692, Declare compatibility using + notation

1. 🤖 Ensure version matching by `semver`
---------

> 更新 #692 , 兼容版本声明为 `+` 的场景
1. 🤖 统一通过 `semver` 进行版本匹配判断

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added support for prerelease versions when checking package version
compatibility.

- **Tests**
- Updated test cases to include operations related to a new object `baz`
with version `*`.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-05-30 11:51:56 +08:00
semantic-release-bot
cacf5e9da3 Release 3.61.1
[skip ci]

## [3.61.1](https://github.com/cnpm/cnpmcore/compare/v3.61.0...v3.61.1) (2024-05-30)

### Bug Fixes

* rc version should match `*` version ([#692](https://github.com/cnpm/cnpmcore/issues/692)) ([0b62238](0b6223882e))
2024-05-30 01:35:17 +00:00
fengmk2
0b6223882e fix: rc version should match * version (#692)
closes https://github.com/cnpm/unpkg-white-list/issues/63

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Improved package version checking to support wildcard (`*`) versions,
ensuring better compatibility and flexibility.
- Fixed issues in handling release candidate (rc) versions in package
version checks.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-05-30 09:33:51 +08:00
semantic-release-bot
9beaf4164c Release 3.61.0
[skip ci]

## [3.61.0](https://github.com/cnpm/cnpmcore/compare/v3.60.0...v3.61.0) (2024-05-25)

### Features

* sync missing acceptDependencies on abbreviated format ([#691](https://github.com/cnpm/cnpmcore/issues/691)) ([96648fd](96648fddaf))
2024-05-25 06:59:41 +00:00
fengmk2
96648fddaf feat: sync missing acceptDependencies on abbreviated format (#691)
closes https://github.com/cnpm/cnpmcore/issues/689

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added support for `acceptDependencies`, `directories`, and `funding`
metadata in package management.

- **Bug Fixes**
  - Improved package metadata synchronization to include new keys.

- **Tests**
- Added new test cases to verify `acceptDependencies` metadata syncing.

- **Chores**
- Updated import statements to use the `node` namespace for better
compatibility.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-05-25 14:57:59 +08:00
semantic-release-bot
6f9f8abc16 Release 3.60.0
[skip ci]

## [3.60.0](https://github.com/cnpm/cnpmcore/compare/v3.59.1...v3.60.0) (2024-05-24)

### Features

* mirror homebrew-macos-cross-toolchains ([#690](https://github.com/cnpm/cnpmcore/issues/690)) ([a247065](a2470650d5))
2024-05-24 02:57:17 +00:00
fengmk2
a2470650d5 feat: mirror homebrew-macos-cross-toolchains (#690)
https://github.com/messense/homebrew-macos-cross-toolchains/releases

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added support for `homebrew-macos-cross-toolchains` in the binaries
list, enhancing cross-toolchain capabilities for macOS users.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-05-24 10:55:27 +08:00
semantic-release-bot
8b1f526966 Release 3.59.1
[skip ci]

## [3.59.1](https://github.com/cnpm/cnpmcore/compare/v3.59.0...v3.59.1) (2024-05-19)

### Bug Fixes

* files meta only return currrent files and sub directory ([#687](https://github.com/cnpm/cnpmcore/issues/687)) ([e442580](e442580b81))
2024-05-19 02:00:04 +00:00
fengmk2
e442580b81 fix: files meta only return currrent files and sub directory (#687)
closes https://github.com/cnpm/cnpmcore/issues/680

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Improved file and directory handling to ensure accurate listing and
filtering of package version files.

- **Tests**
- Updated test cases to reflect the new logic for file and directory
handling, ensuring more reliable and accurate test results.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-05-19 09:58:14 +08:00
semantic-release-bot
8a927fcc2d Release 3.59.0
[skip ci]

## [3.59.0](https://github.com/cnpm/cnpmcore/compare/v3.58.1...v3.59.0) (2024-05-18)

### Features

* add unpkg-white-list to detect sync unpkg files or not ([#686](https://github.com/cnpm/cnpmcore/issues/686)) ([0530116](05301166a2))
2024-05-18 05:13:07 +00:00
fengmk2
05301166a2 feat: add unpkg-white-list to detect sync unpkg files or not (#686)
see https://github.com/cnpm/unpkg-white-list

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit


- **New Features**
- Introduced a new configuration option `enableSyncUnpkgFilesWhiteList`
to enhance package version file synchronization.

- **Improvements**
- Enhanced logging in package version file operations for better
traceability.
- Simplified file redirection logic for improved performance and
readability.

- **Tests**
- Added test cases for the new `enableSyncUnpkgFilesWhiteList`
configuration to ensure reliability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-05-18 13:11:22 +08:00
fengmk2
c5c6145fda chore: update contributors
[skip ci]
2024-05-17 22:32:11 +08:00
semantic-release-bot
3383d7f403 Release 3.58.1
[skip ci]

## [3.58.1](https://github.com/cnpm/cnpmcore/compare/v3.58.0...v3.58.1) (2024-05-17)

### Bug Fixes

* remove CVE-2023-46809 revert config ([#683](https://github.com/cnpm/cnpmcore/issues/683)) ([ff00e42](ff00e42668))
2024-05-17 14:29:38 +00:00
fengmk2
ff00e42668 fix: remove CVE-2023-46809 revert config (#683)
revert https://github.com/cnpm/cnpmcore/pull/650

run test on Node.js 22

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Chores**
  - Updated Node.js version support to 18, 20, and 22.
  - Removed unnecessary configuration from the release workflow.
- Cleaned up outdated security-related entries in the package
configuration.
  - Updated encryption functions in CryptoUtil.ts for improved security.
- Refactored import statements for ES module syntax in
webauthController.test.ts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2024-05-17 22:27:55 +08:00
semantic-release-bot
ade9305342 Release 3.58.0
[skip ci]

## [3.58.0](https://github.com/cnpm/cnpmcore/compare/v3.57.0...v3.58.0) (2024-05-16)

### Features

* support unpkg alias path access entry file [#674](https://github.com/cnpm/cnpmcore/issues/674) ([#675](https://github.com/cnpm/cnpmcore/issues/675)) ([a51891d](a51891d3b9))
2024-05-16 15:48:28 +00:00
chilingling
a51891d3b9 feat: support unpkg alias path access entry file #674 (#675)
closes https://github.com/cnpm/cnpmcore/issues/674

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Implemented a feature to handle compatibility with unpkg by searching
for and redirecting to possible file entries if the requested file is
not found.

- **Tests**
- Added a new test case to ensure the redirection to possible file
entries functions correctly.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-05-16 23:46:51 +08:00
elrrrrrrr
65d6f4489f chore: add sync-setup (#666)
> close #664 Missing a startup document for sync pkgs.
* 📚 Add doc for sync-setup

-------

> close #664 目前缺少一个启动文档,开启包同步服务
* 📚 添加 sync-setup 使用文档

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Documentation**
- Added a new guide for setting up package synchronization services
using `cnpmcore`.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-05-16 13:26:33 +08:00
semantic-release-bot
8366ee70a4 Release 3.57.0
[skip ci]

## [3.57.0](https://github.com/cnpm/cnpmcore/compare/v3.56.2...v3.57.0) (2024-05-14)

### Features

* update playwright config ([#681](https://github.com/cnpm/cnpmcore/issues/681)) ([6bfbe35](6bfbe35c65))
2024-05-14 16:42:10 +00:00
elrrrrrrr
6bfbe35c65 feat: update playwright config (#681)
> follow
[ref](https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/registry/index.ts)
update
* 🔧 Modify Playwright `DOWNLOAD_PATHS` configuration 
* 🔧 Add `android` related configuration 
* ♻️ Remove `chromium-with-symbols` configuration and `ubuntu18.04`
related versions

---------

> 参照
[ref](https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/registry/index.ts)
变更
* 🔧 修改 playwright `DOWNLOAD_PATHS` 相关配置
* 🔧 添加 `android` 相关配置
* ♻️ 移除 `chromium-with-symbols` 配置,`ubuntu18.04` 相关版本




<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added support for `mac14` and `mac14-arm64` platforms for Chromium and
Chromium tip-of-tree downloads.

- **Bug Fixes**
- Updated download paths for various platforms to ensure compatibility
and correct file retrieval.

- **Tests**
- Adjusted test assertions to reflect changes in download paths and
platform support, ensuring accurate validation.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-05-15 00:40:37 +08:00
semantic-release-bot
cedb959f65 Release 3.56.2
[skip ci]

## [3.56.2](https://github.com/cnpm/cnpmcore/compare/v3.56.1...v3.56.2) (2024-05-14)

### Bug Fixes

* allow to disable sync unpkg files ([#679](https://github.com/cnpm/cnpmcore/issues/679)) ([101c9b3](101c9b30b5))
2024-05-14 06:47:57 +00:00
fengmk2
101c9b30b5 fix: allow to disable sync unpkg files (#679)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added a new configuration option `enableSyncUnpkgFiles` for enhanced
synchronization control.
  
- **Improvements**
- Improved synchronization logic to check both `enableUnpkg` and
`enableSyncUnpkgFiles` settings before proceeding.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-05-14 14:46:26 +08:00
semantic-release-bot
cdca770a0b Release 3.56.1
[skip ci]

## [3.56.1](https://github.com/cnpm/cnpmcore/compare/v3.56.0...v3.56.1) (2024-05-14)

### Bug Fixes

* only set err.status on statusCode >= 200 ([#677](https://github.com/cnpm/cnpmcore/issues/677)) ([668eed2](668eed2d50))
2024-05-14 02:30:02 +00:00
fengmk2
668eed2d50 fix: only set err.status on statusCode >= 200 (#677)
> nodejs.AssertionError: invalid status code: -1

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Improved error handling to ensure the HTTP status code is valid and
within the correct range.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-05-14 10:28:31 +08:00
semantic-release-bot
dbf5b5248a Release 3.56.0
[skip ci]

## [3.56.0](https://github.com/cnpm/cnpmcore/compare/v3.55.1...v3.56.0) (2024-05-11)

### Features

* mirror edgewebdriver ([#676](https://github.com/cnpm/cnpmcore/issues/676)) ([21cbc18](21cbc1849f))
2024-05-11 10:11:05 +00:00
fengmk2
21cbc1849f feat: mirror edgewebdriver (#676)
closes https://github.com/cnpm/cnpmcore/issues/594

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced the EdgedriverBinary class to enhance the fetching and
management of Edgedriver binaries.
- Added support for the Edgedriver type in our binary management system.
- Updated the binaries configuration to include the new Edgedriver
category, complete with repository and distribution details.

- **Documentation**
- Updated documentation to reflect the addition of the Edgedriver
category and its functionalities.

- **Tests**
- Implemented new tests for the EdgedriverBinary class to ensure
functionality works as expected.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-05-11 18:09:27 +08:00
semantic-release-bot
67f1a2476d Release 3.55.1
[skip ci]

## [3.55.1](https://github.com/cnpm/cnpmcore/compare/v3.55.0...v3.55.1) (2024-04-17)

### Bug Fixes

* add logs fro aggregate errors ([#672](https://github.com/cnpm/cnpmcore/issues/672)) ([c0f96d7](c0f96d72e5))
2024-04-17 13:18:19 +00:00
elrrrrrrr
c0f96d72e5 fix: add logs fro aggregate errors (#672)
> add logs for sync tasks with the upstream registry errors
[ref](https://cdn.npmmirror.com/packages/%40eggjs/tegg-schedule-plugin/syncs/2024/04/171901-661fac1a613c4b7bd1e015e3.log)
* 📒 Log the corresponding errors for AggregateError

--------------

> 对 upstream registry 创建同步任务失败时,添加日志信息
[ref](https://cdn.npmmirror.com/packages/%40eggjs/tegg-schedule-plugin/syncs/2024/04/171901-661fac1a613c4b7bd1e015e3.log)
* 📒 针对 AggregateError 打印对应 errors



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit


- **Documentation**
- Updated comments in the package synchronization service to include
information about handling `AggregateError` exceptions.
- **New Features**
- Added a retry mechanism with a limit of 3 attempts in the HTTP
requests for the NPMRegistry class.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-04-17 21:16:26 +08:00
semantic-release-bot
abad15b8e0 Release 3.55.0
[skip ci]

## [3.55.0](https://github.com/cnpm/cnpmcore/compare/v3.54.0...v3.55.0) (2024-04-15)

### Features

* use mysql2 ([#671](https://github.com/cnpm/cnpmcore/issues/671)) ([58d19b1](58d19b17f0))
2024-04-15 06:24:32 +00:00
fengmk2
58d19b17f0 feat: use mysql2 (#671)
https://github.com/cyjake/leoric/pull/419

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Refactor**
- Updated the database management system to enhance performance and
compatibility.
- **Chores**
	- Upgraded internal libraries to improve stability and security.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-04-15 14:22:53 +08:00
semantic-release-bot
b94c8efd6c Release 3.54.0
[skip ci]

## [3.54.0](https://github.com/cnpm/cnpmcore/compare/v3.53.4...v3.54.0) (2024-04-13)

### Features

* use mysql2@^3.9.4 ([#669](https://github.com/cnpm/cnpmcore/issues/669)) ([468f9e4](468f9e4e36))

### Bug Fixes

* remove unuse mysql2 deps ([#670](https://github.com/cnpm/cnpmcore/issues/670)) ([c71d185](c71d185ee1))
2024-04-13 14:24:37 +00:00
fengmk2
c71d185ee1 fix: remove unuse mysql2 deps (#670)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Chores**
	- Removed the `mysql2` dependency from the project.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-04-13 22:23:12 +08:00
fengmk2
468f9e4e36 feat: use mysql2@^3.9.4 (#669)
closes https://github.com/cnpm/cnpmcore/issues/668

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit


- **Chores**
- Updated the database management package to enhance performance and
security.
	- Adjusted supported Node.js versions in the workflow configuration.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-04-13 21:51:16 +08:00
semantic-release-bot
ebc212c1c4 Release 3.53.4
[skip ci]

## [3.53.4](https://github.com/cnpm/cnpmcore/compare/v3.53.3...v3.53.4) (2024-03-28)

### Bug Fixes

* sync cypress linux-arm64 binary ([#662](https://github.com/cnpm/cnpmcore/issues/662)) ([049b186](049b186a0e))
2024-03-28 05:21:01 +00:00
fengmk2
049b186a0e fix: sync cypress linux-arm64 binary (#662)
closes https://github.com/cnpm/cnpmjs.org/issues/1560

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added support for the `linux-arm64` platform in Cypress binary
handling.
- **Tests**
- Updated tests to include assertions for the newly supported
`linux-arm64` platform.
- **Chores**
- Updated Node.js version to 21 in workflow configuration for improved
job execution.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-03-28 13:19:44 +08:00
semantic-release-bot
6664189a91 Release 3.53.3
[skip ci]

## [3.53.3](https://github.com/cnpm/cnpmcore/compare/v3.53.2...v3.53.3) (2024-02-23)

### Bug Fixes

* dont set empty string authorization header ([#654](https://github.com/cnpm/cnpmcore/issues/654)) ([64bb78c](64bb78cf8a))
2024-02-23 17:48:31 +00:00
fengmk2
64bb78cf8a fix: dont set empty string authorization header (#654)
https://github.com/cnpm/cnpmcore/issues/652
2024-02-24 01:47:20 +08:00
semantic-release-bot
f7d9d49b4c Release 3.53.2
[skip ci]

## [3.53.2](https://github.com/cnpm/cnpmcore/compare/v3.53.1...v3.53.2) (2024-02-23)

### Bug Fixes

* only set chrome-for-testing data timestamp after sync task finish ([#653](https://github.com/cnpm/cnpmcore/issues/653)) ([4bc0c9c](4bc0c9ca59))
2024-02-23 17:18:58 +00:00
fengmk2
4bc0c9ca59 fix: only set chrome-for-testing data timestamp after sync task finish (#653)
closes https://github.com/cnpm/cnpmcore/issues/652
2024-02-24 01:17:48 +08:00
hljwkwm
ae83136e62 docs: add custom time zone example (#651)
使用 Docker 打出的镜像,默认会使用0区时区,会影响到日志、数据库等时间的显示,考虑到全球化,可在镜像运行时,
设置环境变量 `TZ` 即可解决该问题,TZ 列表可参考:[https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)。
2024-02-21 21:48:28 +08:00
semantic-release-bot
6b4f9af947 Release 3.53.1
[skip ci]

## [3.53.1](https://github.com/cnpm/cnpmcore/compare/v3.53.0...v3.53.1) (2024-02-20)

### Bug Fixes

* fix breaking change about RSA_PKCS1_PADDING ([#650](https://github.com/cnpm/cnpmcore/issues/650)) ([a6737e6](a6737e6150))
2024-02-20 14:10:53 +00:00
hljwkwm
a6737e6150 fix: fix breaking change about RSA_PKCS1_PADDING (#650)
Node.JS 安全性修复导致 RSA_PKCS1_PADDING 不可用,会报出以下错误:

> RSA_PKCS1_PADDING is no longer supported for private decryption, this
can be reverted with --security-revert=CVE-2023-46809。

参考链接:https://www.eggjs.org/zh-CN/core/security#revert-cve
2024-02-20 22:09:44 +08:00
semantic-release-bot
2ec6bd94b2 Release 3.53.0
[skip ci]

## [3.53.0](https://github.com/cnpm/cnpmcore/compare/v3.52.0...v3.53.0) (2024-01-27)

### Features

* mirror bun ([#645](https://github.com/cnpm/cnpmcore/issues/645)) ([26d2ef2](26d2ef2124))
2024-01-27 13:16:31 +00:00
fengmk2
26d2ef2124 feat: mirror bun (#645)
https://github.com/oven-sh/bun
2024-01-27 21:15:05 +08:00
semantic-release-bot
9004ce7a1c Release 3.52.0
[skip ci]

## [3.52.0](https://github.com/cnpm/cnpmcore/compare/v3.51.1...v3.52.0) (2024-01-25)

### Features

* mirror pkg-fetch from yao-pkg/pkg-fetch ([#644](https://github.com/cnpm/cnpmcore/issues/644)) ([6e32679](6e326790c4))
2024-01-25 10:10:11 +00:00
unbyte
6e326790c4 feat: mirror pkg-fetch from yao-pkg/pkg-fetch (#644)
**what:** pkg-fetch provides prebuilt node binaries used by pkg.

**why from yao-pkg:**
[vercel/pkg-fetch](https://github.com/vercel/pkg-fetch) was archived,
[yao-pkg/pkg-fetch](https://github.com/yao-pkg/pkg-fetch) is currently
the most actively maintained fork.

close: https://github.com/cnpm/cnpmcore/issues/543
2024-01-25 18:08:56 +08:00
semantic-release-bot
4644c1e788 Release 3.51.1
[skip ci]

## [3.51.1](https://github.com/cnpm/cnpmcore/compare/v3.51.0...v3.51.1) (2024-01-23)

### Bug Fixes

* hooks triggers ([#641](https://github.com/cnpm/cnpmcore/issues/641)) ([838eecf](838eecff2d))
2024-01-23 07:35:16 +00:00
elrrrrrrr
838eecff2d fix: hooks triggers (#641)
> Since the eventBus#cork , version & tag events are triggered at same
time, cause the abnormal triggers of different types of hooks.
* ~~🐞 Fix triggerHook type targetName to be `tagetName:changeId`~~
* 🤖 Only merge sync tasks (binary, package) which in waiting states
--------
> 由于 `eventBus#cork` 机制,版本事件同时触发,导致不同类型 hook 触发异常
* ~~🐞 修改 triggerHook 类型 targetName 为 `包名:changeId`~~
* 🤖 仅合并 waiting 状态下包同步任务
2024-01-23 15:34:09 +08:00
semantic-release-bot
08678c70db Release 3.51.0
[skip ci]

## [3.51.0](https://github.com/cnpm/cnpmcore/compare/v3.50.0...v3.51.0) (2024-01-13)

### Features

* support GITHUB_TOKEN to increae github api request rate ([#639](https://github.com/cnpm/cnpmcore/issues/639)) ([0f7aa4a](0f7aa4a50f))
2024-01-13 05:49:31 +00:00
fengmk2
0f7aa4a50f feat: support GITHUB_TOKEN to increae github api request rate (#639) 2024-01-13 13:48:10 +08:00
semantic-release-bot
05445b49c3 Release 3.50.0
[skip ci]

## [3.50.0](https://github.com/cnpm/cnpmcore/compare/v3.49.5...v3.50.0) (2024-01-13)

### Features

* mirror ant-design-charts releases ([#638](https://github.com/cnpm/cnpmcore/issues/638)) ([ad86be3](ad86be312e))
2024-01-13 05:11:12 +00:00
fengmk2
ad86be312e feat: mirror ant-design-charts releases (#638)
closes https://github.com/cnpm/cnpmcore/issues/635
2024-01-13 13:09:42 +08:00
windhc
039a56f471 chore: add registry api doc (#636)
closes https://github.com/cnpm/cnpmcore/issues/630
2024-01-13 12:52:10 +08:00
semantic-release-bot
3310b0e435 Release 3.49.5
[skip ci]

## [3.49.5](https://github.com/cnpm/cnpmcore/compare/v3.49.4...v3.49.5) (2024-01-06)

### Bug Fixes

* read @prisma/engines-version from dependencies ([#633](https://github.com/cnpm/cnpmcore/issues/633)) ([a009668](a0096685fc))
2024-01-06 13:28:01 +00:00
fengmk2
a0096685fc fix: read @prisma/engines-version from dependencies (#633)
closes https://github.com/cnpm/cnpm/issues/436
2024-01-06 21:26:59 +08:00
semantic-release-bot
b0e0a2d464 Release 3.49.4
[skip ci]

## [3.49.4](https://github.com/cnpm/cnpmcore/compare/v3.49.3...v3.49.4) (2024-01-06)

### Bug Fixes

* github file size limit increased to 250mb ([#632](https://github.com/cnpm/cnpmcore/issues/632)) ([d5bf9ce](d5bf9ceb1b))
2024-01-06 13:10:36 +00:00
fengmk2
d5bf9ceb1b fix: github file size limit increased to 250mb (#632)
closes https://github.com/cnpm/cnpm/issues/435
2024-01-06 21:08:41 +08:00
semantic-release-bot
10b97c8697 Release 3.49.3
[skip ci]

## [3.49.3](https://github.com/cnpm/cnpmcore/compare/v3.49.2...v3.49.3) (2023-12-25)

### Bug Fixes

* unpkg lock ([#629](https://github.com/cnpm/cnpmcore/issues/629)) ([5a8a4eb](5a8a4eb10c))
2023-12-25 03:07:09 +00:00
elrrrrrrr
5a8a4eb10c fix: unpkg lock (#629)
> When accessing the unpkg service, when the packages have not yet been
synchronized, will lead to multiple synchronization attempts
concurrently causing db insert errors.
* 🔒 Added a Redis lock for the `ensurePackageVersionFilesSync` function,
with a default timeout of 60 seconds.
* 🥸 Admin PUT requests and the package version auto sync process are not
restricted by this.

> 当访问 unpkg 服务时,如果访问存量未同步的包,可能导致多次同步并发报错
* 🔒 为 ensurePackageVersionFilesSync 添加 redis 锁,默认超时 60s
* 🥸 管理员手动 PUT 请求和包同步流程不受限制
2023-12-25 11:05:43 +08:00
semantic-release-bot
7e176f2f42 Release 3.49.2
[skip ci]

## [3.49.2](https://github.com/cnpm/cnpmcore/compare/v3.49.1...v3.49.2) (2023-12-19)

### Bug Fixes

* ignore db query error default logger ([#626](https://github.com/cnpm/cnpmcore/issues/626)) ([6cc2f2d](6cc2f2d830))
* should search default packages when text is empty ([#623](https://github.com/cnpm/cnpmcore/issues/623)) ([0c4a52d](0c4a52d220))
2023-12-19 09:17:04 +00:00
fengmk2
6cc2f2d830 fix: ignore db query error default logger (#626) 2023-12-19 17:15:55 +08:00
semantic-release-bot
dddb10e510 Release 3.49.2
[skip ci]

## [3.49.2](https://github.com/cnpm/cnpmcore/compare/v3.49.1...v3.49.2) (2023-12-18)

### Bug Fixes

* should search default packages when text is empty ([#623](https://github.com/cnpm/cnpmcore/issues/623)) ([0c4a52d](0c4a52d220))
2023-12-18 12:01:55 +00:00
Beace
0c4a52d220 fix: should search default packages when text is empty (#623)
当搜索关键词为空时,不进行 ES 关键词搜索拼接,返回一个默认的全量数据排名。 

close https://github.com/cnpm/cnpmcore/issues/622
2023-12-18 20:00:52 +08:00
semantic-release-bot
c3e481c5c4 Release 3.49.1
[skip ci]

## [3.49.1](https://github.com/cnpm/cnpmcore/compare/v3.49.0...v3.49.1) (2023-12-18)

### Bug Fixes

* use tar fork version to fix memory leak ([#625](https://github.com/cnpm/cnpmcore/issues/625)) ([6c519f7](6c519f73ce))
2023-12-18 03:04:36 +00:00
fengmk2
6c519f73ce fix: use tar fork version to fix memory leak (#625)
closes https://github.com/cnpm/cnpmcore/issues/624
2023-12-18 11:03:32 +08:00
semantic-release-bot
87ca86f1db Release 3.49.0
[skip ci]

## [3.49.0](https://github.com/cnpm/cnpmcore/compare/v3.48.4...v3.49.0) (2023-12-12)

### Features

* use egg-cors@3 ([#620](https://github.com/cnpm/cnpmcore/issues/620)) ([fcca3c3](fcca3c30ce))
2023-12-12 01:31:42 +00:00
fengmk2
fcca3c30ce feat: use egg-cors@3 (#620)
https://github.com/eggjs/egg-cors/pull/27
2023-12-12 09:08:33 +08:00
semantic-release-bot
37b50842fd Release 3.48.4
[skip ci]

## [3.48.4](https://github.com/cnpm/cnpmcore/compare/v3.48.3...v3.48.4) (2023-12-07)

### Bug Fixes

* incorrect latest tag in strict specific version ([#610](https://github.com/cnpm/cnpmcore/issues/610)) ([acfd667](acfd66748f))
2023-12-07 12:48:52 +00:00
elrrrrrrr
e62fa26788 chore: ts (#619)
* 锁定 typescript 依赖至 5.2.2 解决构建报错
2023-12-07 20:47:31 +08:00
semantic-release-bot
64dfcb35a4 Release 3.48.4
[skip ci]

## [3.48.4](https://github.com/cnpm/cnpmcore/compare/v3.48.3...v3.48.4) (2023-11-28)

### Bug Fixes

* incorrect latest tag in strict specific version ([#610](https://github.com/cnpm/cnpmcore/issues/610)) ([acfd667](acfd66748f))
2023-11-28 14:48:48 +00:00
hezhengxu2018
acfd66748f fix: incorrect latest tag in strict specific version (#610)
1. 添加新建任务时的重复版本校验,去除执行任务时的版本去重
2.
在 strictSyncSpecificVersion 开启时,修改为从可用版本中选择 latestTag。之前从任务的指定版本中生成 latestTag 逻辑不正确
2023-11-28 22:47:34 +08:00
semantic-release-bot
072e146e5b Release 3.48.3
[skip ci]

## [3.48.3](https://github.com/cnpm/cnpmcore/compare/v3.48.2...v3.48.3) (2023-11-06)

### Bug Fixes

* es query script score syntax fix and add error handler for 404 error ([#607](https://github.com/cnpm/cnpmcore/issues/607)) ([8e1f4ca](8e1f4ca880))
2023-11-06 06:29:09 +00:00
Beace
8e1f4ca880 fix: es query script score syntax fix and add error handler for 404 error (#607)
closes https://github.com/cnpm/cnpmcore/issues/598
2023-11-06 14:27:32 +08:00
semantic-release-bot
603bb82b1f Release 3.48.2
[skip ci]

## [3.48.2](https://github.com/cnpm/cnpmcore/compare/v3.48.1...v3.48.2) (2023-11-03)

### Bug Fixes

* should set OPTIONS on access-control-allow-methods ([#608](https://github.com/cnpm/cnpmcore/issues/608)) ([0179ef3](0179ef364a))
2023-11-03 11:13:45 +00:00
fengmk2
0179ef364a fix: should set OPTIONS on access-control-allow-methods (#608)
Access to fetch at
'https://registry.npmmirror.com/isstream/-/isstream-0.1.0.tgz' from
origin 'https://foo.com' has been blocked by CORS policy: Method OPTIONS
is not allowed by Access-Control-Allow-Methods in preflight response.
2023-11-03 19:12:25 +08:00
semantic-release-bot
f03d48e511 Release 3.48.1
[skip ci]

## [3.48.1](https://github.com/cnpm/cnpmcore/compare/v3.48.0...v3.48.1) (2023-11-03)

### Bug Fixes

* should set access-control-allow-origin and headers ([#606](https://github.com/cnpm/cnpmcore/issues/606)) ([18ef7f4](18ef7f49af))
2023-11-03 10:53:27 +00:00
fengmk2
18ef7f49af fix: should set access-control-allow-origin and headers (#606) 2023-11-03 18:52:08 +08:00
semantic-release-bot
9ea70088fb Release 3.48.0
[skip ci]

## [3.48.0](https://github.com/cnpm/cnpmcore/compare/v3.47.2...v3.48.0) (2023-11-03)

### Features

* allow OPTIONS request on tgz downlaod url ([#605](https://github.com/cnpm/cnpmcore/issues/605)) ([5bedb25](5bedb25f9d))
2023-11-03 10:24:08 +00:00
fengmk2
5bedb25f9d feat: allow OPTIONS request on tgz downlaod url (#605)
make webcontainer can run npm install on cnpmcore registry
2023-11-03 18:22:57 +08:00
semantic-release-bot
31946ba10e Release 3.47.2
[skip ci]

## [3.47.2](https://github.com/cnpm/cnpmcore/compare/v3.47.1...v3.47.2) (2023-10-28)

### Bug Fixes

* ignore BodyTimeoutError ([#603](https://github.com/cnpm/cnpmcore/issues/603)) ([cde4f03](cde4f03c30))
2023-10-28 14:29:05 +00:00
fengmk2
cde4f03c30 fix: ignore BodyTimeoutError (#603) 2023-10-28 22:27:42 +08:00
semantic-release-bot
c3c7b391c0 Release 3.47.1
[skip ci]

## [3.47.1](https://github.com/cnpm/cnpmcore/compare/v3.47.0...v3.47.1) (2023-10-26)

### Bug Fixes

* ignore HttpClientRequestTimeoutError on change stream worker ([#601](https://github.com/cnpm/cnpmcore/issues/601)) ([0791769](079176926d))
2023-10-26 09:43:17 +00:00
fengmk2
079176926d fix: ignore HttpClientRequestTimeoutError on change stream worker (#601) 2023-10-26 17:41:59 +08:00
semantic-release-bot
e01d39ef4e Release 3.47.0
[skip ci]

## [3.47.0](https://github.com/cnpm/cnpmcore/compare/v3.46.0...v3.47.0) (2023-10-26)

### Features

* ignore network error to error log ([#600](https://github.com/cnpm/cnpmcore/issues/600)) ([22d401e](22d401ee1f))
2023-10-26 07:39:36 +00:00
fengmk2
22d401ee1f feat: ignore network error to error log (#600) 2023-10-26 15:38:10 +08:00
killa
3cdb7cc9df mirrors: add fuse-t (#599) 2023-10-25 12:59:09 +08:00
semantic-release-bot
5ad775e411 Release 3.46.0
[skip ci]

## [3.46.0](https://github.com/cnpm/cnpmcore/compare/v3.45.1...v3.46.0) (2023-10-11)

### Features

* read remote auth token from database ([#595](https://github.com/cnpm/cnpmcore/issues/595)) ([707a1d3](707a1d3809))
2023-10-11 14:26:40 +00:00
hezhengxu2018
707a1d3809 feat: read remote auth token from database (#595)
closes https://github.com/cnpm/cnpmcore/issues/586
2023-10-11 09:25:17 -05:00
semantic-release-bot
9fcbb00406 Release 3.45.1
[skip ci]

## [3.45.1](https://github.com/cnpm/cnpmcore/compare/v3.45.0...v3.45.1) (2023-10-07)

### Bug Fixes

* use oss-cnpm@5.0.1 ([#597](https://github.com/cnpm/cnpmcore/issues/597)) ([413ec56](413ec5685e))
2023-10-07 10:51:42 +00:00
fengmk2
413ec5685e fix: use oss-cnpm@5.0.1 (#597)
https://github.com/node-modules/oss-client/pull/15
2023-10-07 18:50:13 +08:00
fengmk2
f66057794e Release 3.45.0
[skip ci]
2023-10-07 13:34:36 +08:00
semantic-release-bot
9a5e8c387a Release 3.44.0
[skip ci]

## [3.44.0](https://github.com/cnpm/cnpmcore/compare/v3.43.5...v3.44.0) (2023-10-07)

### Features

* sync all crhome for test binaries ([#592](https://github.com/cnpm/cnpmcore/issues/592)) ([4596b21](4596b21271))
* use oss-client v2 ([#596](https://github.com/cnpm/cnpmcore/issues/596)) ([d24e3bd](d24e3bd235))
2023-10-07 04:19:41 +00:00
fengmk2
d24e3bd235 feat: use oss-client v2 (#596)
https://github.com/cnpm/oss-cnpm/pull/29
https://github.com/node-modules/oss-client/pull/12
2023-10-07 12:18:00 +08:00
semantic-release-bot
d6d72650dd Release 3.44.0
[skip ci]

## [3.44.0](https://github.com/cnpm/cnpmcore/compare/v3.43.5...v3.44.0) (2023-09-19)

### Features

* sync all crhome for test binaries ([#592](https://github.com/cnpm/cnpmcore/issues/592)) ([4596b21](4596b21271))
2023-09-19 15:34:28 +00:00
fengmk2
4596b21271 feat: sync all crhome for test binaries (#592)
closes https://github.com/cnpm/cnpmcore/issues/591
2023-09-19 23:33:03 +08:00
semantic-release-bot
c33f10e0ab Release 3.43.5
[skip ci]

## [3.43.5](https://github.com/cnpm/cnpmcore/compare/v3.43.4...v3.43.5) (2023-09-05)

### Bug Fixes

* the license may be an object ([#587](https://github.com/cnpm/cnpmcore/issues/587)) ([88b6afb](88b6afb66e)), closes [/github.com/cnpm/cnpmcore/issues/585#issuecomment-1706009496](https://github.com/cnpm//github.com/cnpm/cnpmcore/issues/585/issues/issuecomment-1706009496)
2023-09-05 10:47:03 +00:00
Beace
88b6afb66e fix: the license may be an object (#587)
有些 package 的 license 是个对象,会导致 es 写入失败
https://github.com/cnpm/cnpmcore/issues/585#issuecomment-1706009496


![image](https://github.com/cnpm/cnpmcore/assets/13284978/4343a1e8-1fa5-4aed-950d-d5038534dad8)
2023-09-05 18:45:42 +08:00
semantic-release-bot
6d156a5c96 Release 3.43.4
[skip ci]

## [3.43.4](https://github.com/cnpm/cnpmcore/compare/v3.43.3...v3.43.4) (2023-09-02)

### Bug Fixes

* add CDN cache header on search api ([#583](https://github.com/cnpm/cnpmcore/issues/583)) ([89f6b98](89f6b989c1))
2023-09-02 02:42:58 +00:00
fengmk2
89f6b989c1 fix: add CDN cache header on search api (#583) 2023-09-02 10:41:39 +08:00
semantic-release-bot
5e4d988c2f Release 3.43.3
[skip ci]

## [3.43.3](https://github.com/cnpm/cnpmcore/compare/v3.43.2...v3.43.3) (2023-09-01)

### Bug Fixes

* author display in cli ([#582](https://github.com/cnpm/cnpmcore/issues/582)) ([9b2dc41](9b2dc41134))
2023-09-01 06:52:08 +00:00
elrrrrrrr
9b2dc41134 fix: author display in cli (#582)
> Revert ineffective changes from
https://github.com/cnpm/cnpmcore/pull/581, add username index
* 🧶 Add username field to maintainers in search results, handle
uniformly when writing
* 📚 Update index documentation
-----
> 回滚 https://github.com/cnpm/cnpmcore/pull/581 无效改动,添加 username 索引
* 🧶 搜索结果中的 maintainers 添加 username 字段,写入时统一处理
* 📚 更新索引文档说明
2023-09-01 14:50:54 +08:00
semantic-release-bot
3f95d0fadd Release 3.43.2
[skip ci]

## [3.43.2](https://github.com/cnpm/cnpmcore/compare/v3.43.1...v3.43.2) (2023-09-01)

### Bug Fixes

* author info ([#581](https://github.com/cnpm/cnpmcore/issues/581)) ([6dd241d](6dd241d690))
2023-09-01 03:25:14 +00:00
elrrrrrrr
6dd241d690 fix: author info (#581)
> in npm@8 cli, empty author information after executing npm search

* 🐞 Fix: npm-cli author field is empty, need to add publisher-related
fields
* 📚 Update API documentation
-----

> npm@8 命令行,执行 npm search 后,author 信息为空
* 🐞 修复 npm-cli author 字段为空,需添加 publisher 相关字段
* 📚 更新文档接口信息


![image](https://github.com/cnpm/cnpmcore/assets/5574625/f3a91d29-0bf4-498a-ae2f-c3dd4b47b22f)
2023-09-01 11:23:34 +08:00
semantic-release-bot
868c8d305e Release 3.43.1
[skip ci]

## [3.43.1](https://github.com/cnpm/cnpmcore/compare/v3.43.0...v3.43.1) (2023-09-01)

### Bug Fixes

* use env.CNPMCORE_CONFIG_ENABLE_ES to enable ([#580](https://github.com/cnpm/cnpmcore/issues/580)) ([bcf67c4](bcf67c4cea))
2023-09-01 02:07:12 +00:00
fengmk2
bcf67c4cea fix: use env.CNPMCORE_CONFIG_ENABLE_ES to enable (#580) 2023-09-01 10:05:58 +08:00
semantic-release-bot
941b277244 Release 3.43.0
[skip ci]

## [3.43.0](https://github.com/cnpm/cnpmcore/compare/v3.42.2...v3.43.0) (2023-09-01)

### Features

* support npm search command like npmio ([#513](https://github.com/cnpm/cnpmcore/issues/513)) ([7f85848](7f858482f7))
2023-09-01 01:22:37 +00:00
Beace
7f858482f7 feat: support npm search command like npmio (#513)
- [x] 找个合适的 eggjs es 插件,或者手撸个,看社区的几个版本都比较低
- [x] HTTP Server 新增 API
`/-/v1/search?text=react&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5`,第一版不一定能
qpm 都支持,先支持现有的下载量数据,即 popularity 的参考数据
- [x] 监听相关的 metadata 变更的 event,同步写入、删除 增量的 ES 数据,ES 有较强抗压能力,这块直接做成同步就好
- [x] 考虑可能同步也会丢部分数据(stream 不稳定时),HTTP Server 再追加一个手动同步 ES 的接口,传包名同步触发写
ES 即可
- [x] 提供全量一次性的初始化同步 ES 脚本
- [x] setting/mapping 参考
https://github.com/npms-io/npms-analyzer/blob/master/config/elasticsearch/npms.json5

---------

Co-authored-by: fengmk2 <fengmk2@gmail.com>
Co-authored-by: elrrrrrrr <elrrrrrrr@gmail.com>
2023-09-01 09:21:03 +08:00
semantic-release-bot
6e45ac5a63 Release 3.42.2
[skip ci]

## [3.42.2](https://github.com/cnpm/cnpmcore/compare/v3.42.1...v3.42.2) (2023-08-31)

### Bug Fixes

* should use NodeNext on module and moduleResolution tsconfig ([#578](https://github.com/cnpm/cnpmcore/issues/578)) ([10d7a84](10d7a8499e))
2023-08-31 01:16:34 +00:00
fengmk2
10d7a8499e fix: should use NodeNext on module and moduleResolution tsconfig (#578)
https://devblogs.microsoft.com/typescript/announcing-typescript-5-2/#module-and-moduleresolution-must-match-under-recent-node-js-settings

> Option 'moduleResolution' must be set to 'NodeNext' (or left
unspecified) when option 'module' is set to 'NodeNext'.
2023-08-31 09:15:04 +08:00
semantic-release-bot
2b2e13c01d Release 3.42.1
[skip ci]

## [3.42.1](https://github.com/cnpm/cnpmcore/compare/v3.42.0...v3.42.1) (2023-08-23)

### Bug Fixes

* default latest tag ([#575](https://github.com/cnpm/cnpmcore/issues/575)) ([ffe8fa7](ffe8fa7d19)), closes [#574](https://github.com/cnpm/cnpmcore/issues/574)
2023-08-23 06:12:39 +00:00
elrrrrrrr
ffe8fa7d19 fix: default latest tag (#575)
> closes #574 Fixed the issue where custom tags in publishConfig
prevented the default latest tag.
* 🧶 Modified the `savePackageVersion` API, automatically add latest tag
if no latest tag.
* 🧶 The publish tag parameter has been changed to tags, triggering
corresponding events in batches.
* ♻️ No changes to the package synchronization process.
------
> closes #574 修复 publishConfig 中自定义 tag,导致 latest tag 未设置的问题
* 🧶 修改 savePackageVersion 接口,如果当前包未配置 latest tag,则自动补全
* 🧶 publish tag 参数改为 tags,对应事件分批触发
* ♻️ 包同步流程不做修改
2023-08-23 14:11:23 +08:00
semantic-release-bot
39de1c7df2 Release 3.42.0
[skip ci]

## [3.42.0](https://github.com/cnpm/cnpmcore/compare/v3.41.0...v3.42.0) (2023-08-22)

### Features

* revalidate latest version ([#573](https://github.com/cnpm/cnpmcore/issues/573)) ([73b4383](73b4383f5c)), closes [#376](https://github.com/cnpm/cnpmcore/issues/376)
2023-08-22 07:42:19 +00:00
elrrrrrrr
73b4383f5c feat: revalidate latest version (#573)
> closes #376: fix issue with incomplete binary file after upstream
release or sync failure
* 🧶 Modify the binary `diff` method, adding latest version check method.
* ♻️ Perform additional comparison on the latest version, without
modifying existing data.

---------
> 修复 #376 ,兼容上游发布或同步失败后,产物同步不全的问题
* 🧶 调整 binary `diff` 方法,添加最新版本校验逻辑
* ♻️ 对最新版本的子目录进行额外比对,存量数据不做修改
2023-08-22 15:41:04 +08:00
semantic-release-bot
9916bd9ecf Release 3.41.0
[skip ci]

## [3.41.0](https://github.com/cnpm/cnpmcore/compare/v3.40.0...v3.41.0) (2023-08-17)

### Features

* improved readability of emoji in sync logs ([#572](https://github.com/cnpm/cnpmcore/issues/572)) ([0ac275a](0ac275a348))
2023-08-17 12:51:41 +00:00
一丝
0ac275a348 feat: improved readability of emoji in sync logs (#572) 2023-08-17 20:50:08 +08:00
semantic-release-bot
3f9c91c430 Release 3.40.0
[skip ci]

## [3.40.0](https://github.com/cnpm/cnpmcore/compare/v3.39.5...v3.40.0) (2023-08-08)

### Features

* signup on auth ([#567](https://github.com/cnpm/cnpmcore/issues/567)) ([c710600](c7106008d9))
2023-08-08 12:47:01 +00:00
elrrrrrrr
c7106008d9 feat: signup on auth (#567)
> Auto init the account when auth

* 🧶 Added `findOrCreateUser` method. Initialize account on both login
and authorization, as per the submitted GitHub."
------

> 授权时,默认进行账户初始化
* 🧶 新增 `findOrCreateUser` 方法,登录和授权时均初始化账户
2023-08-08 20:45:46 +08:00
semantic-release-bot
b102711adf Release 3.39.5
[skip ci]

## [3.39.5](https://github.com/cnpm/cnpmcore/compare/v3.39.4...v3.39.5) (2023-08-08)

### Bug Fixes

* noImplicitAny ts ([#568](https://github.com/cnpm/cnpmcore/issues/568)) ([1932bb9](1932bb9713))
2023-08-08 12:44:23 +00:00
elrrrrrrr
1932bb9713 fix: noImplicitAny ts (#568)
> Attempted to apply the `noImplicitAny`, parameter types should be
specified unless any is manually declared.
* 🐞 Fixed an issue in bugVersionAdvice where AbbreviatedManifest was
being set abnormally.
* 🤖 Added index.d.ts to store declarations for dependencies without
types.
* 🤔 skipLibCheck has no effect on leoric for now, so it cannot be
enabled temporarily.
--------
> 尝试应用 `noImplicitAny` 配置,除非手动声明 any,否则需要指定参数类型
* 🐞 修复 bugVersionAdvice 中,AbbreviatedManifest 设置异常
* 🤖 添加 index.d.ts 存放无类型依赖声明
* 🤔 skipLibCheck 对 leoric 失效,暂时无法开启 



![image](https://github.com/cnpm/cnpmcore/assets/5574625/7ed9d22e-cac8-4202-ba3c-d4c26eb7dc00)
2023-08-08 20:42:57 +08:00
semantic-release-bot
3297121b9f Release 3.39.4
[skip ci]

## [3.39.4](https://github.com/cnpm/cnpmcore/compare/v3.39.3...v3.39.4) (2023-08-04)

### Bug Fixes

* publisher info ([#565](https://github.com/cnpm/cnpmcore/issues/565)) ([94bcc1a](94bcc1a37e))
2023-08-04 05:47:12 +00:00
elrrrrrrr
94bcc1a37e fix: publisher info (#565)
> Fixed an issue with the `_npmUser` field setting during the initial
sync.
1. 🧶 Fixed _npmUser field setting issue during initial sync, should use
displayName.
2. 🧶 skip diff abbreviated meta for _npmUser
3. 🤖 Refined some TypeScript definitions, use isEqual to diff metas

-------

> 修复首次同步时,`_npmUser` 字段设置异常
1. 🧶 修复 publisher 匹配,获取逻辑,应当用 displayName 进行匹配
2. 🧶 精简 meta 信息,跳过比较不存在的 _npmUser 字段
2. 🤖 调整部分 ts 定义,使用 isEqual 来进行 diff 比较
2023-08-04 13:45:35 +08:00
semantic-release-bot
276b9511b8 Release 3.39.3
[skip ci]

## [3.39.3](https://github.com/cnpm/cnpmcore/compare/v3.39.2...v3.39.3) (2023-08-01)

### Bug Fixes

* don't log NotImplementedError to error log ([#563](https://github.com/cnpm/cnpmcore/issues/563)) ([bcf3547](bcf3547ff2))
2023-08-01 10:26:15 +00:00
fengmk2
bcf3547ff2 fix: don't log NotImplementedError to error log (#563)
![image](https://github.com/cnpm/cnpmcore/assets/156269/0a1de25e-47de-4c74-a934-566bd7a16f2f)
2023-08-01 18:24:46 +08:00
semantic-release-bot
9f6b44dfe9 Release 3.39.2
[skip ci]

## [3.39.2](https://github.com/cnpm/cnpmcore/compare/v3.39.1...v3.39.2) (2023-07-29)

### Bug Fixes

* rename libpg-query to libpg-query-node ([#561](https://github.com/cnpm/cnpmcore/issues/561)) ([9483a46](9483a460a3))
2023-07-29 08:22:32 +00:00
fengmk2
9483a460a3 fix: rename libpg-query to libpg-query-node (#561) 2023-07-29 16:21:03 +08:00
semantic-release-bot
3498ba221c Release 3.39.1
[skip ci]

## [3.39.1](https://github.com/cnpm/cnpmcore/compare/v3.39.0...v3.39.1) (2023-07-27)

### Bug Fixes

* body parser ignore ([#558](https://github.com/cnpm/cnpmcore/issues/558)) ([81d6455](81d6455ff8))
2023-07-27 08:13:00 +00:00
elrrrrrrr
81d6455ff8 fix: body parser ignore (#558)
> Closes https://github.com/cnpm/cnpmcore/issues/551 Some versions
encounter issues with audit requests using the GET method & gzip
* 🧶 Modified the NOT_IMPLEMENTED configuration to skip bodyParser
parsing by default.
* 🧶 Added homeService and implemented miscellaneous methods to handle
additional routes.
------
> Closes https://github.com/cnpm/cnpmcore/issues/551 部分版本 audit 请求为 get
且 参数解析异常
* 🧶 修改 NOT_IMPLEMENTED 配置,默认跳过 bodyParser 解析
* 🧶 添加 homeService,提供 misc 方法处理额外路由

---------

Co-authored-by: fengmk2 <fengmk2@gmail.com>
2023-07-27 16:11:47 +08:00
semantic-release-bot
7ba8dbb4a7 Release 3.39.0
[skip ci]

## [3.39.0](https://github.com/cnpm/cnpmcore/compare/v3.38.2...v3.39.0) (2023-07-27)

### Features

* Sync libpg-query binary ([#557](https://github.com/cnpm/cnpmcore/issues/557)) ([8556b5f](8556b5f92f))
2023-07-27 06:30:37 +00:00
wandergis
8556b5f92f feat: Sync libpg-query binary (#557) 2023-07-27 14:29:22 +08:00
semantic-release-bot
e4d44c68e5 Release 3.38.2
[skip ci]

## [3.38.2](https://github.com/cnpm/cnpmcore/compare/v3.38.1...v3.38.2) (2023-07-21)

### Bug Fixes

* _npmUser info in fullManifest ([#554](https://github.com/cnpm/cnpmcore/issues/554)) ([4455295](44552959eb)), closes [#553](https://github.com/cnpm/cnpmcore/issues/553)
2023-07-21 11:52:25 +00:00
elrrrrrrr
44552959eb fix: _npmUser info in fullManifest (#554)
> closes #553, fixing the issue introduced by
https://github.com/cnpm/cnpmcore/pull/491, which caused an abnormality
in the _npmUser field in fullManifest.
1. 🧶 Update the `publish` method to pass in the actual operating
publisher information.
2. 🧶 Update the diffMeta function to compare the _npmUser as well.
3. ♻️ Existing data needs to be resynchronized, from 6.2 to 7.20.
-----
> closes #553 , 修复由 https://github.com/cnpm/cnpmcore/pull/491 引入问题,导致
fullManifest 中 _npmUser 字段异常
1. 🧶 更新 .publish 方法,传入实际操作的 publisher 信息
2. 🧶 更新 diffMeta 算法,将 _npmUser 也进行比对
3. ♻️ 存量数据需要重新进行同步,6.2 -> 7.20
2023-07-21 19:50:55 +08:00
semantic-release-bot
9ac676772d Release 3.38.1
[skip ci]

## [3.38.1](https://github.com/cnpm/cnpmcore/compare/v3.38.0...v3.38.1) (2023-07-21)

### Bug Fixes

* publish lock ([#555](https://github.com/cnpm/cnpmcore/issues/555)) ([ec90ab8](ec90ab85fa))
2023-07-21 09:34:03 +00:00
elrrrrrrr
ec90ab85fa fix: publish lock (#555)
> When concurrently executing packet sending, there is a possibility of
version overwrite

* 🧶 Add `usingLock` logic to the publish interface to handle concurrent
execution, which will prevent version overwrite
* 🔨 Modify usingLock to include a return value indicating the success of
lock creation
------
> 并发执行发包时,可能出现版本覆盖问题
1. 🧶 在发布接口中,添加 usingLock 逻辑,包同步场景不涉及
2. 🔨 `usingLock` 添加返回值,标记是否创建锁成功
2023-07-21 17:32:39 +08:00
semantic-release-bot
9ca483cfa5 Release 3.38.0
[skip ci]

## [3.38.0](https://github.com/cnpm/cnpmcore/compare/v3.37.1...v3.38.0) (2023-07-17)

### Features

* misc router ([#552](https://github.com/cnpm/cnpmcore/issues/552)) ([e9e3a7b](e9e3a7b70f)), closes [#551](https://github.com/cnpm/cnpmcore/issues/551)
2023-07-17 06:53:39 +00:00
elrrrrrrr
e9e3a7b70f feat: misc router (#552)
![image](https://github.com/cnpm/cnpmcore/assets/5574625/d158e354-d390-4835-91de-7aa9f5104e49)


> close #551 , 针对未实现的 post 请求添加错误码

1. 🧶 拦截 audit 相关接口
[ref](https://docs.npmjs.com/cli/v9/commands/npm-audit)
2. ♻️ 默认 404 GET 接口不做额外处理

-----

> close #551, add error code for unimplemented post requests

1. 🧶 Intercept audit-related interfaces
[ref](https://docs.npmjs.com/cli/v9/commands/npm-audit)
2. ♻️ No additional processing for default 404 GET interfaces.
2023-07-17 14:52:21 +08:00
乐潇游
8a9412df4f chore: update tegg deps (#549) (#550) 2023-07-15 00:00:36 +08:00
semantic-release-bot
28deae4b70 Release 3.37.1
[skip ci]

## [3.37.1](https://github.com/cnpm/cnpmcore/compare/v3.37.0...v3.37.1) (2023-07-13)

### Bug Fixes

* playwright bianry sync config ([#548](https://github.com/cnpm/cnpmcore/issues/548)) ([166e334](166e3341f4))
2023-07-13 07:06:22 +00:00
elrrrrrrr
166e3341f4 fix: playwright bianry sync config (#548)
> Follow the Playwright-core configuration,
[ref](https://registry.npmmirror.com/playwright-core/1.36.0/files/lib/server/registry/index.js),
to update the binary download path information.

1. 🧶 Update Playwright configuration information.
--------

> Follow playwright-core 配置,
[ref](https://registry.npmmirror.com/playwright-core/1.36.0/files/lib/server/registry/index.js),更新
binary 下载路径信息
1. 🧶 更新 playwright 配置信息
2023-07-13 15:05:10 +08:00
semantic-release-bot
02d0d2b5a0 Release 3.37.0
[skip ci]

## [3.37.0](https://github.com/cnpm/cnpmcore/compare/v3.36.0...v3.37.0) (2023-07-12)

### Features

* add lastUsedAt for classic token ([#547](https://github.com/cnpm/cnpmcore/issues/547)) ([e061685](e0616859ff))
2023-07-12 07:12:37 +00:00
elrrrrrrr
e0616859ff feat: add lastUsedAt for classic token (#547)
> Follow https://github.com/cnpm/cnpmcore/pull/488 , add lastUsedAt for
classic tokens.
1. 🧶 Modify the `checkTokenExpired` method to `checkTokenStatus` which
update the token field internally.
2. ♻️ No compensation will be made for existing data, and it should be
updated by the consuming end.
---------
> Follow https://github.com/cnpm/cnpmcore/pull/488, 为 classic token 也添加
lastUsedAt 信息
1. 🧶 修改 `checkTokenExpired` 方法为 `checkTokenStatus`,内部进行 token 字段更新
2. ♻️ 存量数据不做补偿,由消费端控制
2023-07-12 15:11:20 +08:00
semantic-release-bot
f5da7e6c19 Release 3.36.0
[skip ci]

## [3.36.0](https://github.com/cnpm/cnpmcore/compare/v3.35.1...v3.36.0) (2023-07-09)

### Features

* support strictValidateTarballPkg ([#546](https://github.com/cnpm/cnpmcore/issues/546)) ([dd3438f](dd3438f470)), closes [#542](https://github.com/cnpm/cnpmcore/issues/542)
2023-07-09 15:34:17 +00:00
elrrrrrrr
dd3438f470 feat: support strictValidateTarballPkg (#546)
> Validate the manifest and tarball info to prevent contamination during
consumption, closes #542.
1. 🔨 Added the "strictValidateTarballPkg" mode to enable validation,
only applicable to the slef registry scenario.
2. 🧶 When the configuration is enabled, validate the relevant fields
during publishing, currently only validating the fields affecting
consumption.
3. ♻️ No corrective actions will be taken for existing scenario data.
-----

> 发布时校验 manifest 和 tarball 字段是否陪陪,防止消费时被污染 closes #542
1. 🔨 新增 strictValidateTarballPkg 配置,仅对在发布当前 registry 场景下生效
2. 🧶 配置开启时,发布时校验相关字段,目前仅校验影响消费相关字段
3. ♻️ 存量场景数据不做订正处理
2023-07-09 23:32:53 +08:00
semantic-release-bot
18af011a51 Release 3.35.1
[skip ci]

## [3.35.1](https://github.com/cnpm/cnpmcore/compare/v3.35.0...v3.35.1) (2023-06-29)

### Bug Fixes

* update source registry ([#537](https://github.com/cnpm/cnpmcore/issues/537)) ([ab2fde7](ab2fde7c80))
2023-06-29 07:51:11 +00:00
elrrrrrrr
ab2fde7c80 fix: update source registry (#537)
> the registryInfo in pkg fullManifest should be updated when the
package is migrated to another registry.

1. 🐞 when query from DB, dynamically add registry information.
2. ♻️ when hit cache, updating metadata should already trigger cache
modifications.
--------

> 当包从属 registryId 发生变化时,包 manifest 内的信息也需要同步更新
1. 🐞 从 db 读取元信息时,实时添加 registry 信息
2. ♻️ 从缓存读取元信息时,发布或修改元信息时已触发缓存修改
2023-06-29 15:49:56 +08:00
semantic-release-bot
ebcb65d27f Release 3.35.0
[skip ci]

## [3.35.0](https://github.com/cnpm/cnpmcore/compare/v3.34.10...v3.35.0) (2023-06-28)

### Features

* adaptive username ([#536](https://github.com/cnpm/cnpmcore/issues/536)) ([dd69606](dd69606365)), closes [/github.com/npm/cli/blob/latest/lib/commands/owner.js#L151](https://github.com/cnpm//github.com/npm/cli/blob/latest/lib/commands/owner.js/issues/L151)
2023-06-28 12:50:47 +00:00
elrrrrrrr
dd69606365 feat: adaptive username (#536)
> When executing npm cli owner add command,
[ref](https://github.com/npm/cli/blob/latest/lib/commands/owner.js#L151),
it causes errors due to duplicate additions,when the selfRegistry
configuration has the userPrefix option enabled.
1. 🧶 Modify the user query api to prioritize returning users from
selfRegistry
2. 🧶 Ensure that the query api uniformly returns displayName

---------

> npm cli 执行 owner add 时
[ref](https://github.com/npm/cli/blob/latest/lib/commands/owner.js#L151),依赖查询结果做去重,selfRegistry
配置 userPrefix 时会导致重复添加报错
1. 🧶 修改用户查询接口,优先返回 selfRegistry 内的用户
2. 🧶 查询接口统一返回 displayName
2023-06-28 20:49:32 +08:00
semantic-release-bot
a4a0f2df3a Release 3.34.10
[skip ci]

## [3.34.10](https://github.com/cnpm/cnpmcore/compare/v3.34.9...v3.34.10) (2023-06-28)

### Bug Fixes

* only syncUpstream in default registry ([#535](https://github.com/cnpm/cnpmcore/issues/535)) ([bb5d993](bb5d993030))
2023-06-28 05:58:08 +00:00
elrrrrrrr
bb5d993030 fix: only syncUpstream in default registry (#535)
> Only syncUpstream for default registry, to optimize the
synchronization speed.
* 🧶 Adjust the syncUpstream judgment process.

----

> 仅对公网包进行 syncUpstream 流程处理,内网包没有多级代理流程,优化同步速度。
* 🧶 调整 syncUpstream 判断流程
2023-06-28 13:56:54 +08:00
semantic-release-bot
506969615b Release 3.34.9
[skip ci]

## [3.34.9](https://github.com/cnpm/cnpmcore/compare/v3.34.8...v3.34.9) (2023-06-27)

### Bug Fixes

* console ([#534](https://github.com/cnpm/cnpmcore/issues/534)) ([4141003](4141003e13))
2023-06-27 04:30:55 +00:00
elrrrrrrr
4141003e13 fix: console (#534)
> remove console.log

----

去除 console.log
2023-06-27 12:29:14 +08:00
semantic-release-bot
241677687a Release 3.34.8
[skip ci]

## [3.34.8](https://github.com/cnpm/cnpmcore/compare/v3.34.7...v3.34.8) (2023-06-27)

### Bug Fixes

* legacy pkg publish ([#533](https://github.com/cnpm/cnpmcore/issues/533)) ([20ffba8](20ffba8d41))
2023-06-27 03:55:20 +00:00
elrrrrrrr
20ffba8d41 fix: legacy pkg publish (#533)
> pkgs sync from cnpmjs.org may contain uppercase characters.
1. 🧶 Update the validation rules, allow to publish existing packages.
---------
> 部分包从 cnpmjs.org 进行同步,可能含有大写字符
1. 🧶 更新校验规则,允许存量包修改
2023-06-27 11:54:02 +08:00
semantic-release-bot
c0415c01f7 Release 3.34.7
[skip ci]

## [3.34.7](https://github.com/cnpm/cnpmcore/compare/v3.34.6...v3.34.7) (2023-06-25)

### Bug Fixes

* sync self pkg ([#532](https://github.com/cnpm/cnpmcore/issues/532)) ([ada3e22](ada3e220a1))
2023-06-25 15:03:34 +00:00
elrrrrrrr
ada3e220a1 fix: sync self pkg (#532)
> During the syncUpstream process, it will attempt to create sync
repeatedly until it times out, when the pkg has been published in the
self registry.
1. 🐞 When executing the syncTask, filter out scenarios where the target
registry is the self registry.
-------

> 包迁移至当前 registry 时,收到同步请求会产生无效的同步任务,当 `syncUpstream` 时,会尝试重复创建 sync
直到超时。
1. 🐞 syncTask 执行时,先过滤目标 registry 是当前 registry 的场景。
2023-06-25 23:02:11 +08:00
semantic-release-bot
fece88201d Release 3.34.6
[skip ci]

## [3.34.6](https://github.com/cnpm/cnpmcore/compare/v3.34.5...v3.34.6) (2023-06-25)

### Bug Fixes

* only auto install s3-cnpmcore on Node.js 18+ ([#531](https://github.com/cnpm/cnpmcore/issues/531)) ([c9d9ce8](c9d9ce8205))
2023-06-25 09:49:58 +00:00
fengmk2
c9d9ce8205 fix: only auto install s3-cnpmcore on Node.js 18+ (#531)
closes https://github.com/cnpm/cnpmcore/issues/530
2023-06-25 17:48:39 +08:00
semantic-release-bot
ff8a81cde4 Release 3.34.5
[skip ci]

## [3.34.5](https://github.com/cnpm/cnpmcore/compare/v3.34.4...v3.34.5) (2023-06-21)

### Bug Fixes

* create sync task by 'GET /:fullname/-/:filenameWithVersion.tgz' ([#526](https://github.com/cnpm/cnpmcore/issues/526)) ([5ceaa6b](5ceaa6b8dd))
2023-06-21 00:48:33 +00:00
hezhengxu2018
5ceaa6b8dd fix: create sync task by 'GET /:fullname/-/:filenameWithVersion.tgz' (#526)
used by pnpm project with lock

closes https://github.com/cnpm/cnpmcore/issues/525
2023-06-21 08:46:48 +08:00
semantic-release-bot
110fdaef55 Release 3.34.4
[skip ci]

## [3.34.4](https://github.com/cnpm/cnpmcore/compare/v3.34.3...v3.34.4) (2023-06-20)

### Bug Fixes

* avoid db query on sync mode all ([#527](https://github.com/cnpm/cnpmcore/issues/527)) ([49855d9](49855d97e5)), closes [/github.com/cnpm/cnpmcore/pull/522/files#r1234655574](https://github.com/cnpm//github.com/cnpm/cnpmcore/pull/522/files/issues/r1234655574)
2023-06-20 02:16:40 +00:00
fengmk2
49855d97e5 fix: avoid db query on sync mode all (#527)
https://github.com/cnpm/cnpmcore/pull/522/files#r1234655574
2023-06-20 10:15:26 +08:00
semantic-release-bot
84499bc9f8 Release 3.34.3
[skip ci]

## [3.34.3](https://github.com/cnpm/cnpmcore/compare/v3.34.2...v3.34.3) (2023-06-17)

### Bug Fixes

* merge docker ENV into one layer ([#523](https://github.com/cnpm/cnpmcore/issues/523)) ([eb91b83](eb91b834c0))
2023-06-17 16:32:14 +00:00
fengmk2
eb91b834c0 fix: merge docker ENV into one layer (#523) 2023-06-18 00:29:46 +08:00
semantic-release-bot
182bc8b3e7 Release 3.34.2
[skip ci]

## [3.34.2](https://github.com/cnpm/cnpmcore/compare/v3.34.1...v3.34.2) (2023-06-17)

### Bug Fixes

* should redirect when nfs adapter support url ([#522](https://github.com/cnpm/cnpmcore/issues/522)) ([3d6864c](3d6864c713))
2023-06-17 16:13:29 +00:00
hezhengxu2018
3d6864c713 fix: should redirect when nfs adapter support url (#522)
will check package version on database before redirect to nfs store url

closes https://github.com/cnpm/cnpmcore/issues/521

---------

Co-authored-by: fengmk2 <fengmk2@gmail.com>
2023-06-18 00:02:12 +08:00
semantic-release-bot
713c879af0 Release 3.34.1
[skip ci]

## [3.34.1](https://github.com/cnpm/cnpmcore/compare/v3.34.0...v3.34.1) (2023-06-14)

### Bug Fixes

* add block package by packageId and name function ([#514](https://github.com/cnpm/cnpmcore/issues/514)) ([b81b2a0](b81b2a03f8))
2023-06-14 09:08:42 +00:00
Ke Wu
b81b2a03f8 fix: add block package by packageId and name function (#514) 2023-06-14 17:07:23 +08:00
semantic-release-bot
f588e272b3 Release 3.34.0
[skip ci]

## [3.34.0](https://github.com/cnpm/cnpmcore/compare/v3.33.0...v3.34.0) (2023-06-13)

### Features

* sync package readme ([#512](https://github.com/cnpm/cnpmcore/issues/512)) ([f64e273](f64e273566))
* use unpkg README.md to update package version readme property ([#511](https://github.com/cnpm/cnpmcore/issues/511)) ([56d8e1a](56d8e1ad87))
2023-06-13 03:29:45 +00:00
fengmk2
f64e273566 feat: sync package readme (#512) 2023-06-13 11:28:31 +08:00
fengmk2
56d8e1ad87 feat: use unpkg README.md to update package version readme property (#511)
closes https://github.com/cnpm/cnpmcore/issues/481
2023-06-13 08:42:26 +08:00
semantic-release-bot
7df9648e6b Release 3.33.0
[skip ci]

## [3.33.0](https://github.com/cnpm/cnpmcore/compare/v3.32.0...v3.33.0) (2023-06-12)

### Features

* support Dockerfile and S3 nfs ([#509](https://github.com/cnpm/cnpmcore/issues/509)) ([f61ef1c](f61ef1c058))
2023-06-12 01:20:41 +00:00
fengmk2
f61ef1c058 feat: support Dockerfile and S3 nfs (#509)
closes https://github.com/cnpm/cnpmcore/issues/507
2023-06-12 09:19:03 +08:00
semantic-release-bot
428b1f3299 Release 3.32.0
[skip ci]

## [3.32.0](https://github.com/cnpm/cnpmcore/compare/v3.31.0...v3.32.0) (2023-06-11)

### Features

* sync specified versions ([#487](https://github.com/cnpm/cnpmcore/issues/487)) ([a9bb81a](a9bb81adfb))
2023-06-11 12:14:23 +00:00
hezhengxu2018
a9bb81adfb feat: sync specified versions (#487)
允许同步指定版本
---------
Allow to sync the specified versions
2023-06-11 20:12:59 +08:00
semantic-release-bot
1dbf481a11 Release 3.31.0
[skip ci]

## [3.31.0](https://github.com/cnpm/cnpmcore/compare/v3.30.2...v3.31.0) (2023-06-11)

### Features

* Support environment variable for config 🚀  ([#489](https://github.com/cnpm/cnpmcore/issues/489)) ([d4d7a3d](d4d7a3d7c8))
2023-06-11 07:08:07 +00:00
Nadeshiko Manju
d4d7a3d7c8 feat: Support environment variable for config 🚀 (#489)
closes https://github.com/cnpm/cnpmcore/issues/465

---------

Co-authored-by: fengmk2 <fengmk2@gmail.com>
2023-06-11 15:06:37 +08:00
semantic-release-bot
f79ac030c1 Release 3.30.2
[skip ci]

## [3.30.2](https://github.com/cnpm/cnpmcore/compare/v3.30.1...v3.30.2) (2023-06-09)

### Bug Fixes

* sync DOWNLOAD_PATHS code update ([#506](https://github.com/cnpm/cnpmcore/issues/506)) ([bf2bf64](bf2bf64532))
2023-06-09 06:48:38 +00:00
fengmk2
bf2bf64532 fix: sync DOWNLOAD_PATHS code update (#506)
closes https://github.com/cnpm/cnpmcore/issues/505
2023-06-09 14:47:01 +08:00
semantic-release-bot
816fa16526 Release 3.30.1
[skip ci]

## [3.30.1](https://github.com/cnpm/cnpmcore/compare/v3.30.0...v3.30.1) (2023-06-07)

### Bug Fixes

* type import ([#502](https://github.com/cnpm/cnpmcore/issues/502)) ([27ee3d6](27ee3d61a3))
2023-06-07 10:11:51 +00:00
elrrrrrrr
27ee3d61a3 fix: type import (#502)
> fix loading issues in the integrate mode
* 🧶 Modify the spec validation to only support `version` `tag` `range`
and not `alias` method.
* 🤖 Remove ts baseUrl config, use relative paths uniformly.

----

> 修复集成模式加载失败
* 🧶 修改 Spec 校验,仅支持 `version` `tag` `range`,不支持 `alias` 方式
* 🤖 去除 ts baseUrl 配置,统一使用相对路径
2023-06-07 18:10:36 +08:00
semantic-release-bot
2001168ab8 Release 3.30.0
[skip ci]

## [3.30.0](https://github.com/cnpm/cnpmcore/compare/v3.29.4...v3.30.0) (2023-06-07)

### Features

* bulk update maintainers ([#501](https://github.com/cnpm/cnpmcore/issues/501)) ([1b6dce6](1b6dce65ac))
2023-06-07 08:13:12 +00:00
elrrrrrrr
1b6dce65ac feat: bulk update maintainers (#501)
> Some dirty data of userPrefix in the maintainers cannot be cleared
during synchronization. Adjust the synchronization logic for
maintainers.
1. 🧶 When synchronizing packages, uniformly update the manifest dist.
2. 🛠️ `replacereplacePackageMaintainers` =>
`replacePackageMaintainersAndDist`
-----
> 部分旧包 maintainer 中的 userPrefix 脏数据同步时由于没有成员变更,无法清除,调整 maintainer 同步逻辑
1. 🧶 包同步时,统一更新 maintainer 对应的 manifest dist
2. 🛠️ `replacereplacePackageMaintainers` =>
`replacePackageMaintainersAndDist`
2023-06-07 16:12:11 +08:00
semantic-release-bot
f1bbd267b4 Release 3.29.4
[skip ci]

## [3.29.4](https://github.com/cnpm/cnpmcore/compare/v3.29.3...v3.29.4) (2023-06-07)

### Bug Fixes

* validate pkg@version spec ([#500](https://github.com/cnpm/cnpmcore/issues/500)) ([a9d2ff7](a9d2ff7e54))
2023-06-07 08:01:13 +00:00
elrrrrrrr
a9d2ff7e54 fix: validate pkg@version spec (#500)
> follow https://github.com/cnpm/cnpmcore/pull/495 after supporting
spec, adjust the parameter validation rules
1. 🆕 Add `Spec` validation rule, validating the spec by npa
2. 🛠️ Upgrade versionOrTag to versionSpec to support semver expressions,
such as `^2.x || > 3.x`
---------

> follow https://github.com/cnpm/cnpmcore/pull/495 支持 spec 后,调整参数校验规则
1. 🆕 新增 `Sepc` 校验规则,使用 npa 拼接包名进行验证
2. 🛠️ versionOrTag 升级为 versionSpec 支持 semver 表达式,例如 `^2.x || > 3.x`
2023-06-07 15:59:58 +08:00
semantic-release-bot
efb6412bac Release 3.29.3
[skip ci]

## [3.29.3](https://github.com/cnpm/cnpmcore/compare/v3.29.2...v3.29.3) (2023-06-07)

### Bug Fixes

* auto fix invalid version to any version ([#499](https://github.com/cnpm/cnpmcore/issues/499)) ([0cf6470](0cf6470993))
2023-06-07 02:02:51 +00:00
fengmk2
0cf6470993 fix: auto fix invalid version to any version (#499) 2023-06-07 10:01:40 +08:00
fengmk2
7fbf79f038 chore: print more error infomation 2023-06-07 09:18:58 +08:00
semantic-release-bot
06eaa13116 Release 3.29.2
[skip ci]

## [3.29.2](https://github.com/cnpm/cnpmcore/compare/v3.29.1...v3.29.2) (2023-06-07)

### Bug Fixes

* ignore fix invalid version ([#498](https://github.com/cnpm/cnpmcore/issues/498)) ([f5607c0](f5607c0a87))
2023-06-07 00:59:48 +00:00
fengmk2
f5607c0a87 fix: ignore fix invalid version (#498) 2023-06-07 08:58:35 +08:00
semantic-release-bot
cb5117aa72 Release 3.29.1
[skip ci]

## [3.29.1](https://github.com/cnpm/cnpmcore/compare/v3.29.0...v3.29.1) (2023-06-07)

### Bug Fixes

* fix encoded semver spec ([#497](https://github.com/cnpm/cnpmcore/issues/497)) ([aa39ced](aa39cedf35))
2023-06-07 00:36:25 +00:00
killa
aa39cedf35 fix: fix encoded semver spec (#497) 2023-06-07 08:34:37 +08:00
semantic-release-bot
183223b67d Release 3.29.0
[skip ci]

## [3.29.0](https://github.com/cnpm/cnpmcore/compare/v3.28.0...v3.29.0) (2023-06-06)

### Features

* infer userPrefix when update maintainers  ([#496](https://github.com/cnpm/cnpmcore/issues/496)) ([e02ea2a](e02ea2a3e5))
2023-06-06 13:00:58 +00:00
elrrrrrrr
e02ea2a3e5 feat: infer userPrefix when update maintainers (#496)
> When update members , infer the userPrefix by default to be compatible
with the default npm cli.
* 🧶 When adding a member, query the userPrefix corresponding to the
registry.
```
 $ npm owner add elrrrrrrr @cnpm/example --registry=http://127.0.0.1:7001
 $ npm owner add cnpm:elrrrrrrr @cnpm/example --registry=http://127.0.0.1:7001
```

-------
> 使用 cli 更新成员时,默认推导 userPrefix 信息,兼容 npm 客户端默认流程
* 🧶 添加成员时,查询 registry 对应的 userPrefix
```
 $ npm owner add elrrrrrrr @cnpm/example --registry=http://127.0.0.1:7001               
 $ npm owner add cnpm:elrrrrrrr @cnpm/example --registry=http://127.0.0.1:7001
```
2023-06-06 20:59:46 +08:00
semantic-release-bot
df8e114a5b Release 3.28.0
[skip ci]

## [3.28.0](https://github.com/cnpm/cnpmcore/compare/v3.27.0...v3.28.0) (2023-06-06)

### Features

* impl fast semver search ([#495](https://github.com/cnpm/cnpmcore/issues/495)) ([a7fd3a8](a7fd3a8c8a))
2023-06-06 12:57:26 +00:00
killa
a7fd3a8c8a feat: impl fast semver search (#495)
Including SQL change: `sql/3.28.0.sql`
2023-06-06 20:56:05 +08:00
semantic-release-bot
926d724793 Release 3.27.0
[skip ci]

## [3.27.0](https://github.com/cnpm/cnpmcore/compare/v3.26.0...v3.27.0) (2023-06-04)

### Features

* source registry manifest ([#493](https://github.com/cnpm/cnpmcore/issues/493)) ([bbec9a3](bbec9a38bd))
2023-06-04 08:58:24 +00:00
elrrrrrrr
bbec9a38bd feat: source registry manifest (#493)
> Add the `_source_registry_manifest` in pkgFullManifest.
* 🧶 Set registryInfo during reading because of the excessive existing
data.
* ♻️ The change takes effect when redis cache expired.
----------

> 在 pkgFullManifest 中添加 _source_registry_manifest 相关字段
1. 🧶 存量数据过多,在读取时统一设置,展示当前对应 registryId
2. ♻️ 在读取 db 时生效,需要等缓存过期
2023-06-04 16:57:19 +08:00
semantic-release-bot
3a423a2eb2 Release 3.26.0
[skip ci]

## [3.26.0](https://github.com/cnpm/cnpmcore/compare/v3.25.1...v3.26.0) (2023-06-04)

### Features

* whoami return granular token info ([#494](https://github.com/cnpm/cnpmcore/issues/494)) ([d0d2f78](d0d2f78d7b))
2023-06-04 08:56:19 +00:00
elrrrrrrr
d0d2f78d7b feat: whoami return granular token info (#494)
> add token info when invoke `whoami` to notify the caller about the
token's current status.

* 🧶 Add token information to the "whoami" interface.
* 🔨 Modify the query logic for allowedPackages uniformly within the
repository.
-----------

> 当使用 granularToken 调用 whoami 信息时,返回当前 token 信息,告知调用方当前 token 状态
* 🧶 在whoami 接口中添加 token 信息
* 🔨 修改 allowedPackages 查询逻辑,统一在 repository 中集成
2023-06-04 16:54:48 +08:00
semantic-release-bot
ad9adf7cd0 Release 3.25.1
[skip ci]

## [3.25.1](https://github.com/cnpm/cnpmcore/compare/v3.25.0...v3.25.1) (2023-06-03)

### Bug Fixes

* hook enable ([#492](https://github.com/cnpm/cnpmcore/issues/492)) ([40e2f92](40e2f92b94))
2023-06-03 11:12:00 +00:00
elrrrrrrr
40e2f92b94 fix: hook enable (#492)
> Fix hookEnable config does not take effect
* 🐞 Modify config key in changesStream event
* 🤖 Add corresponding unit tests
---------
> 修复 hookEnable 开关配置不生效的问题
* 🐞 修改 events 中配置获取异常
* 🤖 添加对应单测
2023-06-03 19:10:33 +08:00
semantic-release-bot
e88a610011 Release 3.25.0
[skip ci]

## [3.25.0](https://github.com/cnpm/cnpmcore/compare/v3.24.0...v3.25.0) (2023-06-02)

### Features

* add _npmUser ([#491](https://github.com/cnpm/cnpmcore/issues/491)) ([f7b5d5a](f7b5d5af12))
2023-06-02 04:38:14 +00:00
elrrrrrrr
f7b5d5af12 feat: add _npmUser (#491)
> For private packages published in the current registry, add the
"_npmUser" field to align with the npm registry.

* 🧶 Add the "_npmUser" field for new scenarios, without modifying the
abbreviated data. Use the following command: curl -H 'Accept:
application/vnd.npm.install-v1+json'
'https://registry.npmjs.org/cnpmcore'
* ♻️ Existing data cannot be traced and will not be compensated.
-----

> 对于在当前 registry 发布的私有包,添加 _npmUser 字段,和公网 registry 保持一致
* 🧶 新增 _npmUser 字段,abbreviated 场景不做修改, (via `curl -H 'Accept:
application/vnd.npm.install-v1+json'
'https://registry.npmjs.org/cnpmcore'`)
* ♻️ 存量数据无法回溯,不做补偿
2023-06-02 12:36:53 +08:00
semantic-release-bot
f30e517f9e Release 3.24.0
[skip ci]

## [3.24.0](https://github.com/cnpm/cnpmcore/compare/v3.23.2...v3.24.0) (2023-06-01)

### Features

* token last used at ([#488](https://github.com/cnpm/cnpmcore/issues/488)) ([3a8a91a](3a8a91ae0b))
2023-06-01 11:19:35 +00:00
elrrrrrrr
3a8a91ae0b feat: token last used at (#488)
> Add the lastUsedBy field to the Token model

* 🧶 Add `lastUsedBy` field to the token, for platform display.
* 🐞 Fix the issue where the graunlarToken is not expired in read-only
scenarios.
------
> 为 Token 模型添加 lastUsedBy 字段
* 🧶 新增 lastUsedBy 字段,记录 token 最近使用时间,用于平台展示
* 🐞 修复 graunlarToken 过期时,只读场景没有禁用的问题
2023-06-01 19:18:17 +08:00
semantic-release-bot
0c1e2ceb7f Release 3.23.2
[skip ci]

## [3.23.2](https://github.com/cnpm/cnpmcore/compare/v3.23.1...v3.23.2) (2023-05-31)

### Bug Fixes

* unpkg support non-npm publish tgz file ([#485](https://github.com/cnpm/cnpmcore/issues/485)) ([5fe883f](5fe883f878)), closes [/github.com/cnpm/cnpmcore/issues/452#issuecomment-1570077310](https://github.com/cnpm//github.com/cnpm/cnpmcore/issues/452/issues/issuecomment-1570077310)
2023-05-31 12:44:11 +00:00
fengmk2
5fe883f878 fix: unpkg support non-npm publish tgz file (#485)
https://github.com/cnpm/cnpmcore/issues/452#issuecomment-1570077310
2023-05-31 20:42:19 +08:00
semantic-release-bot
a7258aa7ec Release 3.23.1
[skip ci]

## [3.23.1](https://github.com/cnpm/cnpmcore/compare/v3.23.0...v3.23.1) (2023-05-30)

### Bug Fixes

* use package version publishTime instead of file mtime ([#483](https://github.com/cnpm/cnpmcore/issues/483)) ([68f6b6b](68f6b6b944))
2023-05-30 15:58:42 +00:00
fengmk2
68f6b6b944 fix: use package version publishTime instead of file mtime (#483)
closes https://github.com/cnpm/cnpmcore/issues/482
2023-05-30 23:57:34 +08:00
semantic-release-bot
9e5e555552 Release 3.23.0
[skip ci]

## [3.23.0](https://github.com/cnpm/cnpmcore/compare/v3.22.3...v3.23.0) (2023-05-29)

### Features

* export getUserAndToken ([#480](https://github.com/cnpm/cnpmcore/issues/480)) ([aa4fdd3](aa4fdd3545))
2023-05-29 17:39:20 +00:00
elrrrrrrr
aa4fdd3545 feat: export getUserAndToken (#480)
> Export `getUserAndToken` method for authorization parsing in integrate
mode.

1. 🧶 TokenService adds getUserAndToken method

----------

> 暴露 `getUserAndToken` 方法进行 authorization 解析,面向集成模式的场景
1. 🧶 TokenService 新增 `getUserAndToken` 方法

---------

Co-authored-by: fengmk2 <fengmk2@gmail.com>
2023-05-30 01:37:22 +08:00
semantic-release-bot
1b89b64356 Release 3.22.3
[skip ci]

## [3.22.3](https://github.com/cnpm/npmcore/compare/v3.22.2...v3.22.3) (2023-05-29)

### Bug Fixes

* unpkg redirect ([#479](https://github.com/cnpm/npmcore/issues/479)) ([c395c79](c395c7906b))
2023-05-29 10:03:47 +00:00
elrrrrrrr
c395c7906b fix: unpkg redirect (#479) 2023-05-29 18:02:23 +08:00
fengmk2
cc01398a16 refactor: use binaries.prisma.sh to download prisma files (#478) 2023-05-25 17:52:46 +08:00
semantic-release-bot
be228399d1 Release 3.22.2
[skip ci]

## [3.22.2](https://github.com/cnpm/npmcore/compare/v3.22.1...v3.22.2) (2023-05-25)

### Bug Fixes

* use S3 url to download file ([#477](https://github.com/cnpm/npmcore/issues/477)) ([9bed829](9bed829628)), closes [/github.com/cnpm/cnpmcore/issues/472#issuecomment-1562452369](https://github.com/cnpm//github.com/cnpm/cnpmcore/issues/472/issues/issuecomment-1562452369)
2023-05-25 08:11:26 +00:00
fengmk2
9bed829628 fix: use S3 url to download file (#477)
https://github.com/cnpm/cnpmcore/issues/472#issuecomment-1562452369
2023-05-25 16:10:18 +08:00
semantic-release-bot
9c60a597f2 Release 3.22.1
[skip ci]

## [3.22.1](https://github.com/cnpm/npmcore/compare/v3.22.0...v3.22.1) (2023-05-25)

### Bug Fixes

* refactor config type ([#476](https://github.com/cnpm/npmcore/issues/476)) ([ebc8c98](ebc8c98fa4))
2023-05-25 07:20:59 +00:00
elrrrrrrr
ebc8c98fa4 fix: refactor config type (#476)
> New CnpmcoreConfig type to handle compatibility issues
https://github.com/cnpm/cnpmcore/pull/475
1. 🆕 add a new CnpmcoreConfig in port/config, referenced in
config.default.ts
2. 🆕 add `ChangesStreamMode` enums.
-------------
> 新增 CnpmcoreConfig 类型处理兼容问题 https://github.com/cnpm/cnpmcore/pull/475
1. 🆕 新增 CnpmcoreConfig,config.default.ts 中进行引用
2. 🆕 新增 `ChangesStreamMode` 枚举


![image](https://github.com/cnpm/cnpmcore/assets/5574625/1fd4afd0-0739-4021-a134-2311bbf78713)
2023-05-25 15:19:51 +08:00
semantic-release-bot
517bb8e8d4 Release 3.22.0
[skip ci]

## [3.22.0](https://github.com/cnpm/npmcore/compare/v3.21.0...v3.22.0) (2023-05-25)

### Features

* sync prisma binary from R2 ([#474](https://github.com/cnpm/npmcore/issues/474)) ([ce4e868](ce4e8681ae))
2023-05-25 03:45:07 +00:00
fengmk2
ce4e8681ae feat: sync prisma binary from R2 (#474)
closes https://github.com/cnpm/cnpmcore/issues/473
2023-05-25 11:43:52 +08:00
fengmk2
26f5eaf438 refactor: jsencrypt and jquery use unpkg (#471) 2023-05-24 11:09:22 +08:00
semantic-release-bot
81865a1790 Release 3.21.0
[skip ci]

## [3.21.0](https://github.com/cnpm/npmcore/compare/v3.20.3...v3.21.0) (2023-05-21)

### Features

* easy config ([#468](https://github.com/cnpm/npmcore/issues/468)) ([9208392](92083924ea))
2023-05-21 13:30:29 +00:00
elrrrrrrr
92083924ea feat: easy config (#468)
> Make the method for tegg integration mode to be more user-friendly.

* 🤖 Automatically add config.cnpmcore type hints.
* 🧶 Export the default `cnpmcoreConfig` , which needs to be explicitly
declared for app config.
* 📚 Supplement the documentation and field definitions.
------

> 对于 egg 集成模式,提供更加友好的自定义配置方式。
* 🤖 自动添加 config.cnpmcore 类型提示
* 🧶 输出默认的 cnpmcoreConfig 对象,应用集成需显式声明,防止新增配置丢失
* 📚 补充文档及字段定义信息


![image](https://github.com/cnpm/cnpmcore/assets/5574625/98d3e0df-32f5-4de5-990a-bc1561cd73be)

---------

Co-authored-by: fengmk2 <fengmk2@gmail.com>
2023-05-21 21:27:50 +08:00
elrrrrrrr
80ab0548f2 refactor: unpublish logic (#467)
> Adjust the logic for unpublishing a package
* 🧶 Determine if a call to unpublish within the removePackageVersion
function
* ♻️ Remove`forceRefresh` in unpublishPackage
-------
> 调整 unpublish package 逻辑
* 🧶 removePackageVersion 内判断是否需要调用 unpublish
* ♻️ unpublishPackage 删除 forceRefresh 逻辑
2023-05-18 22:45:18 +08:00
semantic-release-bot
0a5b0c78d5 Release 3.20.3
[skip ci]

## [3.20.3](https://github.com/cnpm/npmcore/compare/v3.20.2...v3.20.3) (2023-05-18)

### Bug Fixes

* unpublish idempotent ([#466](https://github.com/cnpm/npmcore/issues/466)) ([2a7eacf](2a7eacf27c))
2023-05-18 02:33:25 +00:00
elrrrrrrr
2a7eacf27c fix: unpublish idempotent (#466)
> Fixed the idempotent issue during unpublish pkg, which caused repeated
triggering of change events and endless sync loops for downstream
registries.
* 🐞 Add idempotent check during unpublish; skip when the package has
already been unpublished.
-------------
> 修复 unpublish 时未做幂等控制,导致删包时,不断触发 change 事件,下游 registry 不断 sync 导致任务循环
* 🐞 统一在 unpublish 进行幂等判断,如果该包已 unpublish,则跳过
2023-05-18 10:32:18 +08:00
fengmk2
33628ccacf test: use TypeScript v5 (#425) 2023-05-07 14:54:04 +08:00
semantic-release-bot
e7f5e24b34 Release 3.20.2
[skip ci]

## [3.20.2](https://github.com/cnpm/npmcore/compare/v3.20.1...v3.20.2) (2023-05-06)

### Bug Fixes

* set cache-control default value to "public, max-age=300" ([#462](https://github.com/cnpm/npmcore/issues/462)) ([adda725](adda72566d))
2023-05-06 10:51:15 +00:00
fengmk2
adda72566d fix: set cache-control default value to "public, max-age=300" (#462)
follow registry.npmjs.org default value
2023-05-06 18:50:04 +08:00
semantic-release-bot
42939e99d7 Release 3.20.1
[skip ci]

## [3.20.1](https://github.com/cnpm/npmcore/compare/v3.20.0...v3.20.1) (2023-05-06)

### Bug Fixes

* use nfs download api ([#461](https://github.com/cnpm/npmcore/issues/461)) ([bb16957](bb169577e2))
2023-05-06 08:32:39 +00:00
fengmk2
bb169577e2 fix: use nfs download api (#461) 2023-05-06 16:31:25 +08:00
semantic-release-bot
0858efbc11 Release 3.20.0
[skip ci]

## [3.20.0](https://github.com/cnpm/npmcore/compare/v3.19.3...v3.20.0) (2023-05-06)

### Features

* enable sql logger ([#460](https://github.com/cnpm/npmcore/issues/460)) ([51cd044](51cd044742))
2023-05-06 06:42:49 +00:00
fengmk2
51cd044742 feat: enable sql logger (#460)
And fix registry error response cause data return null bug
2023-05-06 14:41:20 +08:00
fengmk2
a647317c2c chore: update contributors
[skip ci]
2023-05-06 12:40:48 +08:00
semantic-release-bot
098277e274 Release 3.19.3
[skip ci]

## [3.19.3](https://github.com/cnpm/npmcore/compare/v3.19.2...v3.19.3) (2023-05-06)

### Bug Fixes

* ignore hidden dir files ([#459](https://github.com/cnpm/npmcore/issues/459)) ([637e8ad](637e8ad9a0))
2023-05-06 04:38:37 +00:00
fengmk2
637e8ad9a0 fix: ignore hidden dir files (#459)
avoid oss upload fail

> [SignatureDoesNotMatchError]: The request signature we calculated does
not match the signature you provided. Check your key and signing method.
2023-05-06 12:37:23 +08:00
semantic-release-bot
5223e8ca40 Release 3.19.2
[skip ci]

## [3.19.2](https://github.com/cnpm/npmcore/compare/v3.19.1...v3.19.2) (2023-05-05)

### Bug Fixes

* ignore non-file on tar entry filter ([#458](https://github.com/cnpm/npmcore/issues/458)) ([7e63e7f](7e63e7f0eb))
2023-05-05 17:54:58 +00:00
fengmk2
7e63e7f0eb fix: ignore non-file on tar entry filter (#458)
> ENOENT: no such file or directory, stat
'/root/.cnpmcore/downloads/2023/05/06/unpkg_@iov_wallet-providers@1.0.0_0f152162-9cce-4a80-bacc-41271b7aac3f/package'
2023-05-06 01:53:25 +08:00
semantic-release-bot
39b73b18bf Release 3.19.1
[skip ci]

## [3.19.1](https://github.com/cnpm/npmcore/compare/v3.19.0...v3.19.1) (2023-05-05)

### Bug Fixes

* download tgz file to local file before untar it ([#457](https://github.com/cnpm/npmcore/issues/457)) ([90d5046](90d504622a))
2023-05-05 16:25:26 +00:00
fengmk2
90d504622a fix: download tgz file to local file before untar it (#457)
avoid "zlib: unexpected end of file"
2023-05-06 00:24:23 +08:00
semantic-release-bot
5f9a7a8be2 Release 3.19.0
[skip ci]

## [3.19.0](https://github.com/cnpm/npmcore/compare/v3.18.0...v3.19.0) (2023-05-05)

### Features

* support unpkg features ([#456](https://github.com/cnpm/npmcore/issues/456)) ([8ec081a](8ec081acd6))
2023-05-05 15:23:16 +00:00
fengmk2
8ec081acd6 feat: support unpkg features (#456)
WARN: include sql change

😄 Follow unpkg router
😄 Auto sync files after package version add

closes https://github.com/cnpm/cnpmcore/issues/452
2023-05-05 23:22:08 +08:00
semantic-release-bot
23607d9497 Release 3.18.0
[skip ci]

## [3.18.0](https://github.com/cnpm/npmcore/compare/v3.17.1...v3.18.0) (2023-05-05)

### Features

* sync chrome-for-testing binary ([#455](https://github.com/cnpm/npmcore/issues/455)) ([dd7d73e](dd7d73e871))
2023-05-05 08:11:58 +00:00
elrrrrrrr
dd7d73e871 feat: sync chrome-for-testing binary (#455)
> https://github.com/puppeteer/puppeteer/issues/10131 Puppeteer has
updated the default browser to Chrome and added the corresponding
implementation as follows:

🧶 Added a new category `/-/binary/` for Chrome , exp:
`/-/binary/chrome-for-testing/113.0.5672.63/mac-arm64/chrome-mac-arm64.zip`

-----------

> https://github.com/puppeteer/puppeteer/issues/10131 puppeteer
更新了默认浏览器为 chrome,新增对应实现

🧶 `/-/binary/` 新增 chrome binary 分类,
示例链接`/-/binary/chrome-for-testing/113.0.5672.63/mac-arm64/chrome-mac-arm64.zip`
2023-05-05 16:10:50 +08:00
semantic-release-bot
c1fc1a58d4 Release 3.17.1
[skip ci]

## [3.17.1](https://github.com/cnpm/npmcore/compare/v3.17.0...v3.17.1) (2023-05-04)

### Bug Fixes

* calculate _hasShrinkwrap on server-side if not present ([#450](https://github.com/cnpm/npmcore/issues/450)) ([db59bd6](db59bd6cd9))
2023-05-04 08:21:08 +00:00
飞超
db59bd6cd9 fix: calculate _hasShrinkwrap on server-side if not present (#450) 2023-05-04 16:19:55 +08:00
semantic-release-bot
1f592f4b2f Release 3.17.0
[skip ci]

## [3.17.0](https://github.com/cnpm/npmcore/compare/v3.16.0...v3.17.0) (2023-04-25)

### Features

* add source registry name in manifest ([#448](https://github.com/cnpm/npmcore/issues/448)) ([f891aed](f891aedea8))
2023-04-25 08:41:50 +00:00
elrrrrrrr
f891aedea8 feat: add source registry name in manifest (#448)
> Add a private field, _source_registry_name in the version manifest.
* 🧶 Add related types for PackageManifestType and adjust relevant unit
tests.
* 🤖 Update the workflow trigger.
* ♻️ No compensation will be made for the _source_registry_name field in
the existing packageVersion.
-------

> 在 version manifest 中新增私有字段,_source_registry_name 用于标记
* 🧶 新增 PackageManifestType 相关类型,并调整相关单测
* 🤖 调整 workflow 触发时机,不限制 target 分支
* ♻️ 存量 packageVersion 内 _source_registry_name 不做补偿
2023-04-25 16:40:37 +08:00
semantic-release-bot
ae191f3283 Release 3.16.0
[skip ci]

## [3.16.0](https://github.com/cnpm/npmcore/compare/v3.15.0...v3.16.0) (2023-04-21)

### Features

* add health checker for slb ([#445](https://github.com/cnpm/npmcore/issues/445)) ([4dcfe89](4dcfe89575))
2023-04-21 07:49:28 +00:00
fengmk2
4dcfe89575 feat: add health checker for slb (#445)
closes https://github.com/cnpm/cnpmcore/issues/444
2023-04-21 15:48:13 +08:00
semantic-release-bot
e26299a768 Release 3.15.0
[skip ci]

## [3.15.0](https://github.com/cnpm/npmcore/compare/v3.14.0...v3.15.0) (2023-04-21)

### Features

* create sync task with auth header ([#442](https://github.com/cnpm/npmcore/issues/442)) ([d95c58b](d95c58b5ce))
2023-04-21 01:49:29 +00:00
hezhengxu2018
d95c58b5ce feat: create sync task with auth header (#442)
The upstream repository carries authentication header information via
task parameters when alwaysAuth is enabled

---

上游仓库开启 alwaysAuth 时通过任务参数携带认证头信息
2023-04-21 09:48:15 +08:00
semantic-release-bot
59706ab97e Release 3.14.0
[skip ci]

## [3.14.0](https://github.com/cnpm/npmcore/compare/v3.13.2...v3.14.0) (2023-04-20)

### Features

* support granular token ([#443](https://github.com/cnpm/npmcore/issues/443)) ([92ddf2c](92ddf2c8c3))
2023-04-20 07:28:37 +00:00
elrrrrrrr
92ddf2c8c3 feat: support granular token (#443)
> 🚀 Added implementation related to
[granularToken](https://docs.npmjs.com/about-access-tokens#about-granular-access-tokens),
mainly used for web authorization scenarios.

* 📝 Added `1.14.0.sql` to add fields and `token_packages` for
granularToken.
* 🛣️ Added gat related routes, including `create`, `query`, and `delete`
api.
* 🌟 Added `tokenService` to check granularToken access.
* 🔄 Modified Token to perform options and data attribute conversions
internally in the model.
-----------

> 🚀 新增
[granularToken](https://docs.npmjs.com/about-access-tokens#about-granular-access-tokens)
相关实现,主要用于 web 端授权场景
* 📝 新增 `1.14.0.sql` 添加 granularToken 相关字段及 `token_packages` 中间表
* 🛣️ 新增 gat 相关路由,包括`创建`、`查询`、`删除`接口
* 🌟 新增 `tokenService` ,处理 granularToken 鉴权
* 🔄 修改 Token ,在 model 内部进行 options 和 data 属性转换
2023-04-20 15:27:26 +08:00
semantic-release-bot
6961ffb92d Release 3.13.2
[skip ci]

## [3.13.2](https://github.com/cnpm/npmcore/compare/v3.13.1...v3.13.2) (2023-04-10)

### Bug Fixes

* skip tag name out of utf8mb3 ([#440](https://github.com/cnpm/npmcore/issues/440)) ([a64c90b](a64c90b28d))
2023-04-10 08:25:41 +00:00
hezhengxu2018
a64c90b28d fix: skip tag name out of utf8mb3 (#440)
closes https://github.com/cnpm/cnpmcore/issues/438
2023-04-10 16:24:32 +08:00
semantic-release-bot
6177856c9e Release 3.13.1
[skip ci]

## [3.13.1](https://github.com/cnpm/npmcore/compare/v3.13.0...v3.13.1) (2023-04-10)

### Bug Fixes

* update webauth default URL to registry ([#432](https://github.com/cnpm/npmcore/issues/432)) ([cf95d7d](cf95d7dce4))
2023-04-10 06:37:31 +00:00
zhangyuantao
cf95d7dce4 fix: update webauth default URL to registry (#432) 2023-04-10 14:36:05 +08:00
semantic-release-bot
79a5937b74 Release 3.13.0
[skip ci]

## [3.13.0](https://github.com/cnpm/npmcore/compare/v3.12.1...v3.13.0) (2023-04-07)

### Features

* support npm access command ([#436](https://github.com/cnpm/npmcore/issues/436)) ([0ffb614](0ffb61484e))
2023-04-07 04:14:50 +00:00
elrrrrrrr
0ffb61484e feat: support npm access command (#436)
> Supports partial npm access query commands.
https://github.com/cnpm/cnpmcore/issues/64

* The following commands are supported:
  * `npm access list packages [<user>|<scope>|<scope:team> [<package>]`
  * `npm access list collaborators [<package> [<user>]]`
* Added `/-/package/:fullname/collaborators` and
`/-/org/:username/package` interfaces.
* Error code logic is consistent with the npm registry.

--------------

> 支持部分 npm access 查询命令 https://github.com/cnpm/cnpmcore/issues/64
* 支持如下命令:
  * `npm access list packages [<user>|<scope>|<scope:team> [<package>]`
  * `npm access list collaborators [<package> [<user>]]`
* 新增 `/-/package/:fullname/collaborators` 及 `/-/org/:username/package`
接口
* 错误码逻辑和 npm registry 保持一致
2023-04-07 12:13:27 +08:00
semantic-release-bot
eaf88bdf40 Release 3.12.1
[skip ci]

## [3.12.1](https://github.com/cnpm/npmcore/compare/v3.12.0...v3.12.1) (2023-04-07)

### Bug Fixes

* allow to remove the package entity ([#437](https://github.com/cnpm/npmcore/issues/437)) ([613e0a1](613e0a11db))
2023-04-07 02:35:02 +00:00
fengmk2
613e0a11db fix: allow to remove the package entity (#437)
closes https://github.com/cnpm/cnpmcore/issues/435
2023-04-07 10:33:44 +08:00
semantic-release-bot
570d346657 Release 3.12.0
[skip ci]

## [3.12.0](https://github.com/cnpm/npmcore/compare/v3.11.2...v3.12.0) (2023-04-06)

### Features

* allow admin to sync package only ([#434](https://github.com/cnpm/npmcore/issues/434)) ([c5ac715](c5ac715b2b)), closes [#412](https://github.com/cnpm/npmcore/issues/412)
2023-04-06 04:26:12 +00:00
hezhengxu2018
c5ac715b2b feat: allow admin to sync package only (#434)
closes #412
2023-04-06 12:24:41 +08:00
semantic-release-bot
52a60ca6dd Release 3.11.2
[skip ci]

## [3.11.2](https://github.com/cnpm/npmcore/compare/v3.11.1...v3.11.2) (2023-04-03)

### Bug Fixes

* init sync spec registry ([#433](https://github.com/cnpm/npmcore/issues/433)) ([eedfb2b](eedfb2bb86))
2023-04-03 03:43:55 +00:00
elrrrrrrr
eedfb2bb86 fix: init sync spec registry (#433)
> Fixed the issue where the registry was not correctly matched when
synchronizing scoped packages for the first time
* Add scope params in initSpecRegistry
------------
> 修复初次同步 scope 包,未正确匹配 registry 的问题
* 修改 initSpecRegistry 方法,统一传入 scope 参数
2023-04-03 11:42:35 +08:00
semantic-release-bot
30e9140d6c Release 3.11.1
[skip ci]

## [3.11.1](https://github.com/cnpm/npmcore/compare/v3.11.0...v3.11.1) (2023-03-30)

### Bug Fixes

* timeout handler not work ([#430](https://github.com/cnpm/npmcore/issues/430)) ([3f83808](3f838080ca))
* update login assets cdn url ([#429](https://github.com/cnpm/npmcore/issues/429)) ([4ee410a](4ee410a62e))
2023-03-30 05:12:17 +00:00
elrrrrrrr
3f838080ca fix: timeout handler not work (#430)
> 💥 TaskTimeoutHandler did not have try-catch, the redis lock will cause
all queues to fail when a single task update failed.

* 🛡️ Added try-catch statements in TaskTimeoutHandler.
* 🚧 Restricted updates to the primary key when updating the model in
ModelConvertor.

---------------

> 💥 TaskTimeoutHandler 未添加 try-catch,且有同步锁,导致单个任务更新异常时,所有队列不生效

* 🛡️ TaskTimeoutHandler 统一添加 try-catch
* 🚧 ModelConvertor 更新模型时,统一限制不允许更新主键
2023-03-30 13:11:01 +08:00
semantic-release-bot
8c6ce1b5b9 Release 3.11.1
[skip ci]

## [3.11.1](https://github.com/cnpm/npmcore/compare/v3.11.0...v3.11.1) (2023-03-28)

### Bug Fixes

* update login assets cdn url ([#429](https://github.com/cnpm/npmcore/issues/429)) ([4ee410a](4ee410a62e))
2023-03-28 09:26:57 +00:00
LiWanglin
4ee410a62e fix: update login assets cdn url (#429)
使用 gw.alipayobjects.com 的CDN源替换 bootcdn
-------------------------------
Update the CDN of JS resource to gw.alipayobjects.com

Co-authored-by: lanxiu.lwl <lanxiu.lwl@alipay.com>
2023-03-28 17:25:40 +08:00
semantic-release-bot
9d66d35a41 Release 3.11.0
[skip ci]

## [3.11.0](https://github.com/cnpm/npmcore/compare/v3.10.0...v3.11.0) (2023-03-27)

### Features

* support webauthn ([#422](https://github.com/cnpm/npmcore/issues/422)) ([1b8512b](1b8512b321))
2023-03-27 10:28:33 +00:00
LiWanglin
1b8512b321 feat: support webauthn (#422)
1. webauth 由 authentication 改造为 session,并增加 web 登录页面,更安全
2. 支持 webauthn 的登录方式,可通过配置控制(默认关闭),更高效

---------------

1. use session instead http authentication on webauth
2. support [webauthn](https://webauthn.guide/), you should set
`enableWebAuthn: true` in the configuration

closes https://github.com/cnpm/cnpmcore/issues/236

---------

Co-authored-by: lanxiu.lwl <lanxiu.lwl@alipay.com>
Co-authored-by: elrrrrrrr <elrrrrrrr@gmail.com>
2023-03-27 18:27:07 +08:00
semantic-release-bot
f973c016bc Release 3.10.0
[skip ci]

## [3.10.0](https://github.com/cnpm/npmcore/compare/v3.9.0...v3.10.0) (2023-03-27)

### Features

* redirect not found can be false when syncMode='none' ([#428](https://github.com/cnpm/npmcore/issues/428)) ([91ebd19](91ebd195ce))
2023-03-27 10:14:38 +00:00
hezhengxu2018
91ebd195ce feat: redirect not found can be false when syncMode='none' (#428)
> 在禁止自动创建同步任务时也可以关闭 redirectNotFound,实现在私有化部署时用户仅能使用当前仓库内已有的依赖

--------------

> Allow to turn off redirectNotFound when disabling the automatic
creation of sync tasks, enabling users to use only existing dependencies
in the current repository when deploying privately.

---------

Co-authored-by: fengmk2 <fengmk2@gmail.com>
2023-03-27 18:13:16 +08:00
semantic-release-bot
069ac68c5e Release 3.9.0
[skip ci]

## [3.9.0](https://github.com/cnpm/npmcore/compare/v3.8.0...v3.9.0) (2023-03-20)

### Features

* redis weak ([#426](https://github.com/cnpm/npmcore/issues/426)) ([300f0e4](300f0e4fd9))
2023-03-20 03:42:14 +00:00
elrrrrrrr
300f0e4fd9 feat: redis weak (#426)
> manifest 读取场景弱依赖 redis
* 添加 try-catch 防止 redis 请求失败导致读取失败
* 读取完成后异步设置缓存
----
> Redis dependency from manifest with weak scene dependence:

* Adds try-catch to prevent reading failure from redis interruption.
* Asynchronously sets cache after reading is complete.
2023-03-20 11:40:56 +08:00
elrrrrrrr
5877f71527 chore: remove ts-node (#418)
> Remove ts-node dependency and do DB initialization via npm scripts
hook and bash script.

* Added prepare-database.sh
* Remove swc & ts-node, since swc `useDefineForClassFields` will cause
leoric create error
[ref](fd438213ad/src/transpilers/swc.ts (L229)),
which can't customize
--------------
> 删除 ts-node 依赖,通过 npm scripts hook 和 bash 来进行 DB 初始化工作

* 新增 prepare-database.sh 处理 db 初始化工作
* 删除 ts-node 及 swc,swc 新版开启 `useDefineForClassFields` 会导致 leoric
创建对象失败,且无法自定义,
[ref](fd438213ad/src/transpilers/swc.ts (L229))一并去除
2023-03-16 17:47:37 +08:00
fengmk2
7ec53b1796 test: use @cnpmjs/npm-cli-login instead of npm-cli-login (#423)
skip snyk download

```
[09:07:45] [4/4] scripts.postinstall npm-cli-login@1.0.0 › snyk@^1.124.1 run "node wrapper_dist/bootstrap.js exec", root: "/root/workspace/npmmirror-registry_main/node_modules/_snyk@1.1117.0@snyk"
[09:07:45] Downloading from 'https://static.snyk.io/cli/v1.1117.0/snyk-linux' to '/root/workspace/npmmirror-registry_main/node_modules/_snyk@1.1117.0@snyk/wrapper_dist/snyk-linux'
```
2023-03-13 09:36:08 +08:00
semantic-release-bot
ae6b2f0d64 Release 3.8.0
[skip ci]

## [3.8.0](https://github.com/cnpm/npmcore/compare/v3.7.0...v3.8.0) (2023-03-08)

### Features

* Support for migrating packages into current registry ([#417](https://github.com/cnpm/npmcore/issues/417)) ([e5f905b](e5f905bd48))
2023-03-08 11:03:09 +00:00
elrrrrrrr
e5f905bd48 feat: Support for migrating packages into current registry (#417)
> Support for migrating packages into current registry
1. 🆕 Add `ensureSelfRegistry` method to initialize the current
configuration to the DB
2. 🧹 Add displayName to hide userPrefix info
3. 🧶 Uniformly determine publish access with `checkPublishAccess` and
`ensurePublishAccess`
--------------
> 支持将包迁移至当前 registry,避免不再进行包同步
1. 🆕  `ensureSelfRegistry` 方法,将当前配置初始化至 DB
2. 🧹 添加 displayName,外部不再展示 userPrefix 信息
3. 🧶 通过 checkPublishAccess 及 ensurePublishAccess 统一判断发布权限
2023-03-08 19:01:43 +08:00
semantic-release-bot
9fa6c961c4 Release 3.7.0
[skip ci]

## [3.7.0](https://github.com/cnpm/npmcore/compare/v3.6.0...v3.7.0) (2023-03-01)

### Features

* retry changes task when current work error ([#414](https://github.com/cnpm/npmcore/issues/414)) ([d7ae7aa](d7ae7aaaf2))
2023-03-01 07:39:42 +00:00
elrrrrrrr
d7ae7aaaf2 feat: retry changes task when current work error (#414)
> 当前请求 changesStream 失败时,需等待 15 分钟超时调度。
* 原 suspendTaskWhenExit 重构为 suspendSync ,支持传入 exit 参数,控制是否继续等待
* 请求 changesStream 失败时,主动挂起任务

------

> Wait 15 minutes for timeout scheduling if the current request
changesStream fails
* `suspendTaskWhenExit` is refactored to `suspendSync`, add exit
parameter to control whether to exiting the queue
* Suspend task when request changesStream fails
2023-03-01 15:38:35 +08:00
semantic-release-bot
74ab0eb908 Release 3.6.0
[skip ci]

## [3.6.0](https://github.com/cnpm/npmcore/compare/v3.5.0...v3.6.0) (2023-02-27)

### Features

* add integrate doc ([#413](https://github.com/cnpm/npmcore/issues/413)) ([a02f8b4](a02f8b45d3))
2023-02-27 01:14:08 +00:00
elrrrrrrr
a02f8b45d3 feat: add integrate doc (#413)
> 新增集成文档,完善 https://github.com/cnpm/cnpmcore/pull/411 SSORequest 文档说明
* 新增 INTEGRATE.md 文档
* README.md 增加对应链接

-----------

> New integration documentation for
https://github.com/cnpm/cnpmcore/pull/411
* Add INTEGRATE.md
* Add link in README.md
2023-02-27 09:12:58 +08:00
semantic-release-bot
ea3a8aa649 Release 3.5.0
[skip ci]

## [3.5.0](https://github.com/cnpm/npmcore/compare/v3.4.3...v3.5.0) (2023-02-21)

### Features

* support webauth infra ([#411](https://github.com/cnpm/npmcore/issues/411)) ([583437a](583437a83e))
2023-02-21 09:45:19 +00:00
elrrrrrrr
583437a83e feat: support webauth infra (#411)
> 基于 https://github.com/cnpm/cnpmcore/pull/380 ,新增 infra 层,允许自定义 authUrl
、新增 SSO 登录方法

* 从 `app/webauth` 移动至 `app/port`,取消独立 module
* 新增 SSORequest 方法,作为 SSO 内置方法
* 新增 authAdapter,因为 npm cli 请求地址是固定的
* 单测补全

------------

> New infra layer based on https://github.com/cnpm/cnpmcore/pull/380 ,
allowing custom the authUrl and SSO.

* Moved from `app/webauth` to `app/port`, normlize the controller
* New SSORequest method as SSO preset login
* New authAdapter, since npm cli request addresses are fixed
* TestCase updated

![image](https://user-images.githubusercontent.com/5574625/220271869-0b4d96c6-0d89-499e-9c74-eff2727749cb.png)

---------

Co-authored-by: fengmk2 <fengmk2@gmail.com>
2023-02-21 17:43:31 +08:00
fengmk2
4f1555a7f2 test: improve coverage (#410) 2023-02-16 11:20:16 +08:00
fengmk2
9b3352847c deps: use ioredis v5 typings (#409) 2023-02-15 22:17:50 +08:00
semantic-release-bot
5e95781a0c Release 3.4.3
[skip ci]

## [3.4.3](https://github.com/cnpm/npmcore/compare/v3.4.2...v3.4.3) (2023-02-15)

### Bug Fixes

* changesStream suspend ([#408](https://github.com/cnpm/npmcore/issues/408)) ([2c821ea](2c821eaa64))
2023-02-15 05:38:23 +00:00
elrrrrrrr
2c821eaa64 fix: changesStream suspend (#408)
> https://github.com/cnpm/cnpmcore/pull/367 tegg-v3 升级后,service 改为
Singleton,无法通过 ctx 上下文获取
* 修改 app 内 Service 获取方式
* 移除废弃的 typing.ts 声明

------------

> https://github.com/cnpm/cnpmcore/pull/367 Service has been refactored
to Singleton in tegg-v3 , which can't be inited with context anymore.
* Update Service init logic in appHook
* Remove the deprecated typing.ts


![image](https://user-images.githubusercontent.com/5574625/218919092-4a6b7353-7234-47f4-af99-9bf16846c2f1.png)
2023-02-15 13:37:12 +08:00
fengmk2
8964d7074d test: use ts-node with swc (#407)
https://typestrong.org/ts-node/docs/swc/
2023-02-14 22:37:14 +08:00
semantic-release-bot
18ed51e88e Release 3.4.2
[skip ci]

## [3.4.2](https://github.com/cnpm/npmcore/compare/v3.4.1...v3.4.2) (2023-02-14)

### Bug Fixes

* distinct processing task ([#406](https://github.com/cnpm/npmcore/issues/406)) ([c43c067](c43c067211))
2023-02-14 08:19:33 +00:00
elrrrrrrr
c43c067211 fix: distinct processing task (#406)
> 相同任务并发执行时,如果上游有 changesStream 事件,可能会导致版本被错误删除。
* 撤销 https://github.com/cnpm/cnpmcore/pull/352 的变更,我们已在
https://github.com/cnpm/cnpmcore/pull/361 中解决了事件实时性问题

------------

> Concurrent execution of the same task with changesStream events may
cause versions to be deleted incorrectly.
* revert https://github.com/cnpm/cnpmcore/pull/352, since we have fixed
in https://github.com/cnpm/cnpmcore/pull/361
2023-02-14 16:18:17 +08:00
semantic-release-bot
97ca612bf1 Release 3.4.1
[skip ci]

## [3.4.1](https://github.com/cnpm/npmcore/compare/v3.4.0...v3.4.1) (2023-02-13)

### Bug Fixes

* forbidden non-ascii binary subpath ([#405](https://github.com/cnpm/npmcore/issues/405)) ([7b52f6f](7b52f6f303))
2023-02-13 13:44:15 +00:00
fengmk2
7b52f6f303 fix: forbidden non-ascii binary subpath (#405)
closes https://github.com/cnpm/cnpmcore/issues/395
2023-02-13 21:43:07 +08:00
fengmk2
f7344eb90f test: use 127.0.0.1 instead of localhost to connect local db (#404) 2023-02-13 09:54:09 +08:00
fengmk2
1c24c49c0b test: use egg-bin v6 (#403)
https://github.com/eggjs/egg-bin/pull/217
2023-02-12 23:58:38 +08:00
fengmk2
6b1a92dbf6 test: add vscode debug config (#402) 2023-02-12 09:16:09 +08:00
semantic-release-bot
045615d25d Release 3.4.0
[skip ci]

## [3.4.0](https://github.com/cnpm/npmcore/compare/v3.3.2...v3.4.0) (2023-02-10)

### Features

* sync delete mode ([#398](https://github.com/cnpm/npmcore/issues/398)) ([27af0be](27af0beaad))
2023-02-10 13:32:47 +00:00
elrrrrrrr
27af0beaad feat: sync delete mode (#398)
> 为了避免部分 npm 包误封、误删,导致生产环境影响,新增 syncDeleteMode 配置,允许自定义同步策略

* 新增 `syncDeleteMode` : 'ignore' | 'block' | 'delete'
  * delete: 目前默认值,同步删包事件
  * ignore: 忽略 upstream 所有删包事件
  * block: 不做物理删除,只新增 block 记录,不允许访问,除非管理员手动恢复并更新 `syncPackageBlockList`
* `npm-security-holder` 场景也判断为删包事件
* 更新原有删包流程,统一处理,调整部分日志输出

---------------

> New `syncDeleteMode` to allow custom syncing policy to avoid some npm
packages being blocked or deleted by mistake.

* Add `syncDeleteMode` : 'ignore' | 'block' | 'delete'
  * delete: by default, sync delete events
  * ignore: ignore all upstream delete events
* block: only add block records, cant access unless the administrator
manually restores and update `syncPackageBlockList`.
* `npm-security-holder` event is also determined to be a delete event
* Update the original packet deletion process, update log output by the
way
2023-02-10 21:31:23 +08:00
semantic-release-bot
18cfb0d35a Release 3.3.2
[skip ci]

## [3.3.2](https://github.com/cnpm/npmcore/compare/v3.3.1...v3.3.2) (2023-02-10)

### Reverts

* Revert "fix: should sync package deps by default (#400)" (#401) ([b021e1e](b021e1ebc3)), closes [#400](https://github.com/cnpm/npmcore/issues/400) [#401](https://github.com/cnpm/npmcore/issues/401)
2023-02-10 01:07:56 +00:00
fengmk2
b021e1ebc3 Revert "fix: should sync package deps by default (#400)" (#401)
This reverts commit 282abf6920.
2023-02-10 09:06:43 +08:00
semantic-release-bot
2c679bec5c Release 3.3.1
[skip ci]

## [3.3.1](https://github.com/cnpm/npmcore/compare/v3.3.0...v3.3.1) (2023-02-10)

### Bug Fixes

* should sync package deps by default ([#400](https://github.com/cnpm/npmcore/issues/400)) ([282abf6](282abf6920))
2023-02-10 00:51:53 +00:00
fengmk2
282abf6920 fix: should sync package deps by default (#400) 2023-02-10 08:50:38 +08:00
semantic-release-bot
144f1b3a40 Release 3.3.0
[skip ci]

## [3.3.0](https://github.com/cnpm/npmcore/compare/v3.2.6...v3.3.0) (2023-02-09)

### Features

* auto sync package's optionalDependencies ([#399](https://github.com/cnpm/npmcore/issues/399)) ([07a19cf](07a19cfd1d))
2023-02-09 15:40:09 +00:00
fengmk2
07a19cfd1d feat: auto sync package's optionalDependencies (#399)
closes https://github.com/cnpm/cnpmcore/issues/397
2023-02-09 23:38:52 +08:00
fengmk2
db8995a2ab refactor: use Symbol instead of const on decorator attribute (#396) 2023-02-06 13:15:16 +08:00
fengmk2
cfc373c87a refactor: keep ignoreDownloadStatuses as number[] (#394) 2023-02-05 21:52:42 +08:00
semantic-release-bot
baa01835b3 Release 3.2.6
[skip ci]

## [3.2.6](https://github.com/cnpm/npmcore/compare/v3.2.5...v3.2.6) (2023-02-05)

### Bug Fixes

* should init binary adapter before reuse it ([#393](https://github.com/cnpm/npmcore/issues/393)) ([b9985ab](b9985ab166))
2023-02-05 03:35:31 +00:00
fengmk2
b9985ab166 fix: should init binary adapter before reuse it (#393) 2023-02-05 11:34:10 +08:00
semantic-release-bot
1c7feb7d11 Release 3.2.5
[skip ci]

## [3.2.5](https://github.com/cnpm/npmcore/compare/v3.2.4...v3.2.5) (2023-02-03)

### Bug Fixes

* allow publish 10mb tarball package by default ([#391](https://github.com/cnpm/npmcore/issues/391)) ([f873b8d](f873b8d3e4))
2023-02-03 14:18:19 +00:00
fengmk2
f873b8d3e4 fix: allow publish 10mb tarball package by default (#391)
closes https://github.com/cnpm/cnpmcore/issues/388
2023-02-03 22:17:07 +08:00
semantic-release-bot
09a66d1d07 Release 3.2.4
[skip ci]

## [3.2.4](https://github.com/cnpm/npmcore/compare/v3.2.3...v3.2.4) (2023-02-02)

### Bug Fixes

* skip download exists binary file ([#389](https://github.com/cnpm/npmcore/issues/389)) ([f4f40ed](f4f40edf43))
2023-02-02 07:55:11 +00:00
fengmk2
f4f40edf43 fix: skip download exists binary file (#389) 2023-02-02 15:54:07 +08:00
elrrrrrrr
84eff97870 refactor: Restrict binaryName types (#387)
> restrict binaryName type , the single source is the `config/binary.js`
file.

* export `BinaryName` & `CategoryName` type
* use `BinaryNameRule` typebox validator in controller
* `binaryName: string` => `binaryName: BinaryName`
2023-02-01 16:44:51 +08:00
semantic-release-bot
1bcc169e93 Release 3.2.3
[skip ci]

## [3.2.3](https://github.com/cnpm/npmcore/compare/v3.2.2...v3.2.3) (2023-01-30)

### Bug Fixes

* config path ([#385](https://github.com/cnpm/npmcore/issues/385)) ([ab72a3b](ab72a3bb8e))
2023-01-30 02:18:18 +00:00
elrrrrrrr
ab72a3bb8e fix: config path (#385)
Fix partial `config/binaries` file path in binary.
Prevent js parsing issues when cnpmcore is required as an npm module.
This is the part that was missed in the previous pr .
https://github.com/cnpm/cnpmcore/pull/384
2023-01-30 10:17:02 +08:00
semantic-release-bot
aff453ad8b Release 3.2.2
[skip ci]

## [3.2.2](https://github.com/cnpm/npmcore/compare/v3.2.1...v3.2.2) (2023-01-29)

### Bug Fixes

* import path ([#384](https://github.com/cnpm/npmcore/issues/384)) ([750ef60](750ef6092e))
2023-01-29 12:24:38 +00:00
elrrrrrrr
750ef6092e fix: import path (#384) 2023-01-29 20:23:33 +08:00
semantic-release-bot
17df8ecab5 Release 3.2.1
[skip ci]

## [3.2.1](https://github.com/cnpm/npmcore/compare/v3.2.0...v3.2.1) (2023-01-29)

### Bug Fixes

* api binary host config ([#383](https://github.com/cnpm/npmcore/issues/383)) ([8a2415f](8a2415f5a7))
2023-01-29 03:15:57 +00:00
elrrrrrrr
8a2415f5a7 fix: api binary host config (#383) 2023-01-29 11:14:39 +08:00
semantic-release-bot
4884e9f50a Release 3.2.0
[skip ci]

## [3.2.0](https://github.com/cnpm/npmcore/compare/v3.1.2...v3.2.0) (2023-01-28)

### Features

* update index json ([#379](https://github.com/cnpm/npmcore/issues/379)) ([bce6e79](bce6e7971f))
2023-01-28 07:00:51 +00:00
elrrrrrrr
bce6e7971f feat: update index json (#379)
> 多同步源方案之后,原有 srouce_registry 配置仅初始化时消费, 更新 / 状态信息相关字段

1. 使用 `information_schema` 替换 id 计算,解决部分 db id 自增不连续的问题
2. 添加 upstream_registries 列表,返回对应 changesStreamTaskData 以及 registry 信息
3. ~~source_registry~~ , ~~changes_stream_registry~~,
~~sync_changes_steam~~ 标记为 Legacy 字段,暂不移除
4. 新增 rawQueryUtil 处理 getCount 类型查询逻辑
2023-01-28 14:59:40 +08:00
semantic-release-bot
68edfb500d Release 3.1.2
[skip ci]

## [3.1.2](https://github.com/cnpm/npmcore/compare/v3.1.1...v3.1.2) (2023-01-28)

### Bug Fixes

* binary path ([#381](https://github.com/cnpm/npmcore/issues/381)) ([790621b](790621b4b9))
2023-01-28 03:27:38 +00:00
elrrrrrrr
790621b4b9 fix: binary path (#381)
> 目前逻辑会解析出 `
https://skia-canvas.s3.us-east-1.amazonaws.com/v0.9.24/linux-arm64-{node_napi_label}.tar.gz`
会导致任务多次失败重试

* 添加 if/else 判断兼容 `{platform}-{arch}-{node_napi_label}` 
* 临时兼容,后续通过变量替换实现
2023-01-28 11:25:57 +08:00
elrrrrrrr
dd4fe23419 refactor: re-org binary apdater (#378)
> 使用单例动态注入的方式重构 BinaryAdapter,实现类不再依赖上下文参数

1. Adapter 定义统一通过 `@BinaryAdapter(BinaryType.xx)` 定义,去除构造函数
2. 统一 `fetch(dir: string, bianryName?: string)` 接口定义,涉及 config 及
binaryTaskConfig 逻辑由实现类内部实现
3. 新增 `BinarySyncerService#getBinaryAdapter` 根据 binaryName 实例化对应
binaryAdapter
2023-01-21 14:30:06 +08:00
semantic-release-bot
56fa53c566 Release 3.1.1
[skip ci]

## [3.1.1](https://github.com/cnpm/npmcore/compare/v3.1.0...v3.1.1) (2023-01-18)

### Bug Fixes

* not exists binary should return 404 ([#377](https://github.com/cnpm/npmcore/issues/377)) ([0cc348d](0cc348dd6e))
2023-01-18 14:16:31 +00:00
fengmk2
0cc348dd6e fix: not exists binary should return 404 (#377)
closes https://github.com/cnpm/cnpmcore/issues/376
2023-01-18 22:15:22 +08:00
fengmk2
7952e33152 chore: update contributors
[skip ci]
2023-01-18 21:41:56 +08:00
semantic-release-bot
b0878e4107 Release 3.1.0
[skip ci]

## [3.1.0](https://github.com/cnpm/npmcore/compare/v3.0.1...v3.1.0) (2023-01-18)

### Features

* support auto sync when package not found ([#337](https://github.com/cnpm/npmcore/issues/337)) ([8734413](873441374f)), closes [#335](https://github.com/cnpm/npmcore/issues/335) [/github.com/cnpm/cnpmcore/pull/50/files#diff-97cbafa75ed0bae6a1f0a2df0676c00f56b9cf8944b04ddb82d6dd0ab141961](https://github.com/cnpm//github.com/cnpm/cnpmcore/pull/50/files/issues/diff-97cbafa75ed0bae6a1f0a2df0676c00f56b9cf8944b04ddb82d6dd0ab141961)
2023-01-18 01:59:35 +00:00
laoboxie
873441374f feat: support auto sync when package not found (#337)
1、修复 `syncMode = exist` 同步包定时任务不执行的问题
2、支持包不存在时自动同步和重定向到 sourceRegistry 的功能 close #335 ,通过配置 `syncNotFound = true` 开启

参考:

1、https://github.com/cnpm/cnpmcore/pull/50/files#diff-97cbafa75ed0bae6a1f0a2df0676c00f56b9cf8944b04ddb82d6dd0ab141961f
2、https://github.com/cnpm/cnpmjs.org/blob/master/middleware/sync_by_install.js

Co-authored-by: fengmk2 <fengmk2@gmail.com>
2023-01-18 09:58:24 +08:00
semantic-release-bot
23bc3b20f6 Release 3.0.1
[skip ci]

## [3.0.1](https://github.com/cnpm/npmcore/compare/v3.0.0...v3.0.1) (2023-01-18)

### Bug Fixes

* try to show latest version on sync log ([#375](https://github.com/cnpm/npmcore/issues/375)) ([1c64a57](1c64a57dbe))
2023-01-18 01:36:28 +00:00
fengmk2
1c64a57dbe fix: try to show latest version on sync log (#375) 2023-01-18 09:35:10 +08:00
semantic-release-bot
d6b35caa0e Release 3.0.0
[skip ci]

## [3.0.0](https://github.com/cnpm/npmcore/compare/v2.10.1...v3.0.0) (2023-01-17)

### ⚠ BREAKING CHANGES

* use SingletonProto instead of ContextProto

Co-authored-by: killagu <killa123@126.com>

### Code Refactoring

* use tegg v3 ([#370](https://github.com/cnpm/npmcore/issues/370)) ([8e3acae](8e3acaead9))
2023-01-17 15:38:50 +00:00
fengmk2
8e3acaead9 refactor: use tegg v3 (#370)
BREAKING CHANGE: use SingletonProto instead of ContextProto

Co-authored-by: killagu <killa123@126.com>
2023-01-17 23:37:38 +08:00
semantic-release-bot
fff032b1e8 Release 2.10.1
[skip ci]

## [2.10.1](https://github.com/cnpm/npmcore/compare/v2.10.0...v2.10.1) (2023-01-08)

### Bug Fixes

* export _cnpmcore_publish_time on abbreviated manifests ([#374](https://github.com/cnpm/npmcore/issues/374)) ([4bceac5](4bceac5a4c))
2023-01-08 10:41:00 +00:00
fengmk2
4bceac5a4c fix: export _cnpmcore_publish_time on abbreviated manifests (#374) 2023-01-08 18:39:36 +08:00
fengmk2
e09cdad6ec test: run tsc prod on ci (#373) 2023-01-05 22:27:11 +08:00
semantic-release-bot
6384229a53 Release 2.10.0
[skip ci]

## [2.10.0](https://github.com/cnpm/npmcore/compare/v2.9.1...v2.10.0) (2023-01-05)

### Features

* unpublish pkg when upstream block ([#372](https://github.com/cnpm/npmcore/issues/372)) ([7e419c1](7e419c1fb4))
2023-01-05 12:36:22 +00:00
elrrrrrrr
7e419c1fb4 feat: unpublish pkg when upstream block (#372)
> https://registry.npmmirror.com/chalk-next 

如果上游 cnpmcore registry block 包时,下游同步时目前会同步失败,导致包无法自动废弃

* 上游包 unblock 时,下游包应当 unpublish 处理,follow upstream 操作
2023-01-05 20:34:57 +08:00
fengmk2
bda3f1caf4 test: remove tsconfig-paths/register (#369)
https://github.com/eggjs/egg-bin/pull/199
2022-12-19 02:24:59 +08:00
fengmk2
e76885847c test: use redis image (#368) 2022-12-19 00:49:54 +08:00
semantic-release-bot
32d5084fdc Release 2.9.1
[skip ci]

## [2.9.1](https://github.com/cnpm/npmcore/compare/v2.9.0...v2.9.1) (2022-12-17)

### Bug Fixes

* Auto enable npm publish on github action ([3d366dd](3d366dd996))
* fix tsc:prod ([ca78d00](ca78d00f28))
2022-12-17 10:43:53 +00:00
fengmk2
f2055a355f chore: fix branches config 2022-12-17 18:08:57 +08:00
fengmk2
3d366dd996 fix: Auto enable npm publish on github action 2022-12-17 18:04:49 +08:00
fengmk2
6fcc5c6dab Update release.yml 2022-12-16 23:35:58 +08:00
fengmk2
b761a8f4eb Update release.yml 2022-12-16 23:32:38 +08:00
fengmk2
65a8d1d324 Create release.yml 2022-12-16 23:29:46 +08:00
fengmk2
57515de719 🐛 FIX: Use runInAnonymousContextScope instead (#367) 2022-12-16 21:58:55 +08:00
killagu
ca78d00f28 fix: fix tsc:prod 2022-12-15 22:32:11 +08:00
killagu
ea84da989f Release 2.9.0 2022-12-15 22:24:46 +08:00
elrrrrrrr
c562645db7 feat: suspend task before app close (#365)
> 机器计划内重启时,需要等待超时后再重新 retry 恢复 changesStream
同步,新增任务挂起机器,应用退出前,挂起当前机器正在执行的同步任务

* 新增 taskRepository#findTaskByAuthorIpAndType 方法,查找所有当前机器所有 worker 同步的任务
* 新增 module.d.ts 定义,目前仅消费 cnpmcoreCore module 内的 changesStream 方法
* app.ts 内调用 changesStreamService#suspendTaskWhenExit 在应用退出前触发

Co-authored-by: killa <killa123@126.com>
2022-12-15 21:34:24 +08:00
killagu
eb04533714 Release 2.8.1 2022-12-05 14:07:01 +08:00
fengmk2
7bc0fccaca 🤖 TEST: Fix async function mock 2022-12-03 15:03:19 +08:00
fengmk2
84ae9bcfa0 📖 DOC: Update contributors 2022-12-03 15:00:06 +08:00
laibao101
fad30adc56 feat: npm command support npm v6 (#356)
有很多比较久远的包,对 node 版本有限制。最高只能用 node14 npm6。 会导致 npm owner
操作的时候报错。原因是npm6的请求里面 header 没有 npm-command 参数。 增加兼容性。

Co-authored-by: Nice ZHOU 周华 <nice.zhou@nio.com>
2022-12-03 14:59:08 +08:00
elrrrrrrr
f961219dbe fix: Sync save ignore ER_DUP_ENTRY error (#364)
> 2个同步任务并发执行时,可能出现查询时未写入版本,写入时冲突,导致未正常更新 pkg.manifests

* 识别写入异常场景,由于 version 写入已添加 nfs 和 关联数据事务,可以直接放入 updatedVersions 数组进行
manifests 更新
* dist-tag 添加校验逻辑,对于 dist-tag 的更新,必须确保存量 manifests 或本次同步增量的 versions
包含对应版本,否则跳过
2022-12-03 14:49:29 +08:00
fengmk2
c02010f2e5 Release 2.8.0 2022-11-29 00:07:21 +08:00
elrrrrrrr
d55c680ef9 Event cork (#361)
> syncPackage 同步时,由于任务并发,可能会导致同步过程中 versions 表记录已经创建,pkg.manifests 还没有同步
> 针对这种场景做补偿逻辑,防止 tag 打在一个 pkg.manifests 没有的版本里

* 修改 pkg.manifests 补偿逻辑,兼容有 versions 没 pkg.manifests 的情况
* 添加 eventCork 的 advice,在 syncPackage 任务结束后,再统一触发 changes,依赖
[ref](https://github.com/eggjs/tegg/pull/60)

在同步和被同步的场景,确保 changes 发出时,pkg.manifests 已经更新
统一 ctx 内不同 changes 时序可能影响,不影响重新读取 manifests 一致性
2022-11-28 23:59:03 +08:00
fengmk2
c1eb0978ba Release 2.7.1 2022-11-25 21:35:53 +08:00
Ke Wu
c6b8aecfd0 fix: request binary error (#360)
Co-authored-by: 天玎 <tianding.wk@antgroup.com>
2022-11-25 21:34:53 +08:00
fengmk2
32e842e882 Release 2.7.0 2022-11-25 18:26:14 +08:00
Ke Wu
5738d569ea refactor: binary sync task use binaryName by default (#358)
1. 默认使用 config/binaries 的 binaryName 创建同步任务;
2. config/binaries 中的 category 为组合不同 binary 数据的配置。默认跟 binaryName 保持一致;
3. 当 category 跟 binaryName 不一致时,合并 binaryName 和 category 两个二进制数据信息。

以 canvas 和 node-canvas-prebuilt 为例:
1. 创建同步任务时,分别同步各自的数据;
2. 查询二进制数据时,由于 canvas 中的 category 配置为 node-canvas-prebuilt,这时候会合并 canvas
和 node-canvas-prebuilt 两个 binary(binaryName)的数据并返回。

这个重构删除 mergeCategory 字段,使得配置数据更加精简。

Co-authored-by: 天玎 <tianding.wk@antgroup.com>
2022-11-25 18:25:23 +08:00
fengmk2
9dd2d4bbe4 Release 2.6.1 2022-11-23 18:34:56 +08:00
fengmk2
0b35ead2a0 🐛 FIX: typo for canvas 2022-11-23 18:34:22 +08:00
fengmk2
a64ebd80f3 Release 2.6.0 2022-11-23 18:16:50 +08:00
Ke Wu
be8387dfa4 feat: Support canvas sync from different binary (#357)
Co-authored-by: 天玎 <tianding.wk@antgroup.com>
2022-11-23 18:15:51 +08:00
elrrrrrrr
d6c4cf5029 fix: duplicate binary task (#354)
> syncBinary 目前会通过定时任务单机每天创建,导致多实例冲突
> 其他任务类型均通过事件触发不受影响
* 创建 syncBinary 任务时,手动去重
* 添加 bizId 参数 进行兜底
2022-11-12 22:33:42 +08:00
fengmk2
0ada89b2fc Release 2.5.2 2022-11-11 18:32:40 +08:00
elrrrrrrr
7eb209de13 fix: create task when waiting (#352)
> 下游同步 cnpmcore 项目时,cnpmcore 会同时发送 [version, tag] 两个独立的事件
有可能下游在处理 version change 时,tag 尚未创建完成
但由于 task targetName 幂等,导致 tag 的 change 事件未响应,造成 tag 异常

* 创建同步任务时进行判断,如果任务还未开始才放弃创建
2022-11-11 18:31:53 +08:00
killagu
5965dbddbc Release 2.5.1 2022-11-07 10:09:07 +08:00
fengmk2
e40c5021bb 🐛 FIX: Mirror cypress arm64 binary (#351) 2022-11-06 18:46:17 +08:00
killagu
65a3df891d Release 2.5.0 2022-11-04 16:02:50 +08:00
elrrrrrrr
43d77ee91e feat: long description (#349)
目前 db 限制 pkg.description 长度为 10k,cnpmjs.org 为 longtext,可能导致不兼容。

* 长 description 场景,截断字符保存,防止创建 pkg 失败
2022-11-04 14:12:58 +08:00
fengmk2
3e7a434d19 Release 2.4.1 2022-10-28 00:12:00 +08:00
elrrrrrrr
28eeeafd98 fix: registry host config (#346)
closes #343
2022-10-28 00:03:10 +08:00
fengmk2
92350a8643 👌 IMPROVE: Show changes stream create task log (#347) 2022-10-27 22:11:37 +08:00
fengmk2
cd5bd923b8 🐛 FIX: Catch all error on changes stream handler (#344) 2022-10-25 23:26:40 +08:00
418 changed files with 91714 additions and 9907 deletions

36
.docker/alpine/Dockerfile Normal file
View File

@@ -0,0 +1,36 @@
FROM node:22-alpine
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
COPY . .
RUN .docker/build.sh
ENV NODE_ENV=production \
EGG_SERVER_ENV=prod \
CNPMCORE_CONFIG_REGISTRY= \
CNPMCORE_CONFIG_SOURCE_REGISTRY=https://registry.npmmirror.com \
CNPMCORE_CONFIG_SOURCE_REGISTRY_IS_CNPM=true \
CNPMCORE_DATABASE_TYPE= \
CNPMCORE_DATABASE_NAME= \
CNPMCORE_DATABASE_HOST= \
CNPMCORE_DATABASE_PORT=3306 \
CNPMCORE_DATABASE_USER= \
CNPMCORE_DATABASE_PASSWORD= \
CNPMCORE_REDIS_HOST= \
CNPMCORE_REDIS_PORT=6379 \
CNPMCORE_REDIS_PASSWORD= \
CNPMCORE_REDIS_DB= \
CNPMCORE_NFS_TYPE=s3 \
CNPMCORE_NFS_S3_CLIENT_ENDPOINT= \
CNPMCORE_NFS_S3_CLIENT_BUCKET= \
CNPMCORE_NFS_S3_CLIENT_ID= \
CNPMCORE_NFS_S3_CLIENT_SECRET= \
CNPMCORE_NFS_S3_CLIENT_FORCE_PATH_STYLE=true \
CNPMCORE_NFS_S3_CLIENT_DISABLE_URL=true \
TZ=Asia/Shanghai
EXPOSE 7001
CMD ["npm", "run", "start:foreground"]

7
.docker/build.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
node -v && npm -v \
&& npm install -g npminstall --registry=https://registry.npmmirror.com \
&& npminstall -c \
&& npm run tsc \
&& npmupdate -c --production

36
.docker/debian/Dockerfile Normal file
View File

@@ -0,0 +1,36 @@
FROM node:22-bookworm-slim
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
COPY . .
RUN .docker/build.sh
ENV NODE_ENV=production \
EGG_SERVER_ENV=prod \
CNPMCORE_CONFIG_REGISTRY= \
CNPMCORE_CONFIG_SOURCE_REGISTRY=https://registry.npmmirror.com \
CNPMCORE_CONFIG_SOURCE_REGISTRY_IS_CNPM=true \
CNPMCORE_DATABASE_TYPE= \
CNPMCORE_DATABASE_NAME= \
CNPMCORE_DATABASE_HOST= \
CNPMCORE_DATABASE_PORT=3306 \
CNPMCORE_DATABASE_USER= \
CNPMCORE_DATABASE_PASSWORD= \
CNPMCORE_REDIS_HOST= \
CNPMCORE_REDIS_PORT=6379 \
CNPMCORE_REDIS_PASSWORD= \
CNPMCORE_REDIS_DB= \
CNPMCORE_NFS_TYPE=s3 \
CNPMCORE_NFS_S3_CLIENT_ENDPOINT= \
CNPMCORE_NFS_S3_CLIENT_BUCKET= \
CNPMCORE_NFS_S3_CLIENT_ID= \
CNPMCORE_NFS_S3_CLIENT_SECRET= \
CNPMCORE_NFS_S3_CLIENT_FORCE_PATH_STYLE=true \
CNPMCORE_NFS_S3_CLIENT_DISABLE_URL=true \
TZ=Asia/Shanghai
EXPOSE 7001
CMD ["npm", "run", "start:foreground"]

6
.dockerignore Normal file
View File

@@ -0,0 +1,6 @@
logs
node_modules
run
typings
.cnpmcore*
coverage

50
.env.example Normal file
View File

@@ -0,0 +1,50 @@
# CNPMCORE_DATABASE_TYPE=MySQL
# CNPMCORE_DATABASE_USER=root
# CNPMCORE_DATABASE_PASSWORD=
# CNPMCORE_DATABASE_NAME=cnpmcore
# CNPMCORE_DATABASE_TYPE=PostgreSQL
# CNPMCORE_DATABASE_USER=postgres
# CNPMCORE_DATABASE_PASSWORD=postgres
# CNPMCORE_DATABASE_NAME=cnpmcore
# CNPMCORE_CONFIG_ENABLE_ES=true
# CNPMCORE_CONFIG_ES_CLIENT_NODE=http://localhost:9200
# CNPMCORE_CONFIG_ES_CLIENT_AUTH_USERNAME=elastic
# CNPMCORE_CONFIG_ES_CLIENT_AUTH_PASSWORD=abcdef
# https://github.com/cnpm/cnpmcore/blob/next/docs/elasticsearch-setup.md#%E6%96%B0%E5%BB%BA-env-%E6%96%87%E4%BB%B6
# Password for the 'elastic' user (at least 6 characters)
ELASTIC_PASSWORD="abcdef"
# Password for the 'kibana_system' user (at least 6 characters)
KIBANA_PASSWORD="abcdef"
# Version of Elastic products
STACK_VERSION=8.7.1
# enable for arm64
# STACK_VERSION_ARM64=-arm64
# STACK_PLATFORM=linux/arm64
# Set the cluster name
CLUSTER_NAME=docker-cluster
# Set to 'basic' or 'trial' to automatically start the 30-day trial
LICENSE=basic
#LICENSE=trial
# Port to expose Elasticsearch HTTP API to the host
ES_PORT=9200
#ES_PORT=127.0.0.1:9200
# Port to expose Kibana to the host
KIBANA_PORT=5601
#KIBANA_PORT=80
# Increase or decrease based on the available host memory (in bytes)
ES_MEM_LIMIT=1073741824
KB_MEM_LIMIT=1073741824
LS_MEM_LIMIT=1073741824
# SAMPLE Predefined Key only to be used in POC environments
ENCRYPTION_KEY=c34d38b3a14956121ff2170e5030b471551370178f43e5626eec58b04a30fae2

View File

@@ -1,7 +0,0 @@
app/proxy*
**/*.d.ts
node_modules/
dist/
coverage/
mocks/
.react_entries/

View File

@@ -1,6 +0,0 @@
{
"extends": "eslint-config-egg/typescript",
"rules": {
}
}

564
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,564 @@
# cnpmcore - Private NPM Registry for Enterprise
cnpmcore is a TypeScript-based private NPM registry implementation built with Egg.js framework. It provides enterprise-grade package management with support for MySQL/PostgreSQL databases, Redis caching, and optional Elasticsearch.
**ALWAYS reference these instructions first** and fallback to search or bash commands only when you encounter unexpected information that does not match the information here.
## Code Style and Conventions
### Linting and Formatting
- **Linter**: Oxlint (fast Rust-based linter)
- **Formatter**: Prettier with specific configuration
- **Pre-commit hooks**: Husky + lint-staged automatically format and lint on commit
**Code Style Rules:**
```javascript
// From .prettierrc
{
"singleQuote": true, // Use single quotes
"trailingComma": "es5", // ES5 trailing commas
"tabWidth": 2, // 2-space indentation
"printWidth": 120, // 120 character line width
"arrowParens": "avoid" // Avoid parens when possible
}
// From .oxlintrc.json
{
"max-params": 6, // Maximum 6 function parameters
"no-console": "warn", // Warn on console usage
"import/no-anonymous-default-export": "error"
}
```
**Linting Commands:**
```bash
npm run lint # Check for linting errors
npm run lint:fix # Auto-fix linting issues
npm run typecheck # TypeScript type checking without build
```
### TypeScript Conventions
- Use strict TypeScript with comprehensive type definitions
- Avoid `any` types - use proper typing or `unknown`
- Export types and interfaces for reusability
- Use ES modules (`import/export`) syntax throughout
### Testing Conventions
- Test files use `.test.ts` suffix
- Use `@eggjs/mock` for mocking and testing
- Tests organized to mirror source structure in `test/` directory
- Use `assert` from `node:assert/strict` for assertions
- Mock external dependencies using `mock()` from `@eggjs/mock`
**Test Naming Pattern:**
```typescript
describe('test/path/to/SourceFile.test.ts', () => {
describe('[HTTP_METHOD /api/path] functionName()', () => {
it('should handle expected behavior', async () => {
// Test implementation
});
});
});
```
## Domain-Driven Design (DDD) Architecture
cnpmcore follows **Domain-Driven Design** principles with clear separation of concerns:
### Layer Architecture (Dependency Flow)
```
Controller (HTTP Interface Layer)
↓ depends on
Service (Business Logic Layer)
↓ depends on
Repository (Data Access Layer)
↓ depends on
Model (ORM/Database Layer)
Entity (Domain Models - no dependencies, pure business logic)
Common (Utilities and Adapters - available to all layers)
```
### Layer Responsibilities
**Controller Layer** (`app/port/controller/`):
- HTTP request/response handling
- Request validation using `@eggjs/typebox-validate`
- User authentication and authorization
- **NO business logic** - delegate to Services
- Inheritance: `YourController extends AbstractController extends MiddlewareController`
**Service Layer** (`app/core/service/`):
- Core business logic implementation
- Orchestration of multiple repositories and entities
- Transaction management
- Event publishing
- NO HTTP concerns, NO direct database access
**Repository Layer** (`app/repository/`):
- Data access and persistence
- CRUD operations on Models
- Query building and optimization
- NO business logic
**Entity Layer** (`app/core/entity/`):
- Domain models with business behavior
- Pure business logic (no infrastructure dependencies)
- Immutable data structures where possible
- Rich domain objects (not anemic models)
**Model Layer** (`app/repository/model/`):
- ORM definitions using Leoric
- Database schema mapping
- Table and column definitions
- NO business logic
### Repository Method Naming Convention
**ALWAYS follow these naming patterns:**
- `findSomething` - Query a single model/entity
- `saveSomething` - Save (create or update) a model
- `removeSomething` - Delete a model
- `listSomethings` - Query multiple models (use plural)
### Request Validation Trilogy
**ALWAYS validate requests in this exact order:**
1. **Request Parameter Validation** - First line of defense
```typescript
// Use @eggjs/typebox-validate for type-safe validation
// See app/port/typebox.ts for examples
```
2. **User Authentication & Token Permissions**
```typescript
// Token roles: 'read' | 'publish' | 'setting'
const authorizedUser = await this.userRoleManager.requiredAuthorizedUser(ctx, 'publish');
```
3. **Resource Authorization** - Prevent horizontal privilege escalation
```typescript
// Example: Ensure user is package maintainer
await this.userRoleManager.requiredPackageMaintainer(pkg, authorizedUser);
// Or use convenience method
const { pkg } = await this.ensurePublishAccess(ctx, fullname);
```
### Modifying Database Models
When changing a Model, update **all 3 locations**:
1. SQL migration files: `sql/mysql/*.sql` AND `sql/postgresql/*.sql`
2. ORM Model: `app/repository/model/*.ts`
3. Domain Entity: `app/core/entity/*.ts`
**NEVER auto-generate SQL migrations** - manual review is required for safety.
## Prerequisites and Environment Setup
- **Node.js**: Version 20.18.0 or higher (required by engines field in package.json)
- **Database**: MySQL 5.7+ or PostgreSQL 17+
- **Cache**: Redis 6+
- **Optional**: Elasticsearch 8.x for enhanced search capabilities
## Working Effectively
### Bootstrap and Build
```bash
# Install dependencies (takes ~2 minutes)
npm install
# Copy environment configuration
cp .env.example .env
# Lint code (very fast, <1 second)
npm run lint
# Fix linting issues
npm run lint:fix
# Build TypeScript (takes ~6 seconds)
npm run tsc
# Production build (takes ~6 seconds)
npm run tsc:prod
```
### Database Setup - MySQL (Recommended for Development)
```bash
# Start MySQL + Redis services via Docker (takes ~1 minute to pull images initially)
docker compose -f docker-compose.yml up -d
# Verify services are running
docker compose ps
# Initialize database (takes <2 seconds)
CNPMCORE_DATABASE_NAME=cnpmcore bash ./prepare-database-mysql.sh
# For tests, create test database
mysql -h 127.0.0.1 -P 3306 -u root -e "CREATE DATABASE cnpmcore_unittest;"
```
### Database Setup - PostgreSQL (Alternative)
```bash
# Start PostgreSQL + Redis services via Docker
docker compose -f docker-compose-postgres.yml up -d
# Initialize database (takes <1 second)
CNPMCORE_DATABASE_NAME=cnpmcore bash ./prepare-database-postgresql.sh
```
### Development Server
```bash
# MySQL development server (starts in ~20 seconds)
npm run dev
# Server runs on http://127.0.0.1:7001
# PostgreSQL development server
npm run dev:postgresql
# Server runs on http://127.0.0.1:7001
```
### Testing
```bash
# Run full test suite with MySQL - NEVER CANCEL: Takes 4+ minutes. Set timeout to 10+ minutes.
npm run test
# Run full test suite with PostgreSQL - NEVER CANCEL: Takes 4+ minutes. Set timeout to 10+ minutes.
npm run test:postgresql
# Run single test file (for faster iteration, takes ~12 seconds)
npm run test:local test/common/CryptoUtil.test.ts
# Test coverage with MySQL - NEVER CANCEL: Takes 5+ minutes. Set timeout to 15+ minutes.
npm run cov
# Test coverage with PostgreSQL - NEVER CANCEL: Takes 5+ minutes. Set timeout to 15+ minutes.
npm run cov:postgresql
```
**CRITICAL TESTING NOTES:**
- **NEVER CANCEL** build or test commands - they may take 4-15 minutes to complete
- Individual test files run much faster (~12 seconds) for development iteration
- Full test suite processes 100+ test files and requires database initialization
- Test failures may occur in CI environment; use individual test files for validation
**Testing Philosophy:**
- **Write tests for all new features** - No feature is complete without tests
- **Test at the right layer** - Controller tests for HTTP, Service tests for business logic
- **Mock external dependencies** - Use `mock()` from `@eggjs/mock`
- **Use realistic test data** - Create through `TestUtil` helper methods
- **Clean up after tests** - Database is reset between test files
- **Test both success and failure cases** - Error paths are equally important
**Common Test Patterns:**
```typescript
import { app, mock } from '@eggjs/mock/bootstrap';
import { TestUtil } from '../../../test/TestUtil';
describe('test/path/to/YourController.test.ts', () => {
describe('[GET /api/endpoint] methodName()', () => {
it('should return expected result', async () => {
// Setup
const { authorization } = await TestUtil.createUser();
// Execute
const res = await app
.httpRequest()
.get('/api/endpoint')
.set('authorization', authorization)
.expect(200);
// Assert
assert.equal(res.body.someField, expectedValue);
});
it('should handle unauthorized access', async () => {
const res = await app
.httpRequest()
.get('/api/endpoint')
.expect(401);
assert.equal(res.body.error, '[UNAUTHORIZED] Login first');
});
});
});
```
### Production Commands
```bash
# CI pipeline commands - NEVER CANCEL: Takes 5+ minutes. Set timeout to 15+ minutes.
npm run ci # MySQL CI (includes lint, test, coverage, build)
npm run ci:postgresql # PostgreSQL CI
# Production start/stop
npm run start # Start as daemon
npm run stop # Stop daemon
npm run start:foreground # Start in foreground for debugging
```
## Validation Scenarios
**ALWAYS manually validate changes** by running through these scenarios:
### Basic API Validation
```bash
# Start development server
npm run dev
# Test registry root endpoint
curl http://127.0.0.1:7001
# Should return JSON with app metadata and stats
# Test authentication endpoint
curl http://127.0.0.1:7001/-/whoami
# Should return authentication error (expected when not logged in)
# Test package listing (initially empty)
curl http://127.0.0.1:7001/-/all
```
### Admin User Setup and Package Publishing
```bash
# Register admin user (cnpmcore_admin) - requires allowPublicRegistration=true in config
npm login --registry=http://127.0.0.1:7001
# Verify login
npm whoami --registry=http://127.0.0.1:7001
# Test package publishing
npm publish --registry=http://127.0.0.1:7001
```
## Architecture and Navigation
### Project Structure
```
app/
├── common/ # Global utilities and adapters
│ ├── adapter/ # External service adapters (NpmRegistry, Binary, etc.)
│ └── enum/ # Shared enumerations
├── core/ # Business logic layer
│ ├── entity/ # Core domain models
│ ├── event/ # Event handlers and async processing
│ ├── service/ # Core business services
│ └── util/ # Internal utilities
├── port/ # Interface layer
│ ├── controller/ # HTTP controllers
│ ├── middleware/ # Express middleware
│ ├── schedule/ # Background job schedulers
│ └── webauth/ # WebAuth integration
├── repository/ # Data access layer
│ ├── model/ # ORM models
│ └── util/ # Repository utilities
└── infra/ # Infrastructure adapters
```
### Key Services and Controllers
- **PackageController**: Main package CRUD operations
- **PackageManagerService**: Core package management business logic
- **BinarySyncerService**: Binary package synchronization
- **ChangesStreamService**: NPM registry change stream processing
- **UserController**: User authentication and profile management
### Infrastructure Adapters (`app/infra/`)
Enterprise customization layer for PaaS integration. cnpmcore provides default implementations, but enterprises should implement their own based on their infrastructure:
- **NFSClientAdapter**: File storage abstraction (local/S3/OSS)
- **QueueAdapter**: Message queue integration
- **AuthAdapter**: Authentication system integration
- **BinaryAdapter**: Binary package storage adapter
These adapters allow cnpmcore to integrate with different cloud providers and enterprise systems without modifying core business logic.
### Configuration Files
- `config/config.default.ts`: Main application configuration
- `config/database.ts`: Database connection settings
- `config/binaries.ts`: Binary package mirror configurations
- `.env`: Environment-specific variables
- `tsconfig.json`: TypeScript compilation settings
- `tsconfig.prod.json`: Production build settings
## Common Development Tasks
### Adding New Features
**ALWAYS follow this workflow:**
1. **Plan the change** - Identify which layers need modification
2. **Run linter** - `npm run lint:fix` to establish clean baseline
3. **Bottom-up implementation** - Build from data layer up to controller:
a. **Model Layer** (if new data structure needed):
- Add SQL migrations: `sql/mysql/*.sql` AND `sql/postgresql/*.sql`
- Create Model: `app/repository/model/YourModel.ts`
- Run database migration scripts
b. **Entity Layer** (domain models):
- Create Entity: `app/core/entity/YourEntity.ts`
- Implement business logic and behavior
- Keep entities pure (no infrastructure dependencies)
c. **Repository Layer** (data access):
- Create Repository: `app/repository/YourRepository.ts`
- Follow naming: `findX`, `saveX`, `removeX`, `listXs`
- Inject dependencies using `@Inject()`
d. **Service Layer** (business logic):
- Create Service: `app/core/service/YourService.ts`
- Orchestrate repositories and entities
- Use `@SingletonProto()` for service lifecycle
e. **Controller Layer** (HTTP endpoints):
- Create Controller: `app/port/controller/YourController.ts`
- Extend `AbstractController`
- Add HTTP method decorators: `@HTTPMethod()`, `@HTTPBody()`, etc.
- Implement 3-step validation (params → auth → authorization)
4. **Add tests** - Create test file: `test/path/matching/source/YourFile.test.ts`
5. **Lint and test** - `npm run lint:fix && npm run test:local test/your/test.test.ts`
6. **Type check** - `npm run typecheck`
7. **Commit** - Use semantic commit messages (feat/fix/chore/docs/test)
**Example Controller Implementation:**
```typescript
import { AbstractController } from './AbstractController';
import { HTTPController, HTTPMethod, HTTPQuery, Inject } from 'egg';
@HTTPController()
export class YourController extends AbstractController {
@Inject()
private readonly yourService: YourService;
@HTTPMethod({ path: '/api/path', method: 'GET' })
async yourMethod(@HTTPQuery() params: YourQueryType) {
// 1. Validate params (done by @HTTPQuery with typebox)
// 2. Authenticate user
const user = await this.userRoleManager.requiredAuthorizedUser(this.ctx, 'read');
// 3. Authorize resource access (if needed)
// 4. Delegate to service
return await this.yourService.doSomething(params);
}
}
```
### Database Migrations
- SQL files are in `sql/mysql/` and `sql/postgresql/`
- Migration scripts automatically run during database preparation
- **NEVER** modify existing migration files - only add new ones
### Background Jobs
- Schedulers are in `app/port/schedule/`
- Include sync workers, cleanup tasks, and stream processors
- Jobs run automatically when development server starts
## Troubleshooting
### Database Connection Issues
```bash
# Check if services are running
docker compose ps
# Reset MySQL environment
docker compose -f docker-compose.yml down
docker compose -f docker-compose.yml up -d
CNPMCORE_DATABASE_NAME=cnpmcore bash ./prepare-database-mysql.sh
# Reset PostgreSQL environment
docker compose -f docker-compose-postgres.yml down
docker compose -f docker-compose-postgres.yml up -d
CNPMCORE_DATABASE_NAME=cnpmcore bash ./prepare-database-postgresql.sh
```
### Build Issues
```bash
# Clean and rebuild
npm run clean
npm run tsc
# Check TypeScript configuration
npx tsc --noEmit
```
### Test Issues
```bash
# Create missing test database
mysql -h 127.0.0.1 -P 3306 -u root -e "CREATE DATABASE cnpmcore_unittest;"
# Run single test for debugging
npm run test:local test/common/CryptoUtil.test.ts
```
## CI/CD Integration
The project uses GitHub Actions with workflows in `.github/workflows/`:
- `nodejs.yml`: Main CI pipeline with MySQL, PostgreSQL, and Elasticsearch testing
- Multiple Node.js versions tested: 20, 22, 24
- **CRITICAL**: CI jobs include long-running tests that can take 15+ minutes per database type
### Pre-commit Validation
**ALWAYS run before committing:**
```bash
npm run lint:fix # Fix linting issues
npm run tsc # Verify TypeScript compilation
npm run test:local test/path/to/relevant.test.ts # Run relevant tests
```
## Docker Support
### Development Environments
- `docker-compose.yml`: MySQL + Redis + phpMyAdmin
- `docker-compose-postgres.yml`: PostgreSQL + Redis + pgAdmin
- `docker-compose-es.yml`: Elasticsearch integration
### Production Images
```bash
# Build Alpine image
npm run images:alpine
# Build Debian image
npm run images:debian
```
## External Dependencies
- **Database**: MySQL 9.x or PostgreSQL 17+
- **Cache**: Redis 6+
- **Search**: Elasticsearch 8.x (optional)
- **Storage**: Local filesystem or S3-compatible storage
- **Framework**: Egg.js with extensive TypeScript integration
## Performance Notes
Command execution times (for timeout planning):
- **Startup Time**: ~20 seconds for development server
- **Build Time**: ~6 seconds for TypeScript compilation
- **Test Time**: 4-15 minutes for full suite (database dependent)
- **Individual Test**: ~12 seconds for single test file
- **Package Installation**: ~2 minutes for npm install
- **Database Init**: <2 seconds for either MySQL or PostgreSQL
- **Linting**: <1 second (oxlint is very fast)
Always account for these timings when setting timeouts for automated processes.
## Semantic Commit Messages
Use conventional commit format for all commits:
- `feat:` - New features
- `fix:` - Bug fixes
- `docs:` - Documentation changes
- `chore:` - Maintenance tasks
- `test:` - Test additions or modifications
- `refactor:` - Code refactoring
- `perf:` - Performance improvements
Examples:
```bash
feat: add support for GitHub binary mirroring
fix: resolve authentication token expiration issue
docs: update API documentation for sync endpoints
test: add tests for package publication workflow
```

View File

@@ -1,70 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '41 13 * * 3'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript', 'typescript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -9,7 +9,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/first-interaction@v1
- uses: actions/first-interaction@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
issue-message: '我们已经看到你的反馈,如果是功能缺陷,可以提供一下重现该问题的方式;如果是新功能需求,我们会尽快加入讨论。同时我们非常期待你可以加入我们的贡献者行列,让项目可以长期可持续发展。'

View File

@@ -5,18 +5,278 @@ name: Node.js CI
on:
push:
branches:
- main
- master
branches: [master]
pull_request:
branches:
- main
- master
schedule:
- cron: '0 2 * * *'
branches: [master]
merge_group:
jobs:
typecheck:
runs-on: ubuntu-latest
concurrency:
group: typecheck-${{ github.workflow }}-#${{ github.event.pull_request.number || github.head_ref || github.ref }}
cancel-in-progress: true
steps:
- name: Checkout Git Source
uses: actions/checkout@v5
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: 22
- name: Install Dependencies
run: npm i
- name: Lint
run: npm run lint
- name: Typecheck
run: npm run typecheck
- name: Build
run: npm run tsc && npm run tsc:prod
test-deployment:
runs-on: ubuntu-latest
concurrency:
group: test-deployment-${{ github.workflow }}-#${{ github.event.pull_request.number || github.head_ref || github.ref }}
cancel-in-progress: true
services:
mysql:
image: mysql:5.7
env:
MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: cnpmcore
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
redis:
# https://docs.github.com/en/actions/using-containerized-services/about-service-containers#example-mapping-redis-ports
image: redis
ports:
# Opens tcp port 6379 on the host and service container
- 6379:6379
steps:
- name: Checkout Git Source
uses: actions/checkout@v5
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: 22
- name: Install Dependencies
run: npm i
- name: Test Deployment
run: |
npm run build
echo "Preparing database..."
CNPMCORE_DATABASE_NAME=cnpmcore bash ./prepare-database-mysql.sh
echo "Starting cnpmcore..."
CNPMCORE_FORCE_LOCAL_FS=true npm run start:foreground &
sleep 5
echo "Checking cnpmcore is ready..."
set -Eeuo pipefail
URL="http://127.0.0.1:7001"
PATTERN="instance_start_time"
TIMEOUT=60
TMP="$(mktemp)"
echo "🔎 Health check $URL, expect 200 & body contains: $PATTERN"
deadline=$((SECONDS + TIMEOUT))
last_status=""
while (( SECONDS < deadline )); do
last_status="$(curl -sS -o "$TMP" -w '%{http_code}' "$URL" || true)"
echo "last_status=$last_status"
echo "body=$(cat $TMP)"
if [[ "$last_status" == "200" ]] && grep -q "$PATTERN" "$TMP"; then
echo "✅ OK"
rm -f "$TMP"
npx eggctl stop
exit 0
fi
sleep 1
done
echo "::error::❌ Health check failed: status=$last_status"
echo "---- Response body (last try) ----"
cat "$TMP" || true
rm -f "$TMP"
exit 1
test-postgresql-fs-nfs:
strategy:
fail-fast: false
matrix:
node-version: [22, 24]
os: [ubuntu-latest]
# 0-based index
shardIndex: [0, 1, 2]
shardTotal: [3]
name: test on postgresql (node@${{ matrix.node-version }}, shard@${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
concurrency:
group: test-postgresql-fs-nfs-${{ github.workflow }}-#${{ github.event.pull_request.number || github.head_ref || github.ref }}-${{ matrix.node-version }}-${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
cancel-in-progress: true
runs-on: ${{ matrix.os }}
services:
# https://docs.github.com/en/actions/use-cases-and-examples/using-containerized-services/creating-postgresql-service-containers
# Label used to access the service container
postgres:
# Docker Hub image
image: postgres
# Provide the password for postgres
env:
POSTGRES_PASSWORD: postgres
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
redis:
# https://docs.github.com/en/actions/using-containerized-services/about-service-containers#example-mapping-redis-ports
image: redis
ports:
# Opens tcp port 6379 on the host and service container
- 6379:6379
steps:
- name: Checkout Git Source
uses: actions/checkout@v5
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
- name: Install Dependencies
run: npm i
# https://github.com/elastic/elastic-github-actions/blob/master/elasticsearch/README.md
- name: Configure sysctl limits
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- name: Runs Elasticsearch
uses: elastic/elastic-github-actions/elasticsearch@master
with:
stack-version: 8.18.0
security-enabled: false
- name: Wait for Elasticsearch to be ready
run: |
curl -v http://localhost:9200
while ! curl -s http://localhost:9200 | grep -q "elasticsearch"; do
echo "Waiting for Elasticsearch to be ready..."
sleep 1
done
- name: Continuous Integration
run: npm run ci:postgresql
env:
# The hostname used to communicate with the PostgreSQL service container
POSTGRES_HOST: localhost
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
# The default PostgreSQL port
POSTGRES_PORT: 5432
CNPMCORE_CONFIG_ENABLE_ES: true
CNPMCORE_CONFIG_ES_CLIENT_NODES: http://localhost:9200
# https://github.com/jamiebuilds/ci-parallel-vars
CI_NODE_INDEX: ${{ matrix.shardIndex }}
CI_NODE_TOTAL: ${{ matrix.shardTotal }}
- name: Code Coverage
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
test-mysql57-fs-nfs:
strategy:
fail-fast: false
matrix:
node-version: [22, 24]
os: [ubuntu-latest]
# 0-based index
shardIndex: [0, 1, 2]
shardTotal: [3]
name: test on mysql (node@${{ matrix.node-version }}, shard@${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
concurrency:
group: test-mysql57-fs-nfs-${{ github.workflow }}-#${{ github.event.pull_request.number || github.head_ref || github.ref }}-${{ matrix.node-version }}-${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
cancel-in-progress: true
runs-on: ${{ matrix.os }}
services:
mysql:
image: mysql:5.7
env:
MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: cnpmcore_unittest
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
redis:
# https://docs.github.com/en/actions/using-containerized-services/about-service-containers#example-mapping-redis-ports
image: redis
ports:
# Opens tcp port 6379 on the host and service container
- 6379:6379
steps:
- name: Checkout Git Source
uses: actions/checkout@v5
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
- name: Install Dependencies
run: npm i
- name: Continuous Integration
run: npm run ci
env:
# https://github.com/jamiebuilds/ci-parallel-vars
CI_NODE_INDEX: ${{ matrix.shardIndex }}
CI_NODE_TOTAL: ${{ matrix.shardTotal }}
- name: Code Coverage
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
test-mysql57-s3-nfs:
if: ${{ github.ref_name == 'master' }}
strategy:
fail-fast: false
matrix:
node-version: [22, 24]
os: [ubuntu-latest]
concurrency:
group: test-mysql57-s3-nfs-${{ github.workflow }}-#${{ github.event.pull_request.number || github.head_ref || github.ref }}-${{ matrix.node-version }}
cancel-in-progress: true
runs-on: ${{ matrix.os }}
services:
@@ -29,91 +289,46 @@ jobs:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
strategy:
fail-fast: false
matrix:
node-version: [16, 18]
os: [ubuntu-latest]
steps:
- name: Checkout Git Source
uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
# https://github.com/marketplace/actions/redis-server-in-github-actions#usage
- name: Start Redis
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
- name: Install Dependencies
run: npm i
- name: Continuous Integration
run: npm run ci
- name: Code Coverage
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
test-mysql57-oss-nfs:
runs-on: ${{ matrix.os }}
if:
contains('
refs/heads/main
refs/heads/master
refs/heads/dev
', github.ref)
services:
mysql:
image: mysql:5.7
env:
MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: cnpmcore_unittest
redis:
image: redis
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
strategy:
fail-fast: false
matrix:
node-version: [16, 18]
os: [ubuntu-latest]
- 6379:6379
steps:
- name: Checkout Git Source
uses: actions/checkout@v2
- name: Checkout Git Source
uses: actions/checkout@v5
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
# https://github.com/marketplace/actions/redis-server-in-github-actions#usage
- name: Start Redis
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
- name: Install Dependencies
run: npm i
- name: Install Dependencies
run: npm i
- name: Continuous Integration
run: npm run ci "test/cli/npm/install.test.ts"
env:
CNPMCORE_NFS_TYPE: s3
CNPMCORE_NFS_REMOVE_BEFORE_UPLOAD: true
CNPMCORE_NFS_S3_CLIENT_BUCKET: cnpmcore-unittest-github-nodejs-${{ matrix.node-version }}
CNPMCORE_NFS_S3_CLIENT_ENDPOINT: ${{ secrets.CNPMCORE_NFS_S3_ENDPOINT }}
CNPMCORE_NFS_S3_CLIENT_ID: ${{ secrets.CNPMCORE_NFS_S3_ID }}
CNPMCORE_NFS_S3_CLIENT_SECRET: ${{ secrets.CNPMCORE_NFS_S3_SECRET }}
CNPMCORE_NFS_S3_CLIENT_FORCE_PATH_STYLE: true
# CNPMCORE_NFS_S3_CLIENT_DISABLE_URL: true
- name: Continuous Integration
run: npm run ci
env:
CNPMCORE_NFS_TYPE: oss
CNPMCORE_NFS_OSS_BUCKET: cnpmcore-unittest-github-nodejs-${{ matrix.node-version }}
CNPMCORE_NFS_OSS_ENDPOINT: https://oss-us-west-1.aliyuncs.com
CNPMCORE_NFS_OSS_ID: ${{ secrets.CNPMCORE_NFS_OSS_ID }}
CNPMCORE_NFS_OSS_SECRET: ${{ secrets.CNPMCORE_NFS_OSS_SECRET }}
- name: Code Coverage
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
- name: Code Coverage
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
done:
runs-on: ubuntu-latest
needs:
- test-postgresql-fs-nfs
- test-mysql57-fs-nfs
- typecheck
steps:
- run: exit 1
if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}

75
.github/workflows/release-image.yml vendored Normal file
View File

@@ -0,0 +1,75 @@
# https://docs.github.com/en/actions/tutorials/publish-packages/publish-docker-images#publishing-images-to-github-packages
name: Create and publish a Docker image
# Configures this workflow to run manually
on:
workflow_dispatch:
pull_request:
branches: [master]
# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds.
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
jobs:
build-and-push-image:
runs-on: ubuntu-latest
concurrency:
group: build-and-push-image-${{ github.workflow }}-#${{ github.event.pull_request.number || github.head_ref || github.ref }}
cancel-in-progress: true
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v5
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
- name: Log in to the Container registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see [Usage](https://github.com/docker/build-push-action#usage) in the README of the `docker/build-push-action` repository.
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
- name: Build and push Docker image
id: push
uses: docker/build-push-action@v6
with:
context: .
file: .docker/debian/Dockerfile
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# This step generates an artifact attestation for the image, which is a tamper-proof statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see [Using artifact attestations to establish provenance for builds](/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds).
- name: Generate artifact attestation
if: github.event_name != 'pull_request'
uses: actions/attest-build-provenance@v3
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true

18
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Release
on:
push:
branches: [master]
permissions:
contents: write
deployments: write
issues: write
pull-requests: write
id-token: write
jobs:
release:
name: NPM
uses: cnpm/github-actions/.github/workflows/npm-release.yml@master
secrets:
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}

View File

@@ -1,18 +0,0 @@
# https://github.com/marketplace/actions/sql-review
name: SQL Review
on: [pull_request]
jobs:
sql-review:
runs-on: ubuntu-latest
name: SQL Review
steps:
- uses: actions/checkout@v3
- name: Check SQL
uses: bytebase/sql-review-action@main
with:
database-type: MYSQL
file-pattern: ^sql/.*\.sql$
override-file-path: ./sql-review-override.yml

5
.gitignore vendored
View File

@@ -13,6 +13,7 @@ config/config.prod.ts
config/**/*.js
app/**/*.js
test/**/*.js
app.js
.cnpmcore
.cnpmcore_unittest
@@ -116,4 +117,8 @@ dist
.tern-port
.idea
.DS_Store
run
!test/ctx_register.js
.egg/

1
.husky/pre-commit Normal file
View File

@@ -0,0 +1 @@
npx lint-staged

24
.oxlintrc.json Normal file
View File

@@ -0,0 +1,24 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
// FIXME: @eggjs/oxlint-config too strict, disable it for now, will fix it later
// "extends": ["./node_modules/@eggjs/oxlint-config/.oxlintrc.json"],
"env": {
"node": true,
"mocha": true
},
"rules": {
// Project-specific overrides
"max-params": ["error", 6],
"no-console": "warn",
"import/no-anonymous-default-export": "error",
"no-unassigned-import": "allow",
"new-cap": "allow",
"class-methods-use-this": "allow",
"import/no-named-export": "allow",
"unicorn/no-array-sort": "allow",
"no-param-reassign": "allow",
"unicorn/prefer-at": "allow",
"no-process-env": "allow"
},
"ignorePatterns": ["index.d.ts"]
}

4
.prettierignore Normal file
View File

@@ -0,0 +1,4 @@
CHANGELOG.md
__snapshots__
pnpm-lock.yaml
node_modules

7
.prettierrc Normal file
View File

@@ -0,0 +1,7 @@
{
"singleQuote": true,
"trailingComma": "es5",
"tabWidth": 2,
"printWidth": 120,
"arrowParens": "avoid"
}

26
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,26 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Egg Debug",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev", "--", "--inspect-brk"],
"console": "integratedTerminal",
"restart": true,
"autoAttachChildProcesses": true
},
{
"type": "node",
"request": "launch",
"name": "Egg Test",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "test:local", "--", "--inspect-brk"],
"autoAttachChildProcesses": true
}
]
}

1707
CHANGELOG.md Normal file

File diff suppressed because it is too large Load Diff

270
CLAUDE.md Normal file
View File

@@ -0,0 +1,270 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
cnpmcore is a TypeScript-based private NPM registry implementation for enterprise use. It's built on the Egg.js framework using Domain-Driven Design (DDD) architecture principles and supports both MySQL and PostgreSQL databases.
## Essential Commands
### Development
```bash
# Start development server (MySQL)
npm run dev
# Start development server (PostgreSQL)
npm run dev:postgresql
# Lint code
npm run lint
# Fix linting issues
npm run lint:fix
# TypeScript type checking
npm run typecheck
```
### Testing
```bash
# Run all tests with MySQL (takes 4+ minutes)
npm run test
# Run all tests with PostgreSQL (takes 4+ minutes)
npm run test:postgresql
# Run single test file (faster iteration, ~12 seconds)
npm run test:local test/path/to/file.test.ts
# Generate coverage report
npm run cov
```
### Database Setup
```bash
# MySQL setup
docker compose -f docker-compose.yml up -d
CNPMCORE_DATABASE_NAME=cnpmcore bash ./prepare-database-mysql.sh
# PostgreSQL setup
docker compose -f docker-compose-postgres.yml up -d
CNPMCORE_DATABASE_NAME=cnpmcore bash ./prepare-database-postgresql.sh
```
### Build
```bash
# Clean build artifacts
npm run clean
# Development build
npm run tsc
# Production build
npm run tsc:prod
```
## Architecture - Domain-Driven Design (DDD)
The codebase follows strict DDD layering with clear separation of concerns:
```
Controller (app/port/controller/) ← HTTP interface, validation, auth
↓ depends on
Service (app/core/service/) ← Business logic orchestration
↓ depends on
Repository (app/repository/) ← Data access layer
↓ depends on
Model (app/repository/model/) ← ORM/Database mapping
Entity (app/core/entity/) ← Pure domain models (no dependencies)
Common (app/common/) ← Utilities and adapters (all layers)
```
### Layer Responsibilities
**Controller Layer** (`app/port/controller/`):
- Handle HTTP requests/responses
- Validate inputs using `@eggjs/typebox-validate`
- Authenticate users and verify authorization
- Delegate business logic to Services
- All controllers extend `AbstractController`
**Service Layer** (`app/core/service/`):
- Implement core business logic
- Orchestrate multiple repositories
- Publish domain events
- Manage transactions
**Repository Layer** (`app/repository/`):
- CRUD operations on Models
- Data access and persistence
- Query building and optimization
- Methods named: `findX`, `saveX`, `removeX`, `listXs`
**Entity Layer** (`app/core/entity/`):
- Pure domain models with business behavior
- No infrastructure dependencies
- Immutable data structures preferred
**Model Layer** (`app/repository/model/`):
- ORM definitions using Leoric
- Database schema mapping
- No business logic
### Infrastructure Adapters (`app/infra/`)
Enterprise customization layer for PaaS integration:
- **NFSClientAdapter**: File storage (local/S3/OSS)
- **QueueAdapter**: Message queue integration
- **AuthAdapter**: Authentication system
- **BinaryAdapter**: Binary package storage
## Key Development Patterns
### Request Validation Trilogy
Always validate requests in this exact order:
1. **Parameter Validation** - Use `@eggjs/typebox-validate` for type-safe validation
2. **Authentication** - Get authorized user with token role verification
3. **Authorization** - Check resource-level permissions to prevent privilege escalation
```typescript
// Example controller method
async someMethod(@HTTPQuery() params: QueryType) {
// 1. Params already validated by @HTTPQuery with typebox
// 2. Authenticate
const user = await this.userRoleManager.requiredAuthorizedUser(this.ctx, 'publish');
// 3. Authorize (if needed)
const { pkg } = await this.ensurePublishAccess(this.ctx, fullname);
// 4. Execute business logic
return await this.service.doSomething(params);
}
```
### Repository Method Naming
- `findSomething` - Query single entity
- `saveSomething` - Create or update entity
- `removeSomething` - Delete entity
- `listSomethings` - Query multiple entities (plural)
### Modifying Database Models
When changing a Model, update all 3 locations:
1. SQL migrations: `sql/mysql/*.sql` AND `sql/postgresql/*.sql`
2. ORM Model: `app/repository/model/*.ts`
3. Domain Entity: `app/core/entity/*.ts`
## Code Style
### Linting
- **Linter**: Oxlint (Rust-based, very fast)
- **Formatter**: Prettier
- **Pre-commit**: Husky + lint-staged (auto-format on commit)
Style rules:
- Single quotes (`'`)
- 2-space indentation
- 120 character line width
- ES5 trailing commas
- Max 6 function parameters
- No console statements (use logger)
### TypeScript
- Strict TypeScript enabled
- Avoid `any` types - use proper typing or `unknown`
- ES modules (`import/export`) throughout
- Comprehensive type definitions in all files
### Testing
- Test files use `.test.ts` suffix
- Tests mirror source structure in `test/` directory
- Use `@eggjs/mock` for mocking
- Use `assert` from `node:assert/strict`
- Test both success and error cases
Pattern:
```typescript
describe('test/path/to/SourceFile.test.ts', () => {
describe('[HTTP_METHOD /api/path] functionName()', () => {
it('should handle expected behavior', async () => {
// Test implementation
});
});
});
```
## Project Structure
```
app/
├── common/ # Global utilities and adapters
│ ├── adapter/ # External service adapters
│ └── enum/ # Shared enumerations
├── core/ # Business logic layer
│ ├── entity/ # Domain models
│ ├── event/ # Event handlers
│ ├── service/ # Business services
│ └── util/ # Internal utilities
├── port/ # Interface layer
│ ├── controller/ # HTTP controllers
│ ├── middleware/ # Middleware
│ └── schedule/ # Background jobs
├── repository/ # Data access layer
│ └── model/ # ORM models
└── infra/ # Infrastructure adapters
config/ # Configuration files
sql/ # Database migrations
├── mysql/ # MySQL migrations
└── postgresql/ # PostgreSQL migrations
test/ # Test files (mirrors app/ structure)
```
## Important Configuration
- `config/config.default.ts` - Main application configuration
- `config/database.ts` - Database connection settings
- `config/binaries.ts` - Binary package mirror configurations
- `.env` - Environment-specific variables (copy from `.env.example`)
- `tsconfig.json` - TypeScript settings (target: ES2021 for Leoric compatibility)
## Development Workflow
1. **Setup**: Copy `.env.example` to `.env`, start Docker services, initialize database
2. **Feature Development**: Follow bottom-up approach (Model → Entity → Repository → Service → Controller)
3. **Testing**: Write tests at appropriate layer, run individual tests for fast iteration
4. **Validation**: Run linter, typecheck, relevant tests before committing
5. **Commit**: Use semantic commit messages (feat/fix/docs/test/chore)
## Integration as NPM Package
cnpmcore can be integrated into Egg.js/Tegg applications as an NPM package, allowing enterprises to:
- Customize infrastructure adapters (storage, auth, queue)
- Override default behavior while receiving updates
- Integrate with existing enterprise systems
See INTEGRATE.md for detailed integration guide.
## Performance Notes
Typical command execution times:
- Development server startup: ~20 seconds
- TypeScript build: ~6 seconds
- Full test suite: 4-15 minutes
- Single test file: ~12 seconds
- Linting: <1 second
- Database initialization: <2 seconds
## Prerequisites
- Node.js: 20.18.0+ or 22.18.0+
- Database: MySQL 5.7+ or PostgreSQL 17+
- Cache: Redis 6+
- Optional: Elasticsearch 8.x
## Key Services & Controllers
Core components to understand:
- **PackageController**: Package CRUD operations
- **PackageManagerService**: Core package management logic
- **BinarySyncerService**: Binary package synchronization
- **ChangesStreamService**: NPM registry change stream processing
- **UserController**: User authentication and profiles

View File

@@ -2,64 +2,109 @@
## 环境初始化
本项目的外部服务依赖有MySQL 数据服务、Redis 缓存服务。
本项目的外部服务依赖有MySQL 数据库或 PostgreSQL 数据库、Redis 缓存服务。
生成本地开发环境配置文件:
```bash
cp .env.example .env
```
可以通过 Docker 来快速启动本地开发环境:
MySQL 开发环境:
```bash
# 启动本地依赖服务
$ docker-compose up -d
# 启动本地依赖服务 - MySQL + Redis
docker-compose -f docker-compose.yml up -d
# 关闭本地依赖服务
$ docker-compose down
docker-compose -f docker-compose.yml down
```
> 手动初始化依赖服务参见[文档](./docs/setup.md)
PostgreSQL 开发环境:
```bash
# 启动本地依赖服务 - PostgreSQL + Redis
docker-compose -f docker-compose-postgres.yml up -d
# 关闭本地依赖服务
docker-compose -f docker-compose-postgres.yml down
```
> 手动初始化依赖服务参见[本地开发环境 - MySQL](./docs/setup.md) 或 [本地开发环境 - PostgreSQL](./docs/setup-with-postgresql.md)
## 本地开发
### 安装依赖
```bash
$ npm install
npm install
```
### 开发运行
### 开发运行 - MySQL
```bash
# 初始化数据库
$ MYSQL_DATABASE=cnpmcore npm run prepare-database
CNPMCORE_DATABASE_NAME=cnpmcore bash ./prepare-database-mysql.sh
# 启动 Web 服务
$ npm run dev
npm run dev
# 访问
curl -v http://127.0.0.1:7001
```
### 单元测试
### 开发运行 - PostgreSQL
```bash
$ npm run test
# 初始化数据库
CNPMCORE_DATABASE_NAME=cnpmcore bash ./prepare-database-postgresql.sh
# 启动 Web 服务
npm run dev:postgresql
# 访问
curl -v http://127.0.0.1:7001
```
编写单测规范:
### 登录和测试发包
- assert 断言库必须使用 require 引入
> cnpmcore 默认不开放注册,可以通过 `config.default.ts` 中的 `allowPublicRegistration` 配置开启,否则只有管理员可以登录
```ts
import assert = require('assert');
注册 cnpmcore_admin 管理员
```bash
npm login --registry=http://127.0.0.1:7001
# 验证登录
npm whoami --registry=http://127.0.0.1:7001
```
> CAUTION: don't use `import assert from 'assert'`
> Just use old style import assert = require('assert') for assert module. This is limitation.
> See https://github.com/power-assert-js/espower-typescript#caution-dont-use-import-assert-from-assert
发包
```bash
npm publish --registry=http://127.0.0.1:7001
```
### 单元测试
MySQL
```bash
npm run test
```
PostgreSQL
```bash
npm run test:postgresql
```
## 项目结构
```
```txt
app
├── common
│ └── adapter
@@ -72,6 +117,8 @@ app
│ └── controller
├── repository
│ └── model
├── infra
│ └── NFSClientAdapter.ts
└── test
├── control
│ └── response_time.test.js
@@ -80,31 +127,69 @@ app
```
common
- util全局工具类
- adapter外部服务调用
core
- entity核心模型实现业务行为
- event异步事件定义以及消费串联业务
- service核心业务
- util服务 core 内部,不对外暴露
repository
- modelORM 模型,数据定义
- XXXRepository: 仓储接口,存储、查询过程
port
- controllerHTTP controller
infra
基于 PaaS 基础设置实现各种 adapter 真实适配实现cnpmcore 会内置一种实现,企业自定义的 cnpmcore 应该自行基于自身的
PaaS 环境实现自己的 infra module。
- NFSClientAdapter.ts
- QueueAdapter.ts
- AuthAdapter.ts
## 架构分层依赖图
```txt
+--------------------------------+ +--------+ +----------+
| Controller | | | | |
+----^-------------^-------------+ | | | |
| | | | | |
| inject | inject | | | |
| | | | | |
| +----------+-------------+ | | | |
| | Service | | Entity | | |
| +-----------^------------+ | | | |
| | | | | Common |
| | inject | | | |
| | | | | |
+----+--------------+------------+ | | | |
| Repository | | | | |
+-------------------^------------+ +---^----| | |
| | | |
| inject ORM | | |
| | | |
+-----------+------------+ | | |
| Model +<-----+ | |
+------------------------+ +----------+
```
## Controller 开发指南
目前只支持 HTTP 协议的 Controller代码在 `app/port/controller` 目录下。
基于类继承的模式来实现,类关系大致如下:
```
```txt
+----------------------+ +----------------------+ +---------------+
| PackageController.ts | | PackageTagController | | XxxController |
| PackageController | | PackageTagController | | XxxController |
+---------------+------+ +---+------------------+ +--+------------+
| | |
| extends | extends | extends
@@ -130,15 +215,15 @@ port
例如会封装 PackageEntity、PackageVersionEntity 等查询方法。
```ts
// try to get package entity, throw NotFoundError when package not exists
private async getPackageEntity(scope: string, name: string) {
const packageEntity = await this.packageRepository.findPackage(scope, name);
if (!packageEntity) {
const fullname = getFullname(scope, name);
throw new NotFoundError(`${fullname} not found`);
}
return packageEntity;
// try to get package entity, throw NotFoundError when package not exists
private async getPackageEntity(scope: string, name: string) {
const packageEntity = await this.packageRepository.findPackage(scope, name);
if (!packageEntity) {
const fullname = getFullname(scope, name);
throw new NotFoundError(`${fullname} not found`);
}
return packageEntity;
}
```
### 请求合法性校验三部曲
@@ -151,7 +236,7 @@ port
#### 1、请求参数校验
使用 [egg-typebox-validate](https://github.com/xiekw2010/egg-typebox-validate) 来做请求参数校验,只需要定义一次参数类型和规则,就能同时拥有参数校验和类型定义。
使用 [@eggjs/typebox-validate](https://github.com/eggjs/egg/tree/next/plugins/typebox-validate) 来做请求参数校验,只需要定义一次参数类型和规则,就能同时拥有参数校验和类型定义。
详细使用方式可以参考 [PR#12](https://github.com/cnpm/cnpmcore/pull/12)。
使用方式请直接参考 `app/port/typebox.ts` 代码。
@@ -192,13 +277,39 @@ await this.userRoleManager.requiredPackageMaintainer(pkg, authorizedUser);
当然,大部分对包进行写操作的请求下,我们在 AbstractController 里面抽取了一个更加简便的方法,一次性将数据获取和权限校验包含在一起:
```ts
const pkg = await this.getPackageEntityAndRequiredMaintainer(ctx, fullname);
const { pkg } = await this.ensurePublishAccess(ctx, fullname);
```
## Service 开发指南
Service 依赖 Repository然后被 Controller 依赖
```txt
+---------------------------+ +----------------------+ +-------------+
| PackageVersionFileService | | PackageSyncerService | | XxxService |
+---------------^-----------+ +---^------------------+ +--^----------+
| | |
| inject | inject | inject
| | |
+---+-------------------+-------------------------+--+
| PackageManagerService |
+-----------------------^----------------------------+
|
| inject
|
+---------+--------+
| XxxRepository |
+------------------+
```
### PackageManagerService 管理所有包以及版本信息
它会被其他 Service 依赖
## Repository 开发指南
Repository 依赖 Model然后被 Service 和 Controller 依赖
### Repository 类方法命名规则
- `findSomething` 查询一个模型数据
@@ -216,9 +327,9 @@ const pkg = await this.getPackageEntityAndRequiredMaintainer(ctx, fullname);
可能需要涉及3个地方的修改
1. sql/*.sql
2. repository/model/*.ts
3. core/entity/*.ts
1. `sql/mysql/*.sql`, `sql/postgresql/*.sql`
2. `repository/model/*.ts`
3. `core/entity/*.ts`
目前还不会做 Model 到 SQL 的自动转换生成,核心原因有:

View File

@@ -1,237 +0,0 @@
2.4.0 / 2022-10-25
==================
**features**
* [[`6aa302d`](http://github.com/cnpm/cnpmcore/commit/6aa302d074f2c84f39e2065fa20853b007f6fa3b)] - 📦 NEW: Use oss-cnpm v4 (#340) (fengmk2 <<fengmk2@gmail.com>>)
* [[`a217fd0`](http://github.com/cnpm/cnpmcore/commit/a217fd07ccad3fe5058881654a13e0c69c758717)] - 👌 IMPROVE: Reduce warning log (#326) (fengmk2 <<fengmk2@gmail.com>>)
**fixes**
* [[`b19b0a0`](http://github.com/cnpm/cnpmcore/commit/b19b0a0496e35ac1c6b3de746b9221990ba9dc93)] - fix: Lazy set registryId when executeTask (#341) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
**others**
* [[`305175a`](http://github.com/cnpm/cnpmcore/commit/305175ab5fcdc3ad3b60055d45cfcacb23065a80)] - 🤖 TEST: Use enum define on unittest (#333) (fengmk2 <<fengmk2@gmail.com>>)
* [[`07f2eba`](http://github.com/cnpm/cnpmcore/commit/07f2eba137ba625b2d422677a465920617141b87)] - 🤖 TEST: Mock all binary http requests (#328) (fengmk2 <<fengmk2@gmail.com>>)
* [[`4b0c7dc`](http://github.com/cnpm/cnpmcore/commit/4b0c7dc6196960d34b2529bfde724e97f1af8444)] - 🤖 TEST: Mock all httpclient request (#327) (fengmk2 <<fengmk2@gmail.com>>)
2.3.1 / 2022-10-06
==================
**features**
* [[`bbc08fd`](http://github.com/cnpm/cnpmcore/commit/bbc08fd26887d55b98b70d1ed210caf81f9d5c22)] - 👌 IMPROVE: syncPackageWorkerMaxConcurrentTasks up to 20 (#322) (fengmk2 <<fengmk2@gmail.com>>)
* [[`5852f22`](http://github.com/cnpm/cnpmcore/commit/5852f22023525d857ff1ceea205e4315c8079877)] - feat: support sync exist mode (#275) (zhangyuantao <<zhangyuantao@163.com>>)
**fixes**
* [[`d79634e`](http://github.com/cnpm/cnpmcore/commit/d79634eea749fef1a420988a8599f156f28ee85a)] - 🐛 FIX: Should sync package when registry id is null (#324) (fengmk2 <<fengmk2@gmail.com>>)
* [[`24f920d`](http://github.com/cnpm/cnpmcore/commit/24f920d65b31f9eb83c1ecda36adf7f9e2c379c3)] - 🐛 FIX: Should run sync package on all worker (#323) (fengmk2 <<fengmk2@gmail.com>>)
2.3.0 / 2022-09-24
==================
**others**
* [[`bd83a19`](http://github.com/cnpm/cnpmcore/commit/bd83a19eca761c96bcee04e6ae91e68eac3cb6bf)] - 👌 IMPROVE: use urllib3 instead (#302) (fengmk2 <<fengmk2@gmail.com>>)
* [[`35e7d3a`](http://github.com/cnpm/cnpmcore/commit/35e7d3ad3c78712b507d522a0b72b5a6a5a4ec1c)] - 👌 IMPROVE: Enable phpmyadmin and DEBUG_LOCAL_SQL by default (#320) (fengmk2 <<fengmk2@gmail.com>>)
2.2.0 / 2022-09-22
==================
**features**
* [[`bca0fb3`](http://github.com/cnpm/cnpmcore/commit/bca0fb3c37b9f74f3c41ab181dd3113d9dab4c05)] - feat: only allow pkg sync from registry it belong (#317) (killa <<killa123@126.com>>)
**fixes**
* [[`7e9beea`](http://github.com/cnpm/cnpmcore/commit/7e9beead576a41de3aa042b92b788bde5d55f44a)] - fix: only append / if path is not empty and not ends with / (#316) (killa <<killa123@126.com>>)
* [[`4fe68cb`](http://github.com/cnpm/cnpmcore/commit/4fe68cbf38f303e797b80b88407f714ec76bfae0)] - fix: fix directory path (#313) (killa <<killa123@126.com>>)
**others**
* [[`e72ce35`](http://github.com/cnpm/cnpmcore/commit/e72ce3576f9a3cda095e3feac59eeb1d8c1e8033)] - 🤖 TEST: Skip unstable tests (#318) (fengmk2 <<fengmk2@gmail.com>>)
* [[`171b11f`](http://github.com/cnpm/cnpmcore/commit/171b11f7bba534c993af4088b00f8545216734a9)] - Revert "fix: fix directory path (#313)" (fengmk2 <<fengmk2@gmail.com>>)
2.1.1 / 2022-09-08
==================
**fixes**
* [[`8fb9dd8`](http://github.com/cnpm/cnpmcore/commit/8fb9dd8cf4800afe3f54aba9ee4c0ae05efb4f1d)] - fix: findExecuteTask only return waiting task (#312) (killa <<killa123@126.com>>)
2.1.0 / 2022-09-05
==================
**features**
* [[`c5d2b49`](http://github.com/cnpm/cnpmcore/commit/c5d2b49ab3a0ce0d67f6e7cc19e0be867c92d04c)] - feat: auto get next valid task (#311) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
2.0.0 / 2022-09-05
==================
**others**
* [[`fc4baff`](http://github.com/cnpm/cnpmcore/commit/fc4baff226540e7cfee9adc069e17a59f4050a43)] - chore: refactor schedule with @Schedule (#309) (killa <<killa123@126.com>>)
1.11.6 / 2022-09-04
==================
**fixes**
* [[`768f951`](http://github.com/cnpm/cnpmcore/commit/768f951b6f2509f14c30a70d86a6719107d963a4)] - fix: cnpmjsorg changesstream limit (#310) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
1.11.5 / 2022-09-02
==================
**fixes**
* [[`f673ab8`](http://github.com/cnpm/cnpmcore/commit/f673ab8ba1545909ff6b8e445364646511930891)] - fix: execute state check (#308) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
**others**
* [[`091420a`](http://github.com/cnpm/cnpmcore/commit/091420ae2677ecedd1a26a238921321c2a191675)] - 🤖 TEST: Add SQL Review Action (#307) (fengmk2 <<fengmk2@gmail.com>>)
1.11.4 / 2022-08-30
==================
**fixes**
* [[`f9210ca`](http://github.com/cnpm/cnpmcore/commit/f9210ca7e180e19bce08da9ef33e46e990b86ef1)] - fix: changes stream empty (#306) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
1.11.3 / 2022-08-29
==================
**fixes**
* [[`48f228d`](http://github.com/cnpm/cnpmcore/commit/48f228da447d8cde62849fa52cf43bae7754e2e3)] - fix: changes stream updatedAt (#304) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
* [[`87045ba`](http://github.com/cnpm/cnpmcore/commit/87045ba8b0e14547c93689600eb7e2c1de2a611b)] - fix: task updatedAt save (#305) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
1.11.2 / 2022-08-28
==================
**fixes**
* [[`4e8700c`](http://github.com/cnpm/cnpmcore/commit/4e8700c4f7c6fb5c4f4d4a2b9a9546096c5d10e2)] - fix: only create createHookTask if hook enable (#299) (killa <<killa123@126.com>>)
**others**
* [[`e06c841`](http://github.com/cnpm/cnpmcore/commit/e06c841537113fdb0c00beb22b0a55378c61ce80)] - 🐛 FIX: Should sync public package when registryName not exists (#303) (fengmk2 <<fengmk2@gmail.com>>)
* [[`f139444`](http://github.com/cnpm/cnpmcore/commit/f139444213403494ebe9bf073df62125413892d9)] - 📖 DOC: Update contributors (fengmk2 <<fengmk2@gmail.com>>)
* [[`c4a9de5`](http://github.com/cnpm/cnpmcore/commit/c4a9de598dce9a1b82bbcdd91968a15bbc5a4b6b)] - Create SECURITY.md (fengmk2 <<fengmk2@gmail.com>>)
* [[`709d65b`](http://github.com/cnpm/cnpmcore/commit/709d65bd0473856c9bfc4416ea2ca375136e354f)] - 🤖 TEST: Use diff bucket on OSS test (#301) (fengmk2 <<fengmk2@gmail.com>>)
* [[`9576699`](http://github.com/cnpm/cnpmcore/commit/95766990fa9c4c2c43d462f6b151557425b0c741)] - chore: use AsyncGenerator insteadof Transform stream (#300) (killa <<killa123@126.com>>)
* [[`3ed5269`](http://github.com/cnpm/cnpmcore/commit/3ed5269f1d22ca3aaca89a90a4fff90f293e2464)] - 📦 NEW: Mirror better-sqlite3 binary (#296) (fengmk2 <<fengmk2@gmail.com>>)
1.11.1 / 2022-08-24
==================
**fixes**
* [[`359a150`](http://github.com/cnpm/cnpmcore/commit/359a150eb450d69e6523b20efcc5c7cfe3efab4d)] - fix: changes stream (#297) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
1.11.0 / 2022-08-23
==================
**features**
* [[`a91c8ac`](http://github.com/cnpm/cnpmcore/commit/a91c8ac4d05dc903780fda516b09364a05a2b1e6)] - feat: sync package from spec regsitry (#293) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
* [[`de37008`](http://github.com/cnpm/cnpmcore/commit/de37008261b05845f392d66764cdfe14ae324756)] - feat: changesStream adapter & needSync() method (#292) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
* [[`4b506c8`](http://github.com/cnpm/cnpmcore/commit/4b506c8371697ddacdbe99a8ecb330bfc1911ec6)] - feat: init registry & scope (#286) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
* [[`41c6e24`](http://github.com/cnpm/cnpmcore/commit/41c6e24c84d546eb9d5515cc0940cc3e4274687b)] - feat: impl trigger Hooks (#289) (killa <<killa123@126.com>>)
* [[`79cb826`](http://github.com/cnpm/cnpmcore/commit/79cb82615f04bdb3da3ccbe09bb6a861608b69c5)] - feat: impl migration sql (#290) (killa <<killa123@126.com>>)
* [[`4cfa8ed`](http://github.com/cnpm/cnpmcore/commit/4cfa8ed9d687ce7d950d7d20c0ea28221763ba5f)] - feat: impl hooks api (#287) (killa <<killa123@126.com>>)
* [[`47d53d2`](http://github.com/cnpm/cnpmcore/commit/47d53d22ad03c02ee9cb9035a38ae205a6d38381)] - feat: add bizId for task (#285) (killa <<killa123@126.com>>)
* [[`3b1536b`](http://github.com/cnpm/cnpmcore/commit/3b1536b070b2f9062bc2cc377db96d2f4a160efc)] - feat: add node-webrtc mirror (#274) (Opportunity <<opportunity@live.in>>)
**others**
* [[`7106807`](http://github.com/cnpm/cnpmcore/commit/710680742a078b2faf4cb18c3a39c0397308712e)] - 🐛 FIX: Should show queue size on logging (#280) (fengmk2 <<fengmk2@gmail.com>>)
* [[`3a41b21`](http://github.com/cnpm/cnpmcore/commit/3a41b2161cc99bb2f6f6dd7cbaa7abef25ff4393)] - 🐛 FIX: Handle binary configuration value (#278) (fengmk2 <<fengmk2@gmail.com>>)
1.10.0 / 2022-08-04
==================
**features**
* [[`c2b7d5a`](http://github.com/cnpm/cnpmcore/commit/c2b7d5aa98b5ba8649ec246c616574a22e9a74b8)] - feat: use sort set to impl queue (#277) (killa <<killa123@126.com>>)
1.9.1 / 2022-07-29
==================
**fixes**
* [[`c54aa21`](http://github.com/cnpm/cnpmcore/commit/c54aa2165c3938dcbb5a2b3b54e66a0d961cc813)] - fix: check executingCount after task is done (#276) (killa <<killa123@126.com>>)
**others**
* [[`3268d03`](http://github.com/cnpm/cnpmcore/commit/3268d030b620825c8c2e6331e1745c1788066c61)] - 🤖 TEST: show package not use cache if isSync (#273) (fengmk2 <<fengmk2@gmail.com>>)
1.9.0 / 2022-07-25
==================
**features**
* [[`af6a75a`](http://github.com/cnpm/cnpmcore/commit/af6a75af32ea04c90fda82be3a56c99ec77e5807)] - feat: add forceSyncHistory options (#271) (killa <<killa123@126.com>>)
1.8.0 / 2022-07-21
==================
**features**
* [[`b49a38c`](http://github.com/cnpm/cnpmcore/commit/b49a38c77e044c978e6de32a9d3e257cc90ea7c1)] - feat: use Model with inject (#269) (killa <<killa123@126.com>>)
1.7.1 / 2022-07-20
==================
**fixes**
* [[`52fca55`](http://github.com/cnpm/cnpmcore/commit/52fca55aa883865f0ae70bfc1ff274c313b8f76a)] - fix: show package not use cache if isSync (#268) (killa <<killa123@126.com>>)
1.7.0 / 2022-07-12
==================
**others**
* [[`4f7ce8b`](http://github.com/cnpm/cnpmcore/commit/4f7ce8b4b2a5806a225ce67228388e14388b7059)] - deps: upgrade leoric to 2.x (#262) (killa <<killa123@126.com>>)
1.6.0 / 2022-07-11
==================
**features**
* [[`1b9a9c7`](http://github.com/cnpm/cnpmcore/commit/1b9a9c70f66d8393e3b132f18713461a9243db73)] - feat: mirror nydus binaries (#261) (killa <<killa123@126.com>>)
**others**
* [[`c1256bf`](http://github.com/cnpm/cnpmcore/commit/c1256bf3807bcc9a5c8be2ec5bf5ca8a5eef112e)] - 🐛 FIX: Ignore 403 status on s3 download fail (#260) (fengmk2 <<fengmk2@gmail.com>>)
* [[`d685772`](http://github.com/cnpm/cnpmcore/commit/d6857724307fb0df0c4c118491784b30d19a9a15)] - 🐛 FIX: skia-canvas should use NodePreGypBinary (#259) (fengmk2 <<fengmk2@gmail.com>>)
1.5.0 / 2022-07-09
==================
**features**
* [[`b15b10c`](http://github.com/cnpm/cnpmcore/commit/b15b10c5c6cfb32bcc2b1d94434cdd16871ae565)] - feat(mirror): add skia-canvas mirror (#258) (Beace <<beaceshimin@gmail.com>>)
**others**
* [[`2bd6ed0`](http://github.com/cnpm/cnpmcore/commit/2bd6ed0e5dace1d8840c342ecf4c86e8973dc6b7)] - 👌 IMPROVE: use tegg@1.2.0 (fengmk2 <<fengmk2@gmail.com>>)
1.4.0 / 2022-06-28
==================
**features**
* [[`57da0a3`](http://github.com/cnpm/cnpmcore/commit/57da0a3c7e56d6613b57391948949ffea24ec058)] - feat: add configuration enableNopmClientAndVersionCheck (laibao101 <<369632567@qq.com>>)
**others**
* [[`bf62932`](http://github.com/cnpm/cnpmcore/commit/bf62932f2e5224de6e34b873bf690a6e887b94b0)] - 🤖 TEST: Fix unstable test cases on OSS env (#254) (fengmk2 <<fengmk2@gmail.com>>)
1.3.2 / 2022-06-27
==================
**fixes**
* [[`c63159d`](http://github.com/cnpm/cnpmcore/commit/c63159d8df804fe711b664606fe42be42010eb38)] - fix: valid npm client with correct pattern (#252) (TZ | 天猪 <<atian25@qq.com>>)
**others**
* [[`d578baf`](http://github.com/cnpm/cnpmcore/commit/d578bafff07a0f9d4dd75393492cffc7f5d2660b)] - 🐛 FIX: Ignore exists seq on changes worker (#253) (fengmk2 <<fengmk2@gmail.com>>)
1.3.1 / 2022-06-24
==================
**fixes**
* [[`4ea0ef6`](http://github.com/cnpm/cnpmcore/commit/4ea0ef63b7af9fd4dcc247c2c2ac8e4d579f941a)] - fix: query changes with order by id asc (#251) (killa <<killa123@126.com>>)
1.3.0 / 2022-06-24
==================
**features**
* [[`0948a71`](http://github.com/cnpm/cnpmcore/commit/0948a71a40ac4897d129ef56830665dc028f07c7)] - feat: read enableChangesStream when sync changes stream (#250) (killa <<killa123@126.com>>)
1.2.0 / 2022-06-20
==================
**others**
* [[`c0d8b52`](http://github.com/cnpm/cnpmcore/commit/c0d8b52ea09736ac11b0ef780aec781d172fb94c)] - refactor: move CacheAdapter to ContextProto (#249) (killa <<killa123@126.com>>)
1.1.0 / 2022-06-20
==================
**features**
* [[`66b411e`](http://github.com/cnpm/cnpmcore/commit/66b411ea5bf6192dc9509df408525078e7128a27)] - feat: add type for exports (#248) (killa <<killa123@126.com>>)
1.0.0 / 2022-06-17
==================
**others**
* [[`5cadbf4`](http://github.com/cnpm/cnpmcore/commit/5cadbf4b22bee7d85cd14526f5d6c6e2cd3a2e4b)] - refactor: add infra module (#245) (killa <<killa123@126.com>>),fatal: No names found, cannot describe anything.

227
INTEGRATE.md Normal file
View File

@@ -0,0 +1,227 @@
# 🥚 如何在 [tegg](https://github.com/eggjs/egg/blob/next/tegg) 中集成 cnpmcore
> 文档中的示例项目可以在 [这里](https://github.com/eggjs/examples/commit/bed580fe053ae573f8b63f6788002ff9c6e7a142) 查看,在开始前请确保已阅读 [DEVELOPER.md](DEVELOPER.md) 中的相关文档,完成本地开发环境搭建。
在生产环境中,我们也可以直接部署 cnpmcore 系统,实现完整的 Registry 镜像功能。
但通常,在企业内部会有一些内部的中间件服务或限制,例如文件存储、缓存服务、登录鉴权流程等。
除了源码部署、二次开发的方式,我们还提供了 npm 包的方式,便于 [tegg](https://github.com/eggjs/egg/blob/next/tegg) 应用集成。
这样既可以享受到丰富的自定义扩展能力,又可以享受到 cnpmcore 持续迭代的功能演进。
下面,让我们以 [tegg](https://github.com/eggjs/egg/blob/next/tegg) 初始化的应用为例,以 npm 包的方式集成 cnpmcore并扩展登录功能以支持企业内 [SSO](https://en.wikipedia.org/wiki/Single_sign-on) 登录。
## 🚀 快速开始
### 🆕 新建一个 tegg 应用
> 我们以 <https://github.com/eggjs/examples/tree/master/hello-tegg> 为例
```shell
.
├── app
│   ├── biz
│   ├── controller
│   └── middleware
├── config
│   ├── config.default.ts
│   └── plugin.ts
├── package.json
├── test
│   ├── biz
│   └── controller
└── tsconfig.json
```
### 📦︎ 安装 cnpmcore 修改对应配置
```shell
npm i cnpmcore
```
1. 修改 `ts-config.json` 配置,这是因为 cnpmcore 使用了 [subPath](https://nodejs.org/api/packages.html#subpath-exports)
```json
{
"extends": "@eggjs/tsconfig",
"compilerOptions": {
"baseUrl": "./",
"target": "ES2021"
}
}
```
2. 修改 `config/plugin.ts` 文件,开启 cnpmcore 依赖的一些插件
```typescript
import tracerPlugin from '@eggjs/tracer';
import typeboxValidatePlugin from '@eggjs/typebox-validate';
import redisPlugin from '@eggjs/redis';
// 开启如下插件
export default {
...redisPlugin(),
...tracerPlugin(),
...typeboxValidatePlugin(),
}
```
3. 修改 `config.default.ts` 文件,可以直接覆盖默认配置
```typescript
import { SyncMode } from 'cnpmcore/common/constants';
import { cnpmcoreConfig } from 'cnpmcore/common/config';
export default () => {
const config = {};
config.cnpmcore = {
...cnpmcoreConfig,
enableChangesStream: false,
syncMode: SyncMode.all,
allowPublicRegistration: true,
// 放开注册配置
};
return config;
}
```
### 🧑‍🤝‍🧑 集成 cnpmcore
1. 创建文件夹,用于存放自定义的 infra module这里以 app/infra 为例
```shell
├── infra
│   ├── AuthAdapter.ts
│   ├── NFSAdapter.ts
│   ├── QueueAdapter.ts
│   └── package.json
```
* 添加 `package.json` ,声明 infra 作为一个 eggModule 单元
```JSON
{
"name": "infra",
"eggModule": {
"name": "infra"
}
}
```
* 添加 `XXXAdapter.ts` 在对应的 Adapter 中继承 cnpmcore 默认的 Adapter以 AuthAdapter 为例
```typescript
import { AccessLevel, SingletonProto } from 'egg';
import { AuthAdapter } from 'cnpmcore/infra/AuthAdapter';
@SingletonProto({
name: 'authAdapter',
accessLevel: AccessLevel.PUBLIC,
})
export class MyAuthAdapter extends AuthAdapter {
}
```
2. 添加 `config/module.json`,将 cnpmcore 作为一个 module 集成进我们新增的 tegg 应用中
```json
[
{
"path": "../app/biz"
},
{
"path": "../app/infra"
},
{
"package": "cnpmcore/common"
},
{
"package": "cnpmcore/core"
},
{
"package": "cnpmcore/port"
},
{
"package": "cnpmcore/repository"
}
]
```
### ✍🏻 重载 AuthAdapter 实现
我们以 AuthAdapter 为例,来实现 npm cli 的 SSO 登录的功能。
我们需要实现了 getAuthUrl 和 ensureCurrentUser 这两个方法:
1. getAuthUrl 引导用户访问企业内实际的登录中心。
2. ensureCurrentUser 当用户完成访问后,需要回调到应用进行鉴权流程。
我们约定通过 `POST /-/v1/login/sso/:sessionId` 这个路由来进行登录验证。
当然,你也可以任意修改地址和登录回调,只需保证更新 redis 中的 token 状态即可。
修改 AuthAdapter.ts 文件
```typescript
import { AccessLevel, Context, SingletonProto } from 'egg';
import { AuthAdapter } from 'cnpmcore/infra/AuthAdapter';
import { randomUUID } from 'crypto';
import { AuthUrlResult, userResult } from 'cnpmcore/dist/app/common/typing';
const ONE_DAY = 3600 * 24;
@SingletonProto({
name: 'authAdapter',
accessLevel: AccessLevel.PUBLIC,
})
export class MyAuthAdapter extends AuthAdapter {
async getAuthUrl(ctx: Context): Promise<AuthUrlResult> {
const sessionId = randomUUID();
await this.redis.setex(sessionId, ONE_DAY, '');
return {
// 替换实际企业内的登录中心地址,这里我们以系统内默认的 hello 路由为例
loginUrl: `${ctx.origin}/hello?name=${sessionId}`,
doneUrl: `${ctx.href}/done/session/${sessionId}`,
};
}
async ensureCurrentUser(): Promise<userResult | null> {
return {
name: 'hello',
email: 'hello@cnpmjs.org',
};
}
}
```
修改 HelloController 的实现,实际也可以通过登录中心回调、页面确认等方式实现
```typescript
// 触发回调接口,会自动完成用户创建
await this.httpclient.request(`${ctx.origin}/-/v1/login/sso/${name}`, { method: 'POST' });
```
## 🎉 功能验证
1. 在命令行输入 `npm login --registry=http://127.0.0.1:7001`
```shell
npm login --registry=http://127.0.0.1:7001
npm notice Log in on http://127.0.0.1:7001/
Login at:
http://127.0.0.1:7001/hello?name=e44e8c43-211a-4bcd-ae78-c4cbb1a78ae7
Press ENTER to open in the browser...
```
2. 界面提示回车打开浏览器访问登录中心,也就是我们在 getAuthUrl返回的 loginUrl 配置
3. 由于我们 mock 了对应实现,界面会直接显示登录成功
```shell
Logged in on http://127.0.0.1:7001/.
```
4. 在命令行输入 `npm whoami --registry=http://127.0.0.1:7001` 验证
```shell
npm whoami --registry=http://127.0.0.1:7001
hello
```

View File

@@ -1,36 +1,41 @@
# Private NPM Registry for Enterprise
[![Node.js CI](https://github.com/cnpm/cnpmcore/actions/workflows/nodejs.yml/badge.svg)](https://github.com/cnpm/cnpmcore/actions/workflows/nodejs.yml)
[![codecov](https://codecov.io/gh/cnpm/cnpmcore/branch/main/graph/badge.svg)](https://codecov.io/gh/cnpm/cnpmcore)
[![CodeQL](https://github.com/cnpm/cnpmcore/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/cnpm/cnpmcore/actions/workflows/codeql-analysis.yml)
[![emoji-log](https://cdn.rawgit.com/ahmadawais/stuff/ca97874/emoji-log/non-flat-round.svg)](https://github.com/ahmadawais/Emoji-Log/)
[![Node.js CI](https://github.com/cnpm/cnpmcore/actions/workflows/nodejs.yml/badge.svg?branch=master)](https://github.com/cnpm/cnpmcore/actions/workflows/nodejs.yml)
[![codecov](https://codecov.io/gh/cnpm/cnpmcore/master/main/graph/badge.svg)](https://app.codecov.io/gh/cnpm/cnpmcore/tree/master)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fcnpm%2Fcnpmcore.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fcnpm%2Fcnpmcore?ref=badge_shield)
[![Node.js Version](https://img.shields.io/node/v/cnpmcore.svg?style=flat)](https://nodejs.org/en/download/)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com)
![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/cnpm/cnpmcore)
[![NPM Version](https://img.shields.io/npm/v/cnpmcore)](https://www.npmjs.com/package/cnpmcore)
[![NPM Downloads](https://img.shields.io/npm/dm/cnpmcore)](https://www.npmjs.com/package/cnpmcore)
[![NPM License](https://img.shields.io/npm/l/cnpmcore)](https://github.com/cnpm/cnpmcore/blob/master/LICENSE)
Reimplementation based on [cnpmjs.org](https://github.com/cnpm/cnpmjs.org) with TypeScript.
Reimplement based on [cnpmjs.org](https://github.com/cnpm/cnpmjs.org) with TypeScript.
## Registry HTTP API
See https://github.com/cnpm/cnpmjs.org/blob/master/docs/registry-api.md#npm-registry-api
See [registry-api.md](docs/registry-api.md)
## Internal API for Direct HTTP Requests
See [internal-api.md](docs/internal-api.md) for comprehensive documentation of cnpmcore's internal APIs that allow direct HTTP requests for package synchronization, administration, and other advanced operations.
## How to contribute
See [DEVELOPER.md](DEVELOPER.md)
## How to integrate
See [INTEGRATE.md](INTEGRATE.md)
## License
[MIT](LICENSE)
<!-- GITCONTRIBUTOR_START -->
## Contributors
|[<img src="https://avatars.githubusercontent.com/u/156269?v=4" width="100px;"/><br/><sub><b>fengmk2</b></sub>](https://github.com/fengmk2)<br/>|[<img src="https://avatars.githubusercontent.com/u/6897780?v=4" width="100px;"/><br/><sub><b>killagu</b></sub>](https://github.com/killagu)<br/>|[<img src="https://avatars.githubusercontent.com/u/5574625?v=4" width="100px;"/><br/><sub><b>elrrrrrrr</b></sub>](https://github.com/elrrrrrrr)<br/>|[<img src="https://avatars.githubusercontent.com/u/26033663?v=4" width="100px;"/><br/><sub><b>Zian502</b></sub>](https://github.com/Zian502)<br/>|[<img src="https://avatars.githubusercontent.com/u/13284978?v=4" width="100px;"/><br/><sub><b>Beace</b></sub>](https://github.com/Beace)<br/>|[<img src="https://avatars.githubusercontent.com/u/227713?v=4" width="100px;"/><br/><sub><b>atian25</b></sub>](https://github.com/atian25)<br/>|
| :---: | :---: | :---: | :---: | :---: | :---: |
|[<img src="https://avatars.githubusercontent.com/u/17879221?v=4" width="100px;"/><br/><sub><b>laibao101</b></sub>](https://github.com/laibao101)<br/>|[<img src="https://avatars.githubusercontent.com/u/8198408?v=4" width="100px;"/><br/><sub><b>BlackHole1</b></sub>](https://github.com/BlackHole1)<br/>|[<img src="https://avatars.githubusercontent.com/u/1814071?v=4" width="100px;"/><br/><sub><b>xiekw2010</b></sub>](https://github.com/xiekw2010)<br/>|[<img src="https://avatars.githubusercontent.com/u/13471233?v=4" width="100px;"/><br/><sub><b>OpportunityLiu</b></sub>](https://github.com/OpportunityLiu)<br/>|[<img src="https://avatars.githubusercontent.com/u/958063?v=4" width="100px;"/><br/><sub><b>thonatos</b></sub>](https://github.com/thonatos)<br/>|[<img src="https://avatars.githubusercontent.com/u/11039003?v=4" width="100px;"/><br/><sub><b>chenpx976</b></sub>](https://github.com/chenpx976)<br/>|
[<img src="https://avatars.githubusercontent.com/u/29791463?v=4" width="100px;"/><br/><sub><b>fossabot</b></sub>](https://github.com/fossabot)<br/>|[<img src="https://avatars.githubusercontent.com/u/1119126?v=4" width="100px;"/><br/><sub><b>looksgood</b></sub>](https://github.com/looksgood)<br/>|[<img src="https://avatars.githubusercontent.com/u/3478550?v=4" width="100px;"/><br/><sub><b>coolyuantao</b></sub>](https://github.com/coolyuantao)<br/>
[![Contributors](https://contrib.rocks/image?repo=cnpm/cnpmcore)](https://github.com/cnpm/cnpmcore/graphs/contributors)
This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Sun Aug 28 2022 19:00:22 GMT+0800`.
<!-- GITCONTRIBUTOR_END -->
Made with [contributors-img](https://contrib.rocks).
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fcnpm%2Fcnpmcore.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fcnpm%2Fcnpmcore?ref=badge_large)

View File

@@ -6,7 +6,7 @@ Currently being supported with security updates.
| Version | Supported |
| -------- | ------------------ |
| >= 1.0.0 | :white_check_mark: |
| >= 3.0.0 | :white_check_mark: |
## Reporting a Vulnerability
@@ -35,7 +35,7 @@ When the security team receives a security bug report, they will assign it
to a primary handler. This person will coordinate the fix and release
process, involving the following steps:
* Confirm the problem and determine the affected versions.
* Audit code to find any potential similar problems.
* Prepare fixes for all releases still under maintenance. These fixes
* Confirm the problem and determine the affected versions.
* Audit code to find any potential similar problems.
* Prepare fixes for all releases still under maintenance. These fixes
will be released as fast as possible to NPM.

37
app.ts
View File

@@ -1,6 +1,8 @@
import path from 'path';
import { readFile } from 'fs/promises';
import { Application } from 'egg';
import path from 'node:path';
import { readFile } from 'node:fs/promises';
import type { Application, ILifecycleBoot } from 'egg';
import { ChangesStreamService } from './app/core/service/ChangesStreamService.ts';
declare module 'egg' {
interface Application {
@@ -8,7 +10,7 @@ declare module 'egg' {
}
}
export default class CnpmcoreAppHook {
export default class CnpmcoreAppHook implements ILifecycleBoot {
private readonly app: Application;
constructor(app: Application) {
@@ -16,11 +18,34 @@ export default class CnpmcoreAppHook {
this.app.binaryHTML = '';
}
configWillLoad() {
const app = this.app;
// https://github.com/eggjs/egg/blob/next/tegg/plugin/orm/src/app.ts#L37
// store query sql to log
app.config.orm.logger = {
...app.config.orm.logger,
logQuery(sql: string, duration: number) {
app.getLogger('sqlLogger').info('[%s] %s', duration, sql);
},
};
}
// https://eggjs.org/zh-cn/basics/app-start.html
async didReady() {
// ready binary.html and replace registry
const filepath = path.join(this.app.baseDir, 'app/port/binary.html');
const text = await readFile(filepath, 'utf-8');
this.app.binaryHTML = text.replace('{{registry}}', this.app.config.cnpmcore.registry);
const text = await readFile(filepath, 'utf8');
this.app.binaryHTML = text.replace(
'{{registry}}',
this.app.config.cnpmcore.registry
);
}
// 应用退出时执行
// 需要暂停当前执行的 changesStream task
async beforeClose() {
const changesStreamService =
await this.app.getEggObject(ChangesStreamService);
await changesStreamService.suspendSync(true);
}
}

View File

@@ -1,14 +1,8 @@
import {
Inject,
} from '@eggjs/tegg';
import {
EggAppConfig,
EggLogger,
} from 'egg';
import { EggAppConfig, Logger, Inject } from 'egg';
export abstract class AbstractService {
@Inject()
protected readonly config: EggAppConfig;
@Inject()
protected readonly logger: EggLogger;
protected readonly logger: Logger;
}

36
app/common/CryptoUtil.ts Normal file
View File

@@ -0,0 +1,36 @@
import { generateKeyPairSync } from 'node:crypto';
import NodeRSA from 'node-rsa';
// generate rsa key pair
export function genRSAKeys(): { publicKey: string, privateKey: string } {
const key = generateKeyPairSync('rsa', {
modulusLength: 512,
});
const publicKey = key.publicKey.export({
type: 'pkcs1',
format: 'pem',
}).toString('base64');
const privateKey = key.privateKey.export({
type: 'pkcs1',
format: 'pem',
}).toString('base64');
return { publicKey, privateKey };
}
// encrypt rsa private key
export function encryptRSA(publicKey: string, plainText: string): string {
const key = new NodeRSA(publicKey, 'pkcs1-public-pem', {
encryptionScheme: 'pkcs1',
environment: 'browser',
});
return key.encrypt(plainText, 'base64');
}
// decrypt rsa private key
export function decryptRSA(privateKey: string, encryptedBase64: string): string {
const key = new NodeRSA(privateKey, 'pkcs1-private-pem', {
encryptionScheme: 'pkcs1',
environment: 'browser',
});
return key.decrypt(encryptedBase64, 'utf8');
}

29
app/common/ErrorUtil.ts Normal file
View File

@@ -0,0 +1,29 @@
const TimeoutErrorNames = new Set([
'HttpClientRequestTimeoutError',
'HttpClientConnectTimeoutError',
'ConnectionError',
'ConnectTimeoutError',
'BodyTimeoutError',
'ResponseTimeoutError',
]);
export function isTimeoutError(err: Error) {
if (TimeoutErrorNames.has(err.name)) {
return true;
}
if (err instanceof AggregateError && err.errors) {
for (const subError of err.errors) {
if (TimeoutErrorNames.has(subError.name)) {
return true;
}
}
}
if (
'cause' in err &&
err.cause instanceof Error &&
TimeoutErrorNames.has(err.cause.name)
) {
return true;
}
return false;
}

View File

@@ -1,57 +1,41 @@
import { mkdir, rm } from 'fs/promises';
import { createWriteStream } from 'fs';
import { setTimeout } from 'timers/promises';
import path from 'path';
import url from 'url';
import { randomBytes } from 'crypto';
import { EggContextHttpClient } from 'egg';
import dayjs from './dayjs';
// oxlint-disable import/exports-last
import { mkdir, rm } from 'node:fs/promises';
import { createWriteStream } from 'node:fs';
import { setTimeout } from 'node:timers/promises';
import path from 'node:path';
import url from 'node:url';
import { randomBytes } from 'node:crypto';
import type { EggContextHttpClient, HttpClientResponse } from 'egg';
import mime from 'mime-types';
import dayjs from './dayjs.ts';
export async function createTempfile(dataDir: string, filename: string) {
// will auto clean on CleanTempDir Schedule
const tmpdir = path.join(dataDir, 'downloads', dayjs().format('YYYY/MM/DD'));
await mkdir(tmpdir, { recursive: true });
// The filename is a URL (from dist.tarball), which needs to be truncated, (`getconf NAME_MAX /` # max filename length: 255 bytes)
// https://github.com/cnpm/cnpmjs.org/pull/1345
const tmpfile = path.join(tmpdir, `${randomBytes(10).toString('hex')}-${path.basename(url.parse(filename).pathname!)}`);
return tmpfile;
}
export async function downloadToTempfile(httpclient: EggContextHttpClient,
dataDir: string, url: string, ignoreDownloadStatuses?: number[], retries = 3) {
let lastError: any;
while (retries > 0) {
try {
return await _downloadToTempfile(httpclient, dataDir, url, ignoreDownloadStatuses);
} catch (err: any) {
if (err.name === 'DownloadNotFoundError') throw err;
lastError = err;
}
retries--;
if (retries > 0) {
// sleep 1s ~ 4s in random
const delay = process.env.NODE_ENV === 'test' ? 1 : 1000 + Math.random() * 4000;
await setTimeout(delay);
}
}
throw lastError;
}
async function _downloadToTempfile(httpclient: EggContextHttpClient,
dataDir: string, url: string, ignoreDownloadStatuses?: number[]) {
async function _downloadToTempfile(
httpclient: EggContextHttpClient,
dataDir: string,
url: string,
optionalConfig?: DownloadToTempfileOptionalConfig
): Promise<Tempfile> {
const tmpfile = await createTempfile(dataDir, url);
const writeStream = createWriteStream(tmpfile);
try {
// max 10 mins to download
// FIXME: should show download progress
const { status, headers, res } = await httpclient.request(url, {
timeout: 60000 * 10,
const requestHeaders: Record<string, string> = {};
if (optionalConfig?.remoteAuthToken) {
requestHeaders.authorization = `Bearer ${optionalConfig.remoteAuthToken}`;
}
const { status, headers, res } = (await httpclient.request(url, {
timeout: 60_000 * 10,
headers: requestHeaders,
writeStream,
timing: true,
followRedirect: true,
});
if (status === 404 || (ignoreDownloadStatuses && ignoreDownloadStatuses.includes(status))) {
})) as HttpClientResponse;
if (
status === 404 ||
(optionalConfig?.ignoreDownloadStatuses &&
optionalConfig.ignoreDownloadStatuses.includes(status))
) {
const err = new Error(`Not found, status(${status})`);
err.name = 'DownloadNotFoundError';
throw err;
@@ -71,3 +55,107 @@ async function _downloadToTempfile(httpclient: EggContextHttpClient,
throw err;
}
}
export interface DownloadToTempfileOptionalConfig {
retries?: number;
ignoreDownloadStatuses?: number[];
remoteAuthToken?: string;
}
export async function createTempDir(dataDir: string, dirname?: string) {
// will auto clean on CleanTempDir Schedule
let tmpdir = path.join(dataDir, 'downloads', dayjs().format('YYYY/MM/DD'));
if (dirname) {
tmpdir = path.join(tmpdir, dirname);
}
await mkdir(tmpdir, { recursive: true });
return tmpdir;
}
export async function createTempfile(dataDir: string, filename: string) {
const tmpdir = await createTempDir(dataDir);
// The filename is a URL (from dist.tarball), which needs to be truncated, (`getconf NAME_MAX /` # max filename length: 255 bytes)
// https://github.com/cnpm/cnpmjs.org/pull/1345
const tmpfile = path.join(
tmpdir,
// oxlint-disable-next-line typescript-eslint/no-non-null-assertion
`${randomBytes(10).toString('hex')}-${path.basename(url.parse(filename).pathname!)}`
);
return tmpfile;
}
export async function downloadToTempfile(
httpclient: EggContextHttpClient,
dataDir: string,
url: string,
optionalConfig?: DownloadToTempfileOptionalConfig
) {
let retries = optionalConfig?.retries || 3;
let lastError: Error | undefined;
while (retries > 0) {
try {
return await _downloadToTempfile(
httpclient,
dataDir,
url,
optionalConfig
);
} catch (err) {
if (err.name === 'DownloadNotFoundError') throw err;
lastError = err;
}
retries--;
if (retries > 0) {
// sleep 1s ~ 4s in random
const delay =
process.env.NODE_ENV === 'test' ? 1 : 1000 + Math.random() * 4000;
await setTimeout(delay);
}
}
// oxlint-disable-next-line no-throw-literal
throw lastError;
}
export interface Tempfile {
tmpfile: string;
headers: HttpClientResponse['res']['headers'];
timing: HttpClientResponse['res']['timing'];
}
const DEFAULT_CONTENT_TYPE = 'application/octet-stream';
const PLAIN_TEXT = 'text/plain';
const WHITE_FILENAME_CONTENT_TYPES = {
license: PLAIN_TEXT,
readme: PLAIN_TEXT,
history: PLAIN_TEXT,
changelog: PLAIN_TEXT,
'.npmignore': PLAIN_TEXT,
'.jshintignore': PLAIN_TEXT,
'.eslintignore': PLAIN_TEXT,
'.jshintrc': 'application/json',
'.eslintrc': 'application/json',
} as const;
const CONTENT_TYPE_BLACKLIST = new Set(['application/xml', 'text/html']);
export function ensureContentType(contentType: string) {
if (CONTENT_TYPE_BLACKLIST.has(contentType)) {
return 'text/plain';
}
return contentType;
}
export function mimeLookup(filepath: string) {
const filename = path.basename(filepath).toLowerCase();
if (filename.endsWith('.ts')) return PLAIN_TEXT;
if (filename.endsWith('.lock')) return PLAIN_TEXT;
const defaultContentType = mime.lookup(filename);
// https://github.com/cnpm/cnpmcore/issues/693#issuecomment-2955268229
const contentType =
defaultContentType ||
WHITE_FILENAME_CONTENT_TYPES[
filename as keyof typeof WHITE_FILENAME_CONTENT_TYPES
] ||
DEFAULT_CONTENT_TYPE;
return ensureContentType(contentType);
}

View File

@@ -1,5 +1,15 @@
import { createReadStream } from 'fs';
import * as ssri from 'ssri';
import { createReadStream } from 'node:fs';
import { Readable } from 'node:stream';
import { pipeline } from 'node:stream/promises';
import { fromData, fromStream, type HashLike } from 'ssri';
// @ts-expect-error no types available
import tar from '@fengmk2/tar';
import type {
AuthorType,
PackageJSONType,
} from '../repository/PackageRepository.ts';
// /@cnpm%2ffoo
// /@cnpm%2Ffoo
@@ -7,28 +17,44 @@ import * as ssri from 'ssri';
// /foo
// name max length is 214 chars
// https://www.npmjs.com/package/path-to-regexp#custom-matching-parameters
export const FULLNAME_REG_STRING = '@[^/]{1,220}\/[^/]{1,220}|@[^%]+\%2[fF][^/]{1,220}|[^@/]{1,220}';
export const FULLNAME_REG_STRING =
'@[^/]{1,220}/[^/]{1,220}|@[^%]+%2[fF][^/]{1,220}|[^@/]{1,220}';
export function getScopeAndName(fullname: string): string[] {
if (fullname.startsWith('@')) {
return fullname.split('/', 2);
}
return [ '', fullname ];
return ['', fullname];
}
export function getFullname(scope: string, name: string): string {
return scope ? `${scope}/${name}` : name;
}
export async function calculateIntegrity(contentOrFile: Uint8Array | string) {
let integrityObj;
export function cleanUserPrefix(username: string): string {
return username.replace(/^.*:/, '');
}
export function getPrefixedName(prefix: string, username: string): string {
return prefix ? `${prefix}${username}` : username;
}
export interface Integrity {
integrity: string;
shasum: string;
}
export async function calculateIntegrity(
contentOrFile: Uint8Array | string
): Promise<Integrity> {
let integrityObj: HashLike;
if (typeof contentOrFile === 'string') {
integrityObj = await ssri.fromStream(createReadStream(contentOrFile), {
algorithms: [ 'sha512', 'sha1' ],
integrityObj = await fromStream(createReadStream(contentOrFile), {
algorithms: ['sha512', 'sha1'],
});
} else {
integrityObj = ssri.fromData(contentOrFile, {
algorithms: [ 'sha512', 'sha1' ],
integrityObj = fromData(contentOrFile, {
algorithms: ['sha512', 'sha1'],
});
}
const integrity = integrityObj.sha512[0].toString() as string;
@@ -36,20 +62,109 @@ export async function calculateIntegrity(contentOrFile: Uint8Array | string) {
return { integrity, shasum };
}
export function formatTarball(registry: string, scope: string, name: string, version: string) {
export function formatTarball(
registry: string,
scope: string,
name: string,
version: string
) {
const fullname = getFullname(scope, name);
return `${registry}/${fullname}/-/${name}-${version}.tgz`;
}
export function detectInstallScript(manifest: any) {
export function detectInstallScript(manifest: {
scripts?: Record<string, string>;
}) {
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object
let hasInstallScript = false;
const scripts = manifest.scripts;
if (scripts) {
// https://www.npmjs.com/package/fix-has-install-script
if (scripts.install || scripts.preinstall || scripts.postinstall) {
hasInstallScript = true;
}
// https://www.npmjs.com/package/fix-has-install-script
if (scripts?.install || scripts?.preinstall || scripts?.postinstall) {
hasInstallScript = true;
}
return hasInstallScript;
}
/** 判断一个版本压缩包中是否包含 npm-shrinkwrap.json */
export async function hasShrinkWrapInTgz(
contentOrFile: Uint8Array | string
): Promise<boolean> {
let readable: Readable;
if (typeof contentOrFile === 'string') {
readable = createReadStream(contentOrFile);
} else {
readable = new Readable({
read() {
this.push(contentOrFile);
this.push(null);
},
});
}
let hasShrinkWrap = false;
const abortController = new AbortController();
const parser = tar.t({
// options.strict 默认为 false会忽略 Recoverable errors例如 tar 解析失败
// 详见 https://github.com/isaacs/node-tar#warnings-and-errors
// oxlint-disable-next-line typescript-eslint/no-explicit-any
onentry(entry: any) {
if (entry.path === 'package/npm-shrinkwrap.json') {
hasShrinkWrap = true;
abortController.abort();
}
},
});
try {
await pipeline(readable, parser, { signal: abortController.signal });
return hasShrinkWrap;
} catch (e) {
if (e.code === 'ABORT_ERR') {
return hasShrinkWrap;
}
throw Object.assign(
new Error('[hasShrinkWrapInTgz] Fail to parse input file'),
{ cause: e }
);
}
}
/** 写入 ES 时,格式化 author */
export function formatAuthor(
author: string | AuthorType | undefined
): AuthorType | undefined {
if (author === undefined) {
return author;
}
if (typeof author === 'string') {
return { name: author };
}
return author;
}
export async function extractPackageJSON(
tarballBytes: Buffer
): Promise<PackageJSONType> {
// oxlint-disable-next-line promise/avoid-new
return new Promise((resolve, reject) => {
Readable.from(tarballBytes).pipe(
tar.t({
filter: (name: string) => name === 'package/package.json',
onentry: async (entry: Readable) => {
const chunks: Buffer[] = [];
for await (const chunk of entry) {
chunks.push(chunk);
}
try {
const data = Buffer.concat(chunks);
return resolve(JSON.parse(data.toString()));
} catch {
reject(new Error('Error parsing package.json'));
}
},
})
);
});
}

View File

@@ -1,6 +1,6 @@
import { EggContext } from '@eggjs/tegg';
import type { Context } from 'egg';
export function isSyncWorkerRequest(ctx: EggContext) {
export function isSyncWorkerRequest(ctx: Context) {
// sync request will contain this query params
let isSyncWorkerRequest = ctx.query.cache === '0';
if (!isSyncWorkerRequest) {

View File

@@ -1,7 +1,8 @@
import crypto from 'crypto';
import crypto from 'node:crypto';
import base from 'base-x';
import { crc32 } from '@node-rs/crc32';
import * as ssri from 'ssri';
import { checkData, create } from 'ssri';
import UAParser from 'ua-parser-js';
const base62 = base('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
@@ -28,14 +29,28 @@ export function checkToken(token: string, prefix: string): boolean {
}
export function integrity(plain: string): string {
return ssri.create().update(plain).digest()
return create().update(plain).digest()
.toString();
}
export function checkIntegrity(plain: string, expectedIntegrity: string): boolean {
return ssri.checkData(plain, expectedIntegrity);
return !!checkData(plain, expectedIntegrity);
}
export function sha512(plain: string): string {
return crypto.createHash('sha512').update(plain).digest('hex');
}
export function getUAInfo(userAgent?: string) {
if (!userAgent) return null;
return new UAParser(userAgent);
}
export function getBrowserTypeForWebauthn(userAgent?: string) {
const ua = getUAInfo(userAgent);
if (!ua) return null;
const os = ua.getOS();
if (os.name === 'iOS' || os.name === 'Android') return 'mobile';
if (os.name === 'Mac OS') return ua.getBrowser().name;
return null;
}

View File

@@ -1,5 +1,6 @@
import { AccessLevel, SingletonProto } from '@eggjs/tegg';
import { BugVersion } from '../../core/entity/BugVersion';
import { AccessLevel, SingletonProto } from 'egg';
import type { BugVersion } from '../../core/entity/BugVersion.ts';
@SingletonProto({
accessLevel: AccessLevel.PUBLIC,

View File

@@ -1,18 +1,16 @@
import {
ContextProto,
AccessLevel,
Inject,
} from '@eggjs/tegg';
import { Redis } from 'ioredis';
import { AccessLevel, Inject, SingletonProto } from 'egg';
// FIXME: @eggjs/redis should use ioredis v5
// https://github.com/eggjs/redis/issues/35
import type { Redis } from 'ioredis';
const ONE_DAY = 3600 * 24;
@ContextProto({
@SingletonProto({
accessLevel: AccessLevel.PUBLIC,
})
export class CacheAdapter {
@Inject()
private readonly redis: Redis;
private readonly redis: Redis; // 由 redis 插件引入
async setBytes(key: string, bytes: Buffer) {
await this.redis.setex(key, ONE_DAY, bytes);
@@ -38,7 +36,7 @@ export class CacheAdapter {
const lockName = this.getLockName(key);
const existsTimestamp = await this.redis.get(lockName);
if (existsTimestamp) {
if (Date.now() - parseInt(existsTimestamp) < seconds * 1000) {
if (Date.now() - Number.parseInt(existsTimestamp) < seconds * 1000) {
return null;
}
// lock timeout, delete it
@@ -62,12 +60,13 @@ export class CacheAdapter {
async usingLock(key: string, seconds: number, func: () => Promise<void>) {
const lockTimestamp = await this.lock(key, seconds);
if (!lockTimestamp) return;
if (!lockTimestamp) return false;
try {
await func();
} finally {
await this.unlock(key, lockTimestamp);
}
return true;
}
private getLockName(key: string) {

View File

@@ -1,18 +1,15 @@
import { Readable } from 'stream';
import {
ContextProto,
AccessLevel,
Inject,
} from '@eggjs/tegg';
import { Pointcut } from '@eggjs/tegg/aop';
import { EggLogger } from 'egg';
import { AsyncTimer } from '../aop/AsyncTimer';
import { NFSClient } from '../typing';
import { IncomingHttpHeaders } from 'http';
import type { Readable } from 'node:stream';
import type { IncomingHttpHeaders } from 'node:http';
import { AccessLevel, Inject, SingletonProto, Logger } from 'egg';
import { Pointcut } from 'egg/aop';
import { AsyncTimer } from '../aop/AsyncTimer.ts';
import type { NFSClient } from '../typing.ts';
const INSTANCE_NAME = 'nfsAdapter';
@ContextProto({
@SingletonProto({
name: INSTANCE_NAME,
accessLevel: AccessLevel.PUBLIC,
})
@@ -21,17 +18,27 @@ export class NFSAdapter {
private readonly nfsClient: NFSClient;
@Inject()
private readonly logger: EggLogger;
private readonly logger: Logger;
@Pointcut(AsyncTimer)
async uploadBytes(storeKey: string, bytes: Uint8Array) {
this.logger.info('[%s:uploadBytes] key: %s, bytes: %d', INSTANCE_NAME, storeKey, bytes.length);
this.logger.info(
'[%s:uploadBytes] key: %s, bytes: %d',
INSTANCE_NAME,
storeKey,
bytes.length
);
await this.nfsClient.uploadBytes(bytes, { key: storeKey });
}
// will return next store position
@Pointcut(AsyncTimer)
async appendBytes(storeKey: string, bytes: Uint8Array, position?: string, headers?: IncomingHttpHeaders) {
async appendBytes(
storeKey: string,
bytes: Uint8Array,
position?: string,
headers?: IncomingHttpHeaders
) {
// make sure position is undefined by the first time
if (!position) position = undefined;
const options = {
@@ -45,13 +52,30 @@ export class NFSAdapter {
@Pointcut(AsyncTimer)
async uploadFile(storeKey: string, file: string) {
this.logger.info('[%s:uploadFile] key: %s, file: %s', INSTANCE_NAME, storeKey, file);
this.logger.info(
'[%s:uploadFile] key: %s, file: %s',
INSTANCE_NAME,
storeKey,
file
);
await this.nfsClient.upload(file, { key: storeKey });
}
@Pointcut(AsyncTimer)
async downloadFile(storeKey: string, file: string, timeout: number) {
this.logger.info(
'[%s:downloadFile] key: %s, file: %s, timeout: %s',
INSTANCE_NAME,
storeKey,
file,
timeout
);
await this.nfsClient.download(storeKey, file, { timeout });
}
@Pointcut(AsyncTimer)
async remove(storeKey: string) {
this.logger.info('[%s:remove] key: %s, file: %s', INSTANCE_NAME, storeKey);
this.logger.info('[%s:remove] key: %s', INSTANCE_NAME, storeKey);
await this.nfsClient.remove(storeKey);
}
@@ -72,7 +96,9 @@ export class NFSAdapter {
}
}
async getDownloadUrlOrStream(storeKey: string): Promise<string | Readable | undefined> {
async getDownloadUrlOrStream(
storeKey: string
): Promise<string | Readable | undefined> {
const downloadUrl = await this.getDownloadUrl(storeKey);
if (downloadUrl) {
return downloadUrl;

View File

@@ -1,30 +1,36 @@
import { setTimeout } from 'timers/promises';
import { setTimeout } from 'node:timers/promises';
import {
ContextProto,
AccessLevel,
Inject,
} from '@eggjs/tegg';
import {
EggLogger,
EggContextHttpClient,
AccessLevel, Inject,
EggAppConfig,
HttpClient,
Logger,
HttpClientRequestOptions,
HttpClientResponse,
} from 'egg';
import { HttpMethod } from 'urllib/src/Request';
import type { PackageManifestType } from '../../repository/PackageRepository.ts';
import { isTimeoutError } from '../ErrorUtil.ts';
type HttpMethod = HttpClientRequestOptions['method'];
const INSTANCE_NAME = 'npmRegistry';
export type RegistryResponse = { method: HttpMethod } & HttpClientResponse;
@ContextProto({
name: INSTANCE_NAME,
accessLevel: AccessLevel.PUBLIC,
})
export class NPMRegistry {
@Inject()
private readonly logger: EggLogger;
private readonly logger: Logger;
@Inject()
private readonly httpclient: EggContextHttpClient;
private readonly httpClient: HttpClient;
@Inject()
private config: EggAppConfig;
private timeout = 10000;
private timeout = 10_000;
public registryHost: string;
get registry(): string {
@@ -35,68 +41,119 @@ export class NPMRegistry {
this.registryHost = registryHost;
}
public async getFullManifests(fullname: string, retries = 3) {
public async getFullManifests(
fullname: string,
optionalConfig?: { retries?: number; remoteAuthToken?: string }
): Promise<{ method: HttpMethod } & HttpClientResponse<PackageManifestType>> {
let retries = optionalConfig?.retries || 3;
// set query t=timestamp, make sure CDN cache disable
// cache=0 is sync worker request flag
const url = `${this.registry}/${encodeURIComponent(fullname)}?t=${Date.now()}&cache=0`;
let lastError: any;
let lastError: Error | undefined;
while (retries > 0) {
try {
// large package: https://r.cnpmjs.org/%40procore%2Fcore-icons
// https://r.cnpmjs.org/intraactive-sdk-ui 44s
return await this.request('GET', url, undefined, { timeout: 120000 });
} catch (err: any) {
if (err.name === 'ResponseTimeoutError') throw err;
const authorization = this.genAuthorizationHeader(
optionalConfig?.remoteAuthToken
);
return await this.request('GET', url, undefined, {
timeout: 120_000,
headers: { authorization },
});
} catch (err) {
if (isTimeoutError(err)) {
throw err;
}
lastError = err;
}
retries--;
if (retries > 0) {
// sleep 1s ~ 4s in random
const delay = process.env.NODE_ENV === 'test' ? 1 : 1000 + Math.random() * 4000;
const delay =
process.env.NODE_ENV === 'test' ? 1 : 1000 + Math.random() * 4000;
await setTimeout(delay);
}
}
// oxlint-disable-next-line no-throw-literal
throw lastError;
}
// app.put('/:name/sync', sync.sync);
public async createSyncTask(fullname: string) {
public async createSyncTask(
fullname: string,
optionalConfig?: { remoteAuthToken?: string }
): Promise<RegistryResponse> {
const authorization = this.genAuthorizationHeader(
optionalConfig?.remoteAuthToken
);
const url = `${this.registry}/${encodeURIComponent(fullname)}/sync?sync_upstream=true&nodeps=true`;
// {
// ok: true,
// logId: logId
// };
return await this.request('PUT', url);
return await this.request('PUT', url, undefined, { authorization });
}
// app.get('/:name/sync/log/:id', sync.getSyncLog);
public async getSyncTask(fullname: string, id: string, offset: number) {
public async getSyncTask(
fullname: string,
id: string,
offset: number,
optionalConfig?: { remoteAuthToken?: string }
): Promise<RegistryResponse> {
const authorization = this.genAuthorizationHeader(
optionalConfig?.remoteAuthToken
);
const url = `${this.registry}/${encodeURIComponent(fullname)}/sync/log/${id}?offset=${offset}`;
// { ok: true, syncDone: syncDone, log: log }
return await this.request('GET', url);
return await this.request('GET', url, undefined, { authorization });
}
public async getDownloadRanges(registry: string, fullname: string, start: string, end: string) {
public async getDownloadRanges(
registry: string,
fullname: string,
start: string,
end: string,
optionalConfig?: { remoteAuthToken?: string }
): Promise<RegistryResponse> {
const authorization = this.genAuthorizationHeader(
optionalConfig?.remoteAuthToken
);
const url = `${registry}/downloads/range/${start}:${end}/${encodeURIComponent(fullname)}`;
return await this.request('GET', url);
return await this.request('GET', url, undefined, { authorization });
}
private async request(method: HttpMethod, url: string, params?: object, options?: object) {
const res = await this.httpclient.request(url, {
private async request(
method: HttpMethod,
url: string,
params?: object,
options?: object
): Promise<RegistryResponse> {
const res = (await this.httpClient.request(url, {
method,
data: params,
dataType: 'json',
timing: true,
retry: 3,
timeout: this.timeout,
followRedirect: true,
gzip: true,
...options,
});
this.logger.info('[NPMRegistry:request] %s %s, status: %s', method, url, res.status);
return {
})) as HttpClientResponse;
this.logger.info(
'[NPMRegistry:request] %s %s, status: %s',
method,
url,
res.status
);
return {
method,
...res,
};
}
public genAuthorizationHeader(remoteAuthToken?: string) {
return remoteAuthToken ? `Bearer ${remoteAuthToken}` : '';
}
}

View File

@@ -1,69 +1,108 @@
import { EggContextHttpClient, EggLogger } from 'egg';
import { BinaryTaskConfig } from '../../../../config/binaries';
import {
Inject,
QualifierImplDecoratorUtil,
type ImplDecorator,
HttpClient,
Logger,
} from 'egg';
export type BinaryItem = {
import type { BinaryType } from '../../enum/Binary.ts';
import type {
BinaryName,
BinaryTaskConfig,
} from '../../../../config/binaries.ts';
const platforms = ['darwin', 'linux', 'win32'] as const;
export interface BinaryItem {
name: string;
isDir: boolean;
url: string;
size: string | number;
date: string;
ignoreDownloadStatuses?: number[];
};
}
export type FetchResult = {
export interface FetchResult {
items: BinaryItem[];
// oxlint-disable-next-line typescript-eslint/no-explicit-any
nextParams?: any;
};
}
export const BINARY_ADAPTER_ATTRIBUTE = Symbol('BINARY_ADAPTER_ATTRIBUTE');
export abstract class AbstractBinary {
protected httpclient: EggContextHttpClient;
protected logger: EggLogger;
protected binaryConfig: BinaryTaskConfig;
@Inject()
protected logger: Logger;
constructor(httpclient: EggContextHttpClient, logger: EggLogger, binaryConfig: BinaryTaskConfig) {
this.httpclient = httpclient;
this.logger = logger;
this.binaryConfig = binaryConfig;
@Inject()
protected httpclient: HttpClient;
abstract initFetch(binaryName: BinaryName): Promise<void>;
abstract fetch(
dir: string,
binaryName: BinaryName,
lastData?: Record<string, unknown>
): Promise<FetchResult | undefined>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async finishFetch(_success: boolean, _binaryName: BinaryName): Promise<void> {
// do not thing by default
}
abstract fetch(dir: string, params?: any): Promise<FetchResult | undefined>;
protected async requestXml(url: string) {
const { status, data, headers } = await this.httpclient.request(url, {
timeout: 20000,
timeout: 30_000,
followRedirect: true,
gzip: true,
});
const xml = data.toString() as string;
if (status !== 200) {
this.logger.warn('[AbstractBinary.requestXml:non-200-status] url: %s, status: %s, headers: %j, xml: %j', url, status, headers, xml);
this.logger.warn(
'[AbstractBinary.requestXml:non-200-status] url: %s, status: %s, headers: %j, xml: %j',
url,
status,
headers,
xml
);
return '';
}
return xml;
}
protected async requestJSON(url: string) {
// oxlint-disable-next-line typescript-eslint/no-explicit-any
protected async requestJSON<T = any>(
url: string,
requestHeaders?: Record<string, string>
): Promise<T> {
const { status, data, headers } = await this.httpclient.request(url, {
timeout: 20000,
timeout: 30_000,
dataType: 'json',
followRedirect: true,
gzip: true,
headers: requestHeaders,
});
if (status !== 200) {
this.logger.warn('[AbstractBinary.requestJSON:non-200-status] url: %s, status: %s, headers: %j', url, status, headers);
return data;
this.logger.warn(
'[AbstractBinary.requestJSON:non-200-status] url: %s, status: %s, headers: %j',
url,
status,
headers
);
return data as T;
}
return data;
return data as T;
}
// https://nodejs.org/api/n-api.html#n_api_node_api_version_matrix
protected async listNodeABIVersions() {
const nodeABIVersions: number[] = [];
const versions = await this.requestJSON('https://nodejs.org/dist/index.json');
const versions = await this.requestJSON(
'https://nodejs.org/dist/index.json'
);
for (const version of versions) {
if (!version.modules) continue;
const modulesVersion = parseInt(version.modules);
// node v6.0.0 moduels 48 min
const modulesVersion = Number.parseInt(version.modules);
// node v6.0.0 modules 48 min
if (modulesVersion >= 48 && !nodeABIVersions.includes(modulesVersion)) {
nodeABIVersions.push(modulesVersion);
}
@@ -73,25 +112,31 @@ export abstract class AbstractBinary {
protected listNodePlatforms() {
// https://nodejs.org/api/os.html#osplatform
return [ 'darwin', 'linux', 'win32' ];
return platforms;
}
protected listNodeArchs() {
if (this.binaryConfig.options?.nodeArchs) return this.binaryConfig.options.nodeArchs;
protected listNodeArchs(binaryConfig?: BinaryTaskConfig) {
if (binaryConfig?.options?.nodeArchs) return binaryConfig.options.nodeArchs;
// https://nodejs.org/api/os.html#osarch
return {
linux: [ 'arm', 'arm64', 's390x', 'ia32', 'x64' ],
darwin: [ 'arm64', 'ia32', 'x64' ],
win32: [ 'ia32', 'x64' ],
linux: ['arm', 'arm64', 's390x', 'ia32', 'x64'],
darwin: ['arm64', 'ia32', 'x64'],
win32: ['ia32', 'x64'],
};
}
protected listNodeLibcs() {
protected listNodeLibcs(): Record<(typeof platforms)[number], string[]> {
// https://github.com/lovell/detect-libc/blob/master/lib/detect-libc.js#L42
return {
linux: [ 'glibc', 'musl' ],
darwin: [ 'unknown' ],
win32: [ 'unknown' ],
darwin: ['unknown'],
linux: ['glibc', 'musl'],
win32: ['unknown'],
};
}
}
export const BinaryAdapter: ImplDecorator<AbstractBinary, typeof BinaryType> =
QualifierImplDecoratorUtil.generatorDecorator(
AbstractBinary,
BINARY_ADAPTER_ATTRIBUTE
);

View File

@@ -1,19 +1,43 @@
import { EggContextHttpClient, EggLogger } from 'egg';
import { AbstractBinary, FetchResult, BinaryItem } from './AbstractBinary';
import { BinaryTaskConfig } from '../../../../config/binaries';
import { Inject, SingletonProto, EggAppConfig } from 'egg';
import { BinaryType } from '../../enum/Binary.ts';
import {
AbstractBinary,
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
@SingletonProto()
@BinaryAdapter(BinaryType.Api)
export class ApiBinary extends AbstractBinary {
private apiUrl: string;
constructor(httpclient: EggContextHttpClient, logger: EggLogger, binaryConfig: BinaryTaskConfig, apiUrl: string) {
super(httpclient, logger, binaryConfig);
this.apiUrl = apiUrl;
@Inject()
private readonly config: EggAppConfig;
async initFetch() {
// do nothing
return;
}
async fetch(dir: string): Promise<FetchResult | undefined> {
const url = `${this.apiUrl}/${this.binaryConfig.category}${dir}`;
async fetch(
dir: string,
binaryName: string,
lastData?: Record<string, unknown>
): Promise<FetchResult | undefined> {
const apiUrl =
this.config.cnpmcore.syncBinaryFromAPISource ||
`${this.config.cnpmcore.sourceRegistry}/-/binary`;
let url = `${apiUrl}/${binaryName}${dir}`;
if (lastData && lastData.lastSyncTime) {
url += `?since=${lastData.lastSyncTime}&limit=100`;
}
const data = await this.requestJSON(url);
if (!Array.isArray(data)) {
this.logger.warn('[ApiBinary.fetch:response-data-not-array] data: %j', data);
this.logger.warn(
'[ApiBinary.fetch:response-data-not-array] data: %j',
data
);
return;
}
const items: BinaryItem[] = [];
@@ -22,6 +46,7 @@ export class ApiBinary extends AbstractBinary {
name: item.name,
isDir: item.type === 'dir',
url: item.url,
// oxlint-disable-next-line unicorn/explicit-length-check
size: item.size || '-',
date: item.date,
});

View File

@@ -1,22 +1,51 @@
import path from 'path';
import { AbstractBinary, FetchResult, BinaryItem } from './AbstractBinary';
import path from 'node:path';
import { SingletonProto } from 'egg';
import binaries, {
type BinaryName,
type BinaryTaskConfig,
} from '../../../../config/binaries.ts';
import { BinaryType } from '../../enum/Binary.ts';
import {
AbstractBinary,
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
@SingletonProto()
@BinaryAdapter(BinaryType.Bucket)
export class BucketBinary extends AbstractBinary {
async fetch(dir: string): Promise<FetchResult | undefined> {
// /foo/ => foo/
const subDir = dir.substring(1);
const url = `${this.binaryConfig.distUrl}?delimiter=/&prefix=${encodeURIComponent(subDir)}`;
const xml = await this.requestXml(url);
return { items: this.parseItems(xml, dir), nextParams: null };
async initFetch() {
// do nothing
return;
}
protected parseItems(xml: string, dir: string) {
async fetch(
dir: string,
binaryName: BinaryName
): Promise<FetchResult | undefined> {
// /foo/ => foo/
const binaryConfig = binaries[binaryName];
const subDir = dir.slice(1);
const url = `${binaryConfig.distUrl}?delimiter=/&prefix=${encodeURIComponent(subDir)}`;
const xml = await this.requestXml(url);
return { items: this.parseItems(xml, dir, binaryConfig), nextParams: null };
}
protected parseItems(
xml: string,
dir: string,
binaryConfig: BinaryTaskConfig
): BinaryItem[] {
const items: BinaryItem[] = [];
// https://nwjs2.s3.amazonaws.com/?prefix=v0.59.0%2Fx64%2F
// https://chromedriver.storage.googleapis.com/?delimiter=/&prefix=
// <Contents><Key>2.0/chromedriver_linux32.zip</Key><Generation>1380149859530000</Generation><MetaGeneration>2</MetaGeneration><LastModified>2013-09-25T22:57:39.349Z</LastModified><ETag>"c0d96102715c4916b872f91f5bf9b12c"</ETag><Size>7262134</Size><Owner/></Contents><Contents>
// <Contents><Key>v0.59.0/nwjs-v0.59.0-linux-ia32.tar.gz</Key><LastModified>2015-11-02T02:34:18.000Z</LastModified><ETag>&quot;b1b7a52928e9f874bad0cabf7f74ba8e&quot;</ETag><Size>22842</Size><StorageClass>STANDARD</StorageClass></Contents>
const fileRe = /<Contents><Key>([^<]+?)<\/Key>(?:<Generation>\d+?<\/Generation>)?(?:<MetaGeneration>\d+?<\/MetaGeneration>)?<LastModified>([^<]+?)<\/LastModified><ETag>[^<]+?<\/ETag><Size>(\d+?)<\/Size>/g;
const fileRe =
/<Contents><Key>([^<]+?)<\/Key>(?:<Generation>\d+?<\/Generation>)?(?:<MetaGeneration>\d+?<\/MetaGeneration>)?<LastModified>([^<]+?)<\/LastModified><ETag>[^<]+?<\/ETag><Size>(\d+?)<\/Size>/g;
let matchs = xml.matchAll(fileRe);
for (const m of matchs) {
const fullname = m[1].trim();
@@ -31,17 +60,18 @@ export class BucketBinary extends AbstractBinary {
const name = path.basename(fullname);
const date = m[2].trim();
const size = parseInt(m[3].trim());
const size = Number.parseInt(m[3].trim());
items.push({
name,
isDir: false,
url: `${this.binaryConfig.distUrl}${fullname}`,
url: `${binaryConfig.distUrl}${fullname}`,
size,
date,
});
}
// <CommonPrefixes><Prefix>v0.59.0/x64/</Prefix></CommonPrefixes>
const dirRe = /<CommonPrefixes><Prefix>([^<]+?)<\/Prefix><\/CommonPrefixes>/g;
const dirRe =
/<CommonPrefixes><Prefix>([^<]+?)<\/Prefix><\/CommonPrefixes>/g;
matchs = xml.matchAll(dirRe);
for (const m of matchs) {
// <Prefix>AWSLogs/</Prefix>
@@ -50,11 +80,11 @@ export class BucketBinary extends AbstractBinary {
const fullname = m[1].trim();
const name = `${path.basename(fullname)}/`;
const fullpath = `${dir}${name}`;
if (this.binaryConfig.ignoreDirs?.includes(fullpath)) continue;
if (binaryConfig.ignoreDirs?.includes(fullpath)) continue;
let date = '-';
// root dir children, should set date to '2022-04-19T01:00:00Z', sync per hour
if (dir === '/') {
date = new Date().toISOString().split(':', 1)[0] + ':00:00Z';
date = `${new Date().toISOString().split(':', 1)[0]}:00:00Z`;
}
items.push({
name,

View File

@@ -0,0 +1,180 @@
import { basename } from 'node:path';
import { SingletonProto } from 'egg';
import { BinaryType } from '../../enum/Binary.ts';
import {
AbstractBinary,
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
@SingletonProto()
@BinaryAdapter(BinaryType.ChromeForTesting)
export class ChromeForTestingBinary extends AbstractBinary {
static lastTimestamp = '';
#timestamp = '';
private dirItems?: {
[key: string]: BinaryItem[];
};
async initFetch() {
this.dirItems = undefined;
}
async finishFetch(success: boolean) {
if (
success &&
this.#timestamp &&
ChromeForTestingBinary.lastTimestamp !== this.#timestamp
) {
ChromeForTestingBinary.lastTimestamp = this.#timestamp;
}
}
async #syncDirItems() {
this.dirItems = {};
this.dirItems['/'] = [];
const jsonApiEndpoint =
'https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json';
const { data, status, headers } = await this.httpclient.request(
jsonApiEndpoint,
{
dataType: 'json',
timeout: 30_000,
followRedirect: true,
gzip: true,
}
);
if (status !== 200) {
this.logger.warn(
'[ChromeForTestingBinary.request:non-200-status] url: %s, status: %s, headers: %j, data: %j',
jsonApiEndpoint,
status,
headers,
data
);
return;
}
this.#timestamp = data.timestamp;
const hasNewData = this.#timestamp !== ChromeForTestingBinary.lastTimestamp;
this.logger.info(
'[ChromeForTestingBinary] remote data timestamp: %j, last timestamp: %j, hasNewData: %s',
this.#timestamp,
ChromeForTestingBinary.lastTimestamp,
hasNewData
);
if (!hasNewData) {
return;
}
this.dirItems['/'].push({
name: 'known-good-versions-with-downloads.json',
date: data.timestamp,
size: '-',
isDir: false,
url: jsonApiEndpoint,
});
this.dirItems['/'].push({
name: 'latest-patch-versions-per-build.json',
date: data.timestamp,
size: '-',
isDir: false,
url: 'https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build.json',
});
this.dirItems['/'].push({
name: 'last-known-good-versions.json',
date: data.timestamp,
size: '-',
isDir: false,
url: 'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions.json',
});
// "timestamp": "2023-09-16T00:21:21.964Z",
// "versions": [
// {
// "version": "113.0.5672.0",
// "revision": "1121455",
// "downloads": {
// "chrome": [
// {
// "platform": "linux64",
// "url": "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/linux64/chrome-linux64.zip"
// },
// {
// "platform": "mac-arm64",
// "url": "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/mac-arm64/chrome-mac-arm64.zip"
// },
// {
// "platform": "mac-x64",
// "url": "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/mac-x64/chrome-mac-x64.zip"
// },
// {
// "platform": "win32",
// "url": "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/win32/chrome-win32.zip"
// },
// {
// "platform": "win64",
// "url": "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/win64/chrome-win64.zip"
// }
// ]
// }
// },
const versions = data.versions as {
version: string;
revision: string;
downloads: {
[key: string]: {
platform: string;
url: string;
}[];
};
}[];
for (const item of versions) {
this.dirItems['/'].push({
name: `${item.version}/`,
date: item.revision,
size: '-',
isDir: true,
url: '',
});
const versionDir = `/${item.version}/`;
if (!this.dirItems[versionDir]) {
this.dirItems[versionDir] = [];
}
for (const category in item.downloads) {
const downloads = item.downloads[category];
for (const download of downloads) {
const platformDir = `${versionDir}${download.platform}/`;
if (!this.dirItems[platformDir]) {
this.dirItems[platformDir] = [];
this.dirItems[versionDir].push({
name: `${download.platform}/`,
date: item.revision,
size: '-',
isDir: true,
url: '',
});
}
this.dirItems[platformDir].push({
name: basename(download.url),
date: data.timestamp,
size: '-',
isDir: false,
url: download.url,
});
}
}
}
}
async fetch(dir: string): Promise<FetchResult | undefined> {
// use https://github.com/GoogleChromeLabs/chrome-for-testing#json-api-endpoints
if (!this.dirItems) {
await this.#syncDirItems();
}
return { items: this.dirItems?.[dir] ?? [], nextParams: null };
}
}

View File

@@ -1,9 +1,23 @@
import { AbstractBinary, FetchResult, BinaryItem } from './AbstractBinary';
import { SingletonProto } from 'egg';
import { BinaryType } from '../../enum/Binary.ts';
import {
AbstractBinary,
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
@SingletonProto()
@BinaryAdapter(BinaryType.Cypress)
export class CypressBinary extends AbstractBinary {
private dirItems: {
private dirItems?: {
[key: string]: BinaryItem[];
};
} | null;
async initFetch() {
this.dirItems = undefined;
}
async fetch(dir: string): Promise<FetchResult | undefined> {
if (!this.dirItems) {
@@ -12,7 +26,7 @@ export class CypressBinary extends AbstractBinary {
this.dirItems = {};
this.dirItems['/'] = [];
for (const version in data.versions) {
const major = parseInt(version.split('.', 1)[0]);
const major = Number.parseInt(version.split('.', 1)[0]);
// need >= 4.0.0
// https://npmmirror.com/mirrors/cypress/4.0.0/
if (major < 4) continue;
@@ -31,10 +45,26 @@ export class CypressBinary extends AbstractBinary {
// "https://cdn.cypress.io/desktop/4.0.0/darwin-x64/cypress.zip"
// "https://cdn.cypress.io/desktop/4.0.0/linux-x64/cypress.zip"
// "https://cdn.cypress.io/desktop/4.0.0/win32-x64/cypress.zip"
// "https://cdn.cypress.io/desktop/9.2.0/darwin-arm64/cypress.zip"
// "https://cdn.cypress.io/desktop/9.2.0/darwin-x64/cypress.zip"
// "https://cdn.cypress.io/desktop/9.2.0/linux-x64/cypress.zip"
// "https://cdn.cypress.io/desktop/9.2.0/linux-arm64/cypress.zip"
// "https://cdn.cypress.io/desktop/9.2.0/win32-x64/cypress.zip"
const platforms = [ 'darwin-x64', 'linux-x64', 'win32-x64' ];
// https://github.com/cypress-io/cypress/blob/develop/scripts/binary/index.js#L146
// const systems = [
// { platform: 'linux', arch: 'x64' },
// { platform: 'linux', arch: 'arm64' },
// { platform: 'darwin', arch: 'x64' },
// { platform: 'darwin', arch: 'arm64' },
// { platform: 'win32', arch: 'x64' },
// ]
const platforms = [
'darwin-x64',
'darwin-arm64',
'linux-x64',
'linux-arm64',
'win32-x64',
];
for (const platform of platforms) {
this.dirItems[subDir].push({
name: `${platform}/`,

View File

@@ -0,0 +1,227 @@
import path from 'node:path';
import { SingletonProto } from 'egg';
import {
AbstractBinary,
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
import { BinaryType } from '../../enum/Binary.ts';
@SingletonProto()
@BinaryAdapter(BinaryType.Edgedriver)
export class EdgedriverBinary extends AbstractBinary {
private dirItems?: {
[key: string]: BinaryItem[];
};
async initFetch() {
this.dirItems = undefined;
}
async #syncDirItems() {
this.dirItems = {};
this.dirItems['/'] = [];
const jsonApiEndpoint = 'https://edgeupdates.microsoft.com/api/products';
const { data, status, headers } = await this.httpclient.request(
jsonApiEndpoint,
{
dataType: 'json',
timeout: 30_000,
followRedirect: true,
gzip: true,
}
);
if (status !== 200) {
this.logger.warn(
'[EdgedriverBinary.request:non-200-status] url: %s, status: %s, headers: %j, data: %j',
jsonApiEndpoint,
status,
headers,
data
);
return;
}
this.logger.info('[EdgedriverBinary] remote data length: %s', data.length);
// [
// {
// "Product": "Stable",
// "Releases": [
// {
// "ReleaseId": 73376,
// "Platform": "iOS",
// "Architecture": "arm64",
// "CVEs": [],
// "ProductVersion": "124.0.2478.89",
// "Artifacts": [],
// "PublishedTime": "2024-05-07T02:57:00",
// "ExpectedExpiryDate": "2025-05-07T02:57:00"
// },
// {
// "ReleaseId": 73629,
// "Platform": "Windows",
// "Architecture": "x86",
// "CVEs": [
// "CVE-2024-4559",
// "CVE-2024-4671"
// ],
// "ProductVersion": "124.0.2478.97",
// "Artifacts": [
// {
// "ArtifactName": "msi",
// "Location": "https://msedge.sf.dl.delivery.mp.microsoft.com/filestreamingservice/files/aa1c9fe3-bb9c-4a80-9ff7-5c109701fbfe/MicrosoftEdgeEnterpriseX86.msi",
// "Hash": "4CEF7B907D3E2371E953C41190E32C3560CEE7D3F16D7550CA156DC976EBCB80",
// "HashAlgorithm": "SHA256",
// "SizeInBytes": 162029568
// }
// ],
// "PublishedTime": "2024-05-11T06:47:00",
// "ExpectedExpiryDate": "2025-05-10T16:59:00"
// },
// {
// "ReleaseId": 73630,
// "Platform": "Linux",
// "Architecture": "x64",
// "CVEs": [
// "CVE-2024-4559"
// ],
// "ProductVersion": "124.0.2478.97",
// "Artifacts": [
// {
// "ArtifactName": "rpm",
// "Location": "https://packages.microsoft.com/yumrepos/edge/microsoft-edge-stable-124.0.2478.97-1.x86_64.rpm",
// "Hash": "32D9C333544DDD9C56FED54844E89EF00F3E5620942C07B9B68D214016687895",
// "HashAlgorithm": "SHA256",
// "SizeInBytes": 169877932
// },
// {
// "ArtifactName": "deb",
// "Location": "https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-stable/microsoft-edge-stable_124.0.2478.97-1_amd64.deb",
// "Hash": "85D0AD1D63847B3DD54F0F214D18A2B54462BB43291536E773AD1B8B29BBF799",
// "HashAlgorithm": "SHA256",
// "SizeInBytes": 167546042
// }
// ],
// "PublishedTime": "2024-05-10T17:01:00",
// "ExpectedExpiryDate": "2025-05-10T17:01:00"
// },
// {
// "Product": "EdgeUpdate",
// "Releases": [
// {
// "ReleaseId": 73493,
// "Platform": "Windows",
// "Architecture": "x86",
// "CVEs": [],
// "ProductVersion": "1.3.187.37",
// "Artifacts": [
// {
// "ArtifactName": "exe",
// "Location": "https://msedge.sf.dl.delivery.mp.microsoft.com/filestreamingservice/files/a2fa84fe-796b-4f80-b1cd-f4d1f5731aa8/MicrosoftEdgeUpdateSetup_X86_1.3.187.37.exe",
// "Hash": "503088D22461FEE5D7B6B011609D73FFD5869D3ACE1DBB0F00F8F3B9D122C514",
// "HashAlgorithm": "SHA256",
// "SizeInBytes": 1622072
// }
// ],
// "PublishedTime": "2024-05-08T05:44:00",
// "ExpectedExpiryDate": "2025-05-08T05:44:00"
// }
// ]
// }
const products = data as {
Product: string;
Releases: {
ReleaseId: number;
Platform: string;
Architecture: string;
CVEs: string[];
ProductVersion: string;
Artifacts: {
ArtifactName: string;
Location: string;
Hash: string;
HashAlgorithm: string;
SizeInBytes: string;
}[];
PublishedTime: string;
ExpectedExpiryDate: string;
}[];
}[];
const existsVersions = new Set<string>();
for (const product of products) {
if (product.Product === 'EdgeUpdate') continue;
for (const release of product.Releases) {
if (!release.Artifacts || release.Artifacts.length === 0) continue;
if (existsVersions.has(release.ProductVersion)) continue;
this.dirItems['/'].push({
name: `${release.ProductVersion}/`,
date: release.PublishedTime,
size: '-',
isDir: true,
url: '',
});
existsVersions.add(release.ProductVersion);
}
}
}
async fetch(dir: string): Promise<FetchResult | undefined> {
if (!this.dirItems) {
await this.#syncDirItems();
}
// fetch root dir
if (dir === '/') {
return { items: this.dirItems?.[dir] ?? [], nextParams: null };
}
// fetch sub dir
// /foo/ => foo/
const subDir = dir.slice(1);
// https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver?prefix=124.0.2478.97/&delimiter=/&maxresults=100&restype=container&comp=list
const url = `https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver?prefix=${encodeURIComponent(subDir)}&delimiter=/&maxresults=100&restype=container&comp=list`;
const xml = await this.requestXml(url);
return { items: this.#parseItems(xml), nextParams: null };
}
#parseItems(xml: string): BinaryItem[] {
const items: BinaryItem[] = [];
// <Blob><Name>124.0.2478.97/edgedriver_arm64.zip</Name><Url>https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver/124.0.2478.97/edgedriver_arm64.zip</Url><Properties><Last-Modified>Fri, 10 May 2024 18:35:44 GMT</Last-Modified><Etag>0x8DC712000713C13</Etag><Content-Length>9191362</Content-Length><Content-Type>application/octet-stream</Content-Type><Content-Encoding /><Content-Language /><Content-MD5>1tjPTf5JU6KKB06Qf1JOGw==</Content-MD5><Cache-Control /><BlobType>BlockBlob</BlobType><LeaseStatus>unlocked</LeaseStatus></Properties></Blob>
const fileRe =
/<Blob><Name>([^<]+?)<\/Name><Url>([^<]+?)<\/Url><Properties><Last-Modified>([^<]+?)<\/Last-Modified><Etag>(?:[^<]+?)<\/Etag><Content-Length>(\d+)<\/Content-Length>/g;
const matchItems = xml.matchAll(fileRe);
for (const m of matchItems) {
const fullname = m[1].trim();
// <Blob>
// <Name>124.0.2478.97/edgedriver_arm64.zip</Name>
// <Url>https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver/124.0.2478.97/edgedriver_arm64.zip</Url>
// <Properties>
// <Last-Modified>Fri, 10 May 2024 18:35:44 GMT</Last-Modified>
// <Etag>0x8DC712000713C13</Etag>
// <Content-Length>9191362</Content-Length>
// <Content-Type>application/octet-stream</Content-Type>
// <Content-Encoding/>
// <Content-Language/>
// <Content-MD5>1tjPTf5JU6KKB06Qf1JOGw==</Content-MD5>
// <Cache-Control/>
// <BlobType>BlockBlob</BlobType>
// <LeaseStatus>unlocked</LeaseStatus>
// </Properties>
// </Blob>
// ignore size = 0 dir
const name = path.basename(fullname);
const url = m[2].trim();
const date = m[3].trim();
const size = Number.parseInt(m[4].trim());
items.push({
name,
isDir: false,
url,
size,
date,
});
}
return items;
}
}

View File

@@ -1,9 +1,22 @@
import { BinaryItem, FetchResult } from './AbstractBinary';
import { GithubBinary } from './GithubBinary';
import { SingletonProto } from 'egg';
import binaries, { type BinaryName } from '../../../../config/binaries.ts';
import { BinaryType } from '../../enum/Binary.ts';
import { GithubBinary } from './GithubBinary.ts';
import {
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
@SingletonProto()
@BinaryAdapter(BinaryType.Electron)
export class ElectronBinary extends GithubBinary {
async fetch(dir: string): Promise<FetchResult | undefined> {
const releases = await this.initReleases();
async fetch(
dir: string,
binaryName: BinaryName = 'electron'
): Promise<FetchResult | undefined> {
const releases = await this.initReleases(binaryName, binaries.electron);
if (!releases) return;
let items: BinaryItem[] = [];
@@ -19,7 +32,7 @@ export class ElectronBinary extends GithubBinary {
// v14.2.6 => 14.2.6
if (/^v\d+?\./.test(item.tag_name)) {
items.push({
name: `${item.tag_name.substring(1)}/`,
name: `${item.tag_name.slice(1)}/`,
isDir: true,
url: item.url,
size: '-',
@@ -29,8 +42,11 @@ export class ElectronBinary extends GithubBinary {
}
} else {
for (const item of releases) {
if (dir === `/${item.tag_name}/` || dir === `/${item.tag_name.substring(1)}/`) {
items = this.formatItems(item);
if (
dir === `/${item.tag_name}/` ||
dir === `/${item.tag_name.slice(1)}/`
) {
items = this.formatItems(item, binaries.electron);
break;
}
}

View File

@@ -0,0 +1,145 @@
import { basename } from 'node:path';
import { SingletonProto } from 'egg';
import binaries, { type BinaryName } from '../../../../config/binaries.ts';
import { BinaryType } from '../../enum/Binary.ts';
import {
AbstractBinary,
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
@SingletonProto()
@BinaryAdapter(BinaryType.Firefox)
export class FirefoxBinary extends AbstractBinary {
async initFetch() {
// do nothing
return;
}
// Only fetch Firefox versions >= 100.0.0 to avoid too old versions
async fetch(
dir: string,
binaryName: BinaryName
): Promise<FetchResult | undefined> {
const binaryConfig = binaries[binaryName];
const url = `${binaryConfig.distUrl}${dir}`;
const html = await this.requestXml(url);
// Mozilla archive has format like:
// <tr>
// <td>Dir</td>
// <td><a href="/pub/firefox/releases/131.0.3/update/">update/</a></td>
// <td></td>
// <td></td>
// </tr>
// <tr>
// <td>File</td>
// <td><a href="/pub/firefox/releases/131.0.3/SHA256SUMS.asc">SHA256SUMS.asc</a></td>
// <td>833</td>
// <td>12-Apr-2025 08:52</td>
// </tr>
// Parse Mozilla directory listing format - handles two different formats:
// Format 1 (main index): <td><a href="/path/">name/</a></td>
// Format 2 (version dir): <td>Type</td><td><a href="/path/">name</a></td><td>size</td><td>date</td>
// Try the detailed format first (with Type/Size/Date columns)
const detailedRe = /<tr>\s*<td>(Dir|File)<\/td>\s*<td><a href="([^"]+?)"[^>]*?>[^<]+?<\/a><\/td>\s*<td>([^<]*?)<\/td>\s*<td>([^<]*?)<\/td>\s*<\/tr>/gi;
const detailedMatches = Array.from(html.matchAll(detailedRe));
let matchs: RegExpMatchArray[];
let useDetailedFormat = false;
if (detailedMatches.length > 0) {
// Use detailed format
matchs = detailedMatches;
useDetailedFormat = true;
} else {
// Fallback to simple format
const simpleRe = /<td><a href="([^"]+?)"[^>]*?>[^<]+?<\/a><\/td>/gi;
matchs = Array.from(html.matchAll(simpleRe));
}
const items: BinaryItem[] = [];
for (const m of matchs) {
let href: string;
let isDir: boolean;
let size: string;
let date: string;
if (useDetailedFormat) {
// Detailed format: [fullMatch, type, href, size, date]
const type = m[1]; // "Dir" or "File"
href = m[2];
size = m[3].trim() || '-';
date = m[4].trim() || '-';
isDir = type === 'Dir';
} else {
// Simple format: [fullMatch, href]
href = m[1];
isDir = href.endsWith('/');
size = '-';
date = '-';
}
// Extract the name from the href path
// href could be "/pub/firefox/releases/130.0/" or just "130.0/"
let name = href;
if (href.startsWith('/')) {
// Extract the last part of the path
const parts = href.split('/').filter(Boolean);
name = parts[parts.length - 1] ?? '';
if (href.endsWith('/')) {
name += '/';
}
}
if (!isDir) {
// Keep the full name for files
name = basename(name);
}
// Skip parent directory links
if (name === '../' || href === '/pub/firefox/' || href.endsWith('/..') || href === '/pub/firefox/releases/') continue;
// Filter out old Firefox versions (< 100.0.0) for directories - apply to main index (root directory)
if (isDir && name !== '../' && dir === '/') {
const versionName = name.slice(0, -1); // Remove trailing '/'
// Skip non-version directories that are just special names
if (/^\d+\.\d+/.test(versionName)) {
try {
const major = Number.parseInt(versionName.split('.')[0]);
if (major < 100) {
continue; // Skip versions < 100.0.0
}
} catch {
// If version parsing fails, skip this directory
continue;
}
}
// Also skip named directories that aren't version numbers
else if (!['latest', 'latest-beta', 'latest-esr'].includes(versionName)) {
continue;
}
}
const fileUrl = isDir ? '' : `${url}${name}`;
if (binaryConfig.ignoreFiles?.includes(`${dir}${name}`)) continue;
const item = {
name,
isDir,
url: fileUrl,
size,
date,
ignoreDownloadStatuses: binaryConfig.options?.ignoreDownloadStatuses,
};
items.push(item);
}
return { items, nextParams: null };
}
}

View File

@@ -1,18 +1,35 @@
import { AbstractBinary, FetchResult, BinaryItem } from './AbstractBinary';
import { SingletonProto } from 'egg';
import binaries, { type BinaryName, type BinaryTaskConfig } from '../../../../config/binaries.ts';
import { BinaryType } from '../../enum/Binary.ts';
import { AbstractBinary, BinaryAdapter, type BinaryItem, type FetchResult } from './AbstractBinary.ts';
@SingletonProto()
@BinaryAdapter(BinaryType.GitHub)
export class GithubBinary extends AbstractBinary {
private releases?: any[];
// oxlint-disable-next-line typescript-eslint/no-explicit-any
private releases: Record<string, any[] | undefined> = {};
protected async initReleases() {
if (!this.releases) {
async initFetch(binaryName: BinaryName) {
this.releases[binaryName] = undefined;
}
protected async initReleases(binaryName: BinaryName, binaryConfig: BinaryTaskConfig) {
if (!this.releases[binaryName]) {
// https://docs.github.com/en/rest/reference/releases get three pages
// https://api.github.com/repos/electron/electron/releases
// https://api.github.com/repos/electron/electron/releases?per_page=100&page=3
// oxlint-disable-next-line typescript-eslint/no-explicit-any
let releases: any[] = [];
const maxPage = this.binaryConfig.options?.maxPage || 1;
const maxPage = binaryConfig.options?.maxPage || 1;
const perPage = binaryConfig.options?.perPage || 100;
for (let i = 0; i < maxPage; i++) {
const url = `https://api.github.com/repos/${this.binaryConfig.repo}/releases?per_page=100&page=${i + 1}`;
const data = await this.requestJSON(url);
const url = `https://api.github.com/repos/${binaryConfig.repo}/releases?per_page=${perPage}&page=${i + 1}`;
const requestHeaders: Record<string, string> = {};
if (process.env.GITHUB_TOKEN) {
requestHeaders.Authorization = `token ${process.env.GITHUB_TOKEN}`;
}
const data = await this.requestJSON(url, requestHeaders);
if (!Array.isArray(data)) {
// {"message":"API rate limit exceeded for 47.57.239.54. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)","documentation_url":"https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting"}
if (typeof data?.message === 'string' && data.message.includes('rate limit')) {
@@ -24,17 +41,24 @@ export class GithubBinary extends AbstractBinary {
}
releases = releases.concat(data);
}
this.releases = releases;
this.releases[binaryName] = releases;
}
return this.releases;
return this.releases[binaryName];
}
protected formatItems(releaseItem: any) {
// oxlint-disable-next-line typescript-eslint/no-explicit-any
protected formatItems(releaseItem: any, binaryConfig: BinaryTaskConfig) {
const items: BinaryItem[] = [];
// 200MB
const maxFileSize = 1024 * 1024 * 200;
// 250MB
const maxFileSize = 1024 * 1024 * 250;
for (const asset of releaseItem.assets) {
if (asset.size > maxFileSize) continue;
if (asset.size > maxFileSize) {
this.logger.info(
'[GithubBinary.formatItems] asset reach max file size(> 250MB), ignore download it, asset: %j',
asset
);
continue;
}
items.push({
name: asset.name,
isDir: false,
@@ -50,7 +74,7 @@ export class GithubBinary extends AbstractBinary {
items.push({
name: `${releaseItem.tag_name}.tar.gz`,
isDir: false,
url: `https://github.com/${this.binaryConfig.repo}/archive/${releaseItem.tag_name}.tar.gz`,
url: `https://github.com/${binaryConfig.repo}/archive/${releaseItem.tag_name}.tar.gz`,
size: '-',
date: releaseItem.published_at,
});
@@ -59,7 +83,7 @@ export class GithubBinary extends AbstractBinary {
items.push({
name: `${releaseItem.tag_name}.zip`,
isDir: false,
url: `https://github.com/${this.binaryConfig.repo}/archive/${releaseItem.tag_name}.zip`,
url: `https://github.com/${binaryConfig.repo}/archive/${releaseItem.tag_name}.zip`,
size: '-',
date: releaseItem.published_at,
});
@@ -67,8 +91,9 @@ export class GithubBinary extends AbstractBinary {
return items;
}
async fetch(dir: string): Promise<FetchResult | undefined> {
const releases = await this.initReleases();
async fetch(dir: string, binaryName: BinaryName): Promise<FetchResult | undefined> {
const binaryConfig = binaries[binaryName];
const releases = await this.initReleases(binaryName, binaryConfig);
if (!releases) return;
let items: BinaryItem[] = [];
@@ -85,7 +110,7 @@ export class GithubBinary extends AbstractBinary {
} else {
for (const item of releases) {
if (dir === `/${item.tag_name}/`) {
items = this.formatItems(item);
items = this.formatItems(item, binaryConfig);
break;
}
}

View File

@@ -1,94 +1,110 @@
import { AbstractBinary, FetchResult, BinaryItem } from './AbstractBinary';
import { SingletonProto } from 'egg';
import binaries, { type BinaryName } from '../../../../config/binaries.ts';
import { BinaryType } from '../../enum/Binary.ts';
import {
AbstractBinary,
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
@SingletonProto()
@BinaryAdapter(BinaryType.Imagemin)
export class ImageminBinary extends AbstractBinary {
private dirItems: {
[key: string]: BinaryItem[];
};
async initFetch() {
// do nothing
return;
}
async fetch(dir: string): Promise<FetchResult | undefined> {
if (!this.dirItems) {
this.dirItems = {};
const npmPackageName = this.binaryConfig.options?.npmPackageName ?? this.binaryConfig.category;
const pkgUrl = `https://registry.npmjs.com/${npmPackageName}`;
const data = await this.requestJSON(pkgUrl);
this.dirItems = {};
this.dirItems['/'] = [];
// mini version 4.0.0
// https://github.com/imagemin/jpegtran-bin/blob/v4.0.0/lib/index.js
// https://github.com/imagemin/pngquant-bin/blob/v4.0.0/lib/index.js
for (const version in data.versions) {
const major = parseInt(version.split('.', 1)[0]);
if (major < 4) continue;
// >= 4.0.0
const date = data.time[version];
// https://raw.githubusercontent.com/imagemin/jpegtran-bin/v${pkg.version}/vendor/`
this.dirItems['/'].push({
name: `v${version}/`,
async fetch(
dir: string,
binaryName: BinaryName
): Promise<FetchResult | undefined> {
const binaryConfig = binaries[binaryName];
const dirItems: {
[key: string]: BinaryItem[];
} = {};
const npmPackageName = binaryConfig.options?.npmPackageName ?? binaryName;
const pkgUrl = `https://registry.npmjs.com/${npmPackageName}`;
const data = await this.requestJSON(pkgUrl);
dirItems['/'] = [];
// mini version 4.0.0
// https://github.com/imagemin/jpegtran-bin/blob/v4.0.0/lib/index.js
// https://github.com/imagemin/pngquant-bin/blob/v4.0.0/lib/index.js
for (const version in data.versions) {
const major = Number.parseInt(version.split('.', 1)[0]);
if (major < 4) continue;
// >= 4.0.0
const date = data.time[version];
// https://raw.githubusercontent.com/imagemin/jpegtran-bin/v${pkg.version}/vendor/`
dirItems['/'].push({
name: `v${version}/`,
date,
size: '-',
isDir: true,
url: '',
});
const versionDir = `/v${version}/`;
dirItems[versionDir] = [];
dirItems[versionDir].push({
name: 'vendor/',
date,
size: '-',
isDir: true,
url: '',
});
const versionVendorDir = `/v${version}/vendor/`;
dirItems[versionVendorDir] = [];
for (const platform of binaryConfig.options?.nodePlatforms ?? []) {
dirItems[versionVendorDir].push({
name: `${platform}/`,
date,
size: '-',
isDir: true,
url: '',
});
const versionDir = `/v${version}/`;
this.dirItems[versionDir] = [];
this.dirItems[versionDir].push({
name: 'vendor/',
date,
size: '-',
isDir: true,
url: '',
});
const versionVendorDir = `/v${version}/vendor/`;
this.dirItems[versionVendorDir] = [];
for (const platform of this.binaryConfig.options!.nodePlatforms!) {
this.dirItems[versionVendorDir].push({
name: `${platform}/`,
date,
size: '-',
isDir: true,
url: '',
});
const platformDir = `/v${version}/vendor/${platform}/`;
this.dirItems[platformDir] = [];
const archs = this.binaryConfig.options!.nodeArchs![platform];
if (archs.length === 0) {
for (const name of this.binaryConfig.options!.binFiles![platform]) {
this.dirItems[platformDir].push({
const platformDir = `/v${version}/vendor/${platform}/`;
dirItems[platformDir] = [];
const archs = binaryConfig.options?.nodeArchs?.[platform] ?? [];
if (archs.length === 0) {
for (const name of binaryConfig.options?.binFiles?.[platform] ?? []) {
dirItems[platformDir].push({
name,
date,
size: '-',
isDir: false,
url: `${binaryConfig.distUrl}/${binaryConfig.repo}${platformDir}${name}`,
ignoreDownloadStatuses: [404],
});
}
} else {
for (const arch of archs) {
dirItems[platformDir].push({
name: `${arch}/`,
date,
size: '-',
isDir: true,
url: '',
});
const platformArchDir = `/v${version}/vendor/${platform}/${arch}/`;
dirItems[platformArchDir] = [];
for (const name of binaryConfig.options?.binFiles?.[platform] ??
[]) {
dirItems[platformArchDir].push({
name,
date,
size: '-',
isDir: false,
url: `${this.binaryConfig.distUrl}/${this.binaryConfig.repo}${platformDir}${name}`,
ignoreDownloadStatuses: [ 404 ],
url: `${binaryConfig.distUrl}/${binaryConfig.repo}${platformArchDir}${name}`,
ignoreDownloadStatuses: [404],
});
}
} else {
for (const arch of archs) {
this.dirItems[platformDir].push({
name: `${arch}/`,
date,
size: '-',
isDir: true,
url: '',
});
const platformArchDir = `/v${version}/vendor/${platform}/${arch}/`;
this.dirItems[platformArchDir] = [];
for (const name of this.binaryConfig.options!.binFiles![platform]) {
this.dirItems[platformArchDir].push({
name,
date,
size: '-',
isDir: false,
url: `${this.binaryConfig.distUrl}/${this.binaryConfig.repo}${platformArchDir}${name}`,
ignoreDownloadStatuses: [ 404 ],
});
}
}
}
}
}
}
return { items: this.dirItems[dir] };
return { items: dirItems[dir] };
}
}

View File

@@ -1,34 +1,108 @@
import { AbstractBinary, FetchResult, BinaryItem } from './AbstractBinary';
import { basename } from 'node:path';
import { SingletonProto } from 'egg';
import dayjs from 'dayjs';
import binaries, { type BinaryName } from '../../../../config/binaries.ts';
import { BinaryType } from '../../enum/Binary.ts';
import {
AbstractBinary,
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
@SingletonProto()
@BinaryAdapter(BinaryType.Node)
export class NodeBinary extends AbstractBinary {
async fetch(dir: string): Promise<FetchResult | undefined> {
const url = `${this.binaryConfig.distUrl}${dir}`;
async initFetch() {
// do nothing
return;
}
async fetch(
dir: string,
binaryName: BinaryName
): Promise<FetchResult | undefined> {
const binaryConfig = binaries[binaryName];
const url = `${binaryConfig.distUrl}${dir}`;
const html = await this.requestXml(url);
// <a href="v9.8.0/">v9.8.0/</a> 08-Mar-2018 01:55 -
// <a href="v9.9.0/">v9.9.0/</a> 21-Mar-2018 15:47 -
// <a href="index.json">index.json</a> 17-Dec-2021 23:16 219862
// <a href="index.tab">index.tab</a> 17-Dec-2021 23:16 136319
// <a href="node-0.0.1.tar.gz">node-0.0.1.tar.gz</a> 26-Aug-2011 16:22 2846972
// <a href="node-v14.0.0-nightly20200119b318926634-linux-armv7l.tar.xz">node-v14.0.0-nightly20200119b318926634-linux-ar..&gt;</a> 19-Jan-2020 06:07 18565976
const re = /<a href="([^\"]+?)"[^>]*?>[^<]+?<\/a>\s+?([\w\-]+? \w{2}\:\d{2})\s+?(\d+|\-)/ig;
// new html format
// <a href="docs/">docs/</a> - -
// <a href="win-x64/">win-x64/</a> - -
// <a href="win-x86/">win-x86/</a> - -
// <a href="/dist/v18.15.0/SHASUMS256.txt.asc">SHASUMS256.txt.asc</a> 04-Nov-2024 17:29 3.7 KB
// <a href="/dist/v18.15.0/SHASUMS256.txt.sig">SHASUMS256.txt.sig</a> 04-Nov-2024 17:29 310 B
// <a href="/dist/v18.15.0/SHASUMS256.txt">SHASUMS256.txt</a> 04-Nov-2024 17:29 3.2 KB
// <a href="/dist/latest-v20.x/SHASUMS256.txt.asc">SHASUMS256.txt.asc</a> 03 Sept 2025, 18:20 4.7 KB
// <a href="/dist/latest-v20.x/SHASUMS256.txt.sig">SHASUMS256.txt.sig</a> 03 Sept 2025, 18:20 566 B
// <a href="/dist/latest-v20.x/SHASUMS256.txt">SHASUMS256.txt</a> 03 Sept 2025, 18:19 3.8 KB
// <a href="/dist/latest-v20.x/node-v20.19.5-aix-ppc64.tar.gz">node-v20.19.5-aix-ppc64.tar.gz</a> 03 Sept 2025, 18:19 60 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-arm64.msi">node-v20.19.5-arm64.msi</a> 03 Sept 2025, 18:19 24 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-darwin-arm64.tar.gz">node-v20.19.5-darwin-arm64.tar.gz</a> 03 Sept 2025, 18:19 41 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-darwin-arm64.tar.xz">node-v20.19.5-darwin-arm64.tar.xz</a> 03 Sept 2025, 18:19 21 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-darwin-x64.tar.gz">node-v20.19.5-darwin-x64.tar.gz</a> 03 Sept 2025, 18:19 43 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-darwin-x64.tar.xz">node-v20.19.5-darwin-x64.tar.xz</a> 03 Sept 2025, 18:19 23 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-headers.tar.gz">node-v20.19.5-headers.tar.gz</a> 03 Sept 2025, 18:19 8.7 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-headers.tar.xz">node-v20.19.5-headers.tar.xz</a> 03 Sept 2025, 18:19 524 KB
// <a href="/dist/latest-v20.x/node-v20.19.5-linux-arm64.tar.gz">node-v20.19.5-linux-arm64.tar.gz</a> 03 Sept 2025, 18:19 47 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-linux-arm64.tar.xz">node-v20.19.5-linux-arm64.tar.xz</a> 03 Sept 2025, 18:19 25 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-linux-armv7l.tar.gz">node-v20.19.5-linux-armv7l.tar.gz</a> 03 Sept 2025, 18:19 43 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-linux-armv7l.tar.xz">node-v20.19.5-linux-armv7l.tar.xz</a> 03 Sept 2025, 18:19 22 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-linux-ppc64le.tar.gz">node-v20.19.5-linux-ppc64le.tar.gz</a> 03 Sept 2025, 18:19 49 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-linux-ppc64le.tar.xz">node-v20.19.5-linux-ppc64le.tar.xz</a> 03 Sept 2025, 18:19 26 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-linux-s390x.tar.gz">node-v20.19.5-linux-s390x.tar.gz</a> 03 Sept 2025, 18:19 47 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-linux-s390x.tar.xz">node-v20.19.5-linux-s390x.tar.xz</a> 03 Sept 2025, 18:19 25 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-linux-x64.tar.gz">node-v20.19.5-linux-x64.tar.gz</a> 03 Sept 2025, 18:19 47 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-linux-x64.tar.xz">node-v20.19.5-linux-x64.tar.xz</a> 03 Sept 2025, 18:19 26 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-win-arm64.7z">node-v20.19.5-win-arm64.7z</a> 03 Sept 2025, 18:19 17 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-win-arm64.zip">node-v20.19.5-win-arm64.zip</a> 03 Sept 2025, 18:19 26 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-win-x64.7z">node-v20.19.5-win-x64.7z</a> 03 Sept 2025, 18:19 19 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-win-x64.zip">node-v20.19.5-win-x64.zip</a> 03 Sept 2025, 18:19 30 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-win-x86.7z">node-v20.19.5-win-x86.7z</a> 03 Sept 2025, 18:19 18 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-win-x86.zip">node-v20.19.5-win-x86.zip</a> 03 Sept 2025, 18:19 28 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-x64.msi">node-v20.19.5-x64.msi</a> 03 Sept 2025, 18:19 27 MB
// <a href="/dist/latest-v20.x/node-v20.19.5-x86.msi">node-v20.19.5-x86.msi</a> 03 Sept 2025, 18:19 25 MB
// <a href="/dist/latest-v20.x/node-v20.19.5.pkg">node-v20.19.5.pkg</a> 03 Sept 2025, 18:19 72 MB
// <a href="/dist/latest-v20.x/node-v20.19.5.tar.gz">node-v20.19.5.tar.gz</a> 03 Sept 2025, 18:19 89 MB
// <a href="/dist/latest-v20.x/node-v20.19.5.tar.xz">node-v20.19.5.tar.xz</a> 03 Sept 2025, 18:19 43 MB
// date format: 19-Jan-2020 06:07 or 03 Sept 2025, 18:19
const re =
/<a href="([^"]+?)"[^>]*?>[^<]+?<\/a>\s+?((?:[\w-]+? \w{2}:\d{2})|(?:\d{2} [A-Za-z]{3,9} \d{4}, \d{2}:\d{2})|-)\s+?([\d.\-\s\w]+)/gi;
const matchs = html.matchAll(re);
const items: BinaryItem[] = [];
for (const m of matchs) {
const name = m[1];
let name = m[1];
const isDir = name.endsWith('/');
if (!isDir) {
// /dist/v18.15.0/SHASUMS256.txt => SHASUMS256.txt
name = basename(name);
}
const fileUrl = isDir ? '' : `${url}${name}`;
const date = m[2];
const size = m[3];
const date = m[2] === '-' ? '-' : dayjs(m[2]).format('DD-MMM-YYYY HH:mm');
const size = m[3].trim();
if (size === '0') continue;
if (this.binaryConfig.ignoreFiles?.includes(`${dir}${name}`)) continue;
if (binaryConfig.ignoreFiles?.includes(`${dir}${name}`)) continue;
items.push({
const item = {
name,
isDir,
url: fileUrl,
size,
date,
});
ignoreDownloadStatuses: binaryConfig.options?.ignoreDownloadStatuses,
};
items.push(item);
}
return { items, nextParams: null };
}

View File

@@ -1,186 +1,266 @@
import { join } from 'path';
import { AbstractBinary, FetchResult, BinaryItem } from './AbstractBinary';
import { join } from 'node:path';
import { SingletonProto } from 'egg';
import binaries, { type BinaryName } from '../../../../config/binaries.ts';
import { BinaryType } from '../../enum/Binary.ts';
import {
AbstractBinary,
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
@SingletonProto()
@BinaryAdapter(BinaryType.NodePreGyp)
export class NodePreGypBinary extends AbstractBinary {
private dirItems: {
[key: string]: BinaryItem[];
};
async initFetch() {
// do nothing
return;
}
// https://github.com/mapbox/node-pre-gyp
async fetch(dir: string): Promise<FetchResult | undefined> {
if (!this.dirItems) {
this.dirItems = {};
const pkgUrl = `https://registry.npmjs.com/${this.binaryConfig.category}`;
const data = await this.requestJSON(pkgUrl);
this.dirItems = {};
this.dirItems['/'] = [];
const nodeABIVersions = await this.listNodeABIVersions();
const nodePlatforms = this.listNodePlatforms();
const nodeArchs = this.listNodeArchs();
const nodeLibcs = this.listNodeLibcs();
for (const version in data.versions) {
const date = data.time[version];
const pkgVersion = data.versions[version];
if (!pkgVersion.binary) continue;
// https://github.com/mapbox/node-pre-gyp#package_name
// defaults to {module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz
let binaryFile = pkgVersion.binary.package_name
|| '{module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz';
if (!binaryFile) continue;
const moduleName = pkgVersion.binary.module_name || pkgVersion.name;
binaryFile = binaryFile.replace('{version}', version)
.replace('{module_name}', moduleName);
async fetch(
dir: string,
binaryName: BinaryName
): Promise<FetchResult | undefined> {
const binaryConfig = binaries[binaryName];
const npmPackageName = binaryConfig.options?.npmPackageName ?? binaryName;
const pkgUrl = `https://registry.npmjs.com/${npmPackageName}`;
const data = await this.requestJSON(pkgUrl);
const dirItems: {
[key: string]: BinaryItem[];
} = {
'/': [],
};
const nodeABIVersions = await this.listNodeABIVersions();
const nodePlatforms = this.listNodePlatforms();
const nodeArchs = this.listNodeArchs(binaryConfig);
const nodeLibcs = this.listNodeLibcs();
for (const version in data.versions) {
const date = data.time[version];
const pkgVersion = data.versions[version];
if (!pkgVersion.binary) continue;
// https://github.com/mapbox/node-pre-gyp#package_name
// defaults to {module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz
let binaryFile =
pkgVersion.binary.package_name ||
'{module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz';
if (!binaryFile) continue;
const moduleName = pkgVersion.binary.module_name || pkgVersion.name;
binaryFile = binaryFile
.replace('{version}', version)
.replace('{module_name}', moduleName);
let currentDir = this.dirItems['/'];
let versionPrefix = '';
let remotePath = pkgVersion.binary.remote_path;
const napiVersions = pkgVersion.binary.napi_versions ?? [];
if (this.binaryConfig.options?.requiredNapiVersions && napiVersions.length === 0) continue;
if (remotePath?.includes('{version}')) {
const dirName = remotePath.includes('v{version}') ? `v${version}` : version;
versionPrefix = `/${dirName}`;
this.dirItems['/'].push({
name: `${dirName}/`,
date,
size: '-',
isDir: true,
url: '',
});
currentDir = this.dirItems[`/${dirName}/`] = [];
}
let currentDir = dirItems['/'];
let versionPrefix = '';
let remotePath = pkgVersion.binary.remote_path;
const napiVersions = pkgVersion.binary.napi_versions ?? [];
if (
binaryConfig.options?.requiredNapiVersions &&
napiVersions.length === 0
)
continue;
if (remotePath?.includes('{version}')) {
const dirName = remotePath.includes('v{version}')
? `v${version}`
: version;
versionPrefix = `/${dirName}`;
dirItems['/'].push({
name: `${dirName}/`,
date,
size: '-',
isDir: true,
url: '',
});
currentDir = [];
dirItems[`/${dirName}/`] = currentDir;
}
// https://node-precompiled-binaries.grpc.io/?delimiter=/&prefix=grpc/v1.24.11/
// https://github.com/grpc/grpc-node/blob/grpc%401.24.x/packages/grpc-native-core/package.json#L50
// "binary": {
// "module_name": "grpc_node",
// "module_path": "src/node/extension_binary/{node_abi}-{platform}-{arch}-{libc}",
// "host": "https://node-precompiled-binaries.grpc.io/",
// "remote_path": "{name}/v{version}",
// "package_name": "{node_abi}-{platform}-{arch}-{libc}.tar.gz"
// },
if (binaryFile.includes('{node_abi}')
&& binaryFile.includes('{platform}')
&& binaryFile.includes('{arch}')
&& binaryFile.includes('{libc}')) {
for (const nodeAbi of nodeABIVersions) {
for (const platform of nodePlatforms) {
const archs = nodeArchs[platform];
const libcs = nodeLibcs[platform];
for (const arch of archs) {
for (const libc of libcs) {
const name = binaryFile.replace('{node_abi}', `node-v${nodeAbi}`)
.replace('{platform}', platform)
.replace('{arch}', arch)
.replace('{libc}', libc);
currentDir.push({
name,
date,
size: '-',
isDir: false,
url: `${this.binaryConfig.distUrl}/${this.binaryConfig.category}${versionPrefix}/${name}`,
ignoreDownloadStatuses: [ 404 ],
});
}
}
}
}
} else if (binaryFile.includes('{node_abi}')
&& binaryFile.includes('{platform}')
&& binaryFile.includes('{arch}')) {
for (const nodeAbi of nodeABIVersions) {
for (const platform of nodePlatforms) {
const archs = nodeArchs[platform];
for (const arch of archs) {
const name = binaryFile.replace('{node_abi}', `node-v${nodeAbi}`)
.replace('{platform}', platform)
.replace('{arch}', arch);
currentDir.push({
name,
date,
size: '-',
isDir: false,
url: `${this.binaryConfig.distUrl}/${this.binaryConfig.category}${versionPrefix}/${name}`,
ignoreDownloadStatuses: [ 404 ],
});
}
}
}
} else if (binaryFile.includes('{platform}-{arch}-{node_napi_label}-{libc}') && napiVersions.length > 0) {
// https://skia-canvas.s3.us-east-1.amazonaws.com/v0.9.30/darwin-arm64-napi-v6-unknown.tar.gz
// https://github.com/samizdatco/skia-canvas/blob/2a75801d7cce3b4e4e6ad015a173daefaa8465e6/package.json#L48
// "binary": {
// "module_name": "index",
// "module_path": "./lib/v{napi_build_version}",
// "remote_path": "./v{version}",
// "package_name": "{platform}-{arch}-{node_napi_label}-{libc}.tar.gz",
// "host": "https://skia-canvas.s3.us-east-1.amazonaws.com",
// "napi_versions": [
// 6
// ]
// },
// https://node-precompiled-binaries.grpc.io/?delimiter=/&prefix=grpc/v1.24.11/
// https://github.com/grpc/grpc-node/blob/grpc%401.24.x/packages/grpc-native-core/package.json#L50
// "binary": {
// "module_name": "grpc_node",
// "module_path": "src/node/extension_binary/{node_abi}-{platform}-{arch}-{libc}",
// "host": "https://node-precompiled-binaries.grpc.io/",
// "remote_path": "{name}/v{version}",
// "package_name": "{node_abi}-{platform}-{arch}-{libc}.tar.gz"
// },
if (
binaryFile.includes('{node_abi}') &&
binaryFile.includes('{platform}') &&
binaryFile.includes('{arch}') &&
binaryFile.includes('{libc}')
) {
for (const nodeAbi of nodeABIVersions) {
for (const platform of nodePlatforms) {
const archs = nodeArchs[platform];
const libcs = nodeLibcs[platform];
for (const arch of archs) {
for (const libc of libcs) {
for (const napiVersion of napiVersions) {
const name = binaryFile.replace('{platform}', platform)
.replace('{arch}', arch)
.replace('{node_napi_label}', `napi-v${napiVersion}`)
.replace('{libc}', libc);
currentDir.push({
name,
date,
size: '-',
isDir: false,
url: `${this.binaryConfig.distUrl}${versionPrefix}/${name}`,
ignoreDownloadStatuses: [ 404, 403 ],
});
}
const name = binaryFile
.replace('{node_abi}', `node-v${nodeAbi}`)
.replace('{platform}', platform)
.replace('{arch}', arch)
.replace('{libc}', libc);
currentDir.push({
name,
date,
size: '-',
isDir: false,
url: `${binaryConfig.distUrl}/${binaryName}${versionPrefix}/${name}`,
ignoreDownloadStatuses: [404],
});
}
}
}
} else if (binaryFile.includes('{platform}') && binaryFile.includes('{arch}')) {
// https://github.com/grpc/grpc-node/blob/master/packages/grpc-tools/package.json#L29
// "binary": {
// "module_name": "grpc_tools",
// "host": "https://node-precompiled-binaries.grpc.io/",
// "remote_path": "{name}/v{version}",
// "package_name": "{platform}-{arch}.tar.gz",
// "module_path": "bin"
// },
// handle {configuration}
// "binary": {
// "module_name": "wrtc",
// "module_path": "./build/{configuration}/",
// "remote_path": "./{module_name}/v{version}/{configuration}/",
// "package_name": "{platform}-{arch}.tar.gz",
// "host": "https://node-webrtc.s3.amazonaws.com"
// },
}
} else if (
binaryFile.includes('{node_abi}') &&
binaryFile.includes('{platform}') &&
binaryFile.includes('{arch}')
) {
for (const nodeAbi of nodeABIVersions) {
for (const platform of nodePlatforms) {
const archs = nodeArchs[platform];
for (const arch of archs) {
const binaryFileName = binaryFile.replace('{platform}', platform)
const name = binaryFile
.replace('{node_abi}', `node-v${nodeAbi}`)
.replace('{platform}', platform)
.replace('{arch}', arch);
remotePath = remotePath.replace('{module_name}', moduleName)
.replace('{name}', this.binaryConfig.category)
currentDir.push({
name,
date,
size: '-',
isDir: false,
url: `${binaryConfig.distUrl}/${binaryName}${versionPrefix}/${name}`,
ignoreDownloadStatuses: [404],
});
}
}
}
} else if (
binaryFile.includes('{platform}-{arch}-{node_napi_label}-{libc}') &&
napiVersions.length > 0
) {
// https://skia-canvas.s3.us-east-1.amazonaws.com/v0.9.30/darwin-arm64-napi-v6-unknown.tar.gz
// https://github.com/samizdatco/skia-canvas/blob/2a75801d7cce3b4e4e6ad015a173daefaa8465e6/package.json#L48
// "binary": {
// "module_name": "index",
// "module_path": "./lib/v{napi_build_version}",
// "remote_path": "./v{version}",
// "package_name": "{platform}-{arch}-{node_napi_label}-{libc}.tar.gz",
// "host": "https://skia-canvas.s3.us-east-1.amazonaws.com",
// "napi_versions": [
// 6
// ]
// },
for (const platform of nodePlatforms) {
const archs = nodeArchs[platform];
const libcs = nodeLibcs[platform];
for (const arch of archs) {
for (const libc of libcs) {
for (const napiVersion of napiVersions) {
const name = binaryFile
.replace('{platform}', platform)
.replace('{arch}', arch)
.replace('{node_napi_label}', `napi-v${napiVersion}`)
.replace('{libc}', libc);
currentDir.push({
name,
date,
size: '-',
isDir: false,
url: `${binaryConfig.distUrl}${versionPrefix}/${name}`,
ignoreDownloadStatuses: [404, 403],
});
}
}
}
}
} else if (binaryFile.includes('{platform}-{arch}-{node_napi_label}')) {
// "_id": "skia-canvas@0.9.22",
// "binary": {
// "module_name": "index",
// "module_path": "./lib/v{napi_build_version}",
// "remote_path": "./v{version}",
// "package_name": "{platform}-{arch}-{node_napi_label}.tar.gz",
// "host": "https://skia-canvas.s3.us-east-1.amazonaws.com",
// "napi_versions": [
// 6
// ]
// },
for (const platform of nodePlatforms) {
const archs = nodeArchs[platform];
for (const arch of archs) {
for (const napiVersion of napiVersions) {
const binaryFileName = binaryFile
.replace('{platform}', platform)
.replace('{arch}', arch)
.replace('{node_napi_label}', napiVersion);
remotePath = remotePath
.replace('{module_name}', moduleName)
.replace('{name}', binaryName)
.replace('{version}', version)
.replace('{configuration}', 'Release');
const binaryFilePath = join('/', remotePath, binaryFileName);
const remoteUrl = `${this.binaryConfig.distUrl}${binaryFilePath}`;
const remoteUrl = `${binaryConfig.distUrl}${binaryFilePath}`;
currentDir.push({
name: binaryFileName,
date,
size: '-',
isDir: false,
url: remoteUrl,
ignoreDownloadStatuses: [ 404 ],
ignoreDownloadStatuses: [404],
});
}
}
}
} else if (
binaryFile.includes('{platform}') &&
binaryFile.includes('{arch}')
) {
// https://github.com/grpc/grpc-node/blob/master/packages/grpc-tools/package.json#L29
// "binary": {
// "module_name": "grpc_tools",
// "host": "https://node-precompiled-binaries.grpc.io/",
// "remote_path": "{name}/v{version}",
// "package_name": "{platform}-{arch}.tar.gz",
// "module_path": "bin"
// },
// handle {configuration}
// "binary": {
// "module_name": "wrtc",
// "module_path": "./build/{configuration}/",
// "remote_path": "./{module_name}/v{version}/{configuration}/",
// "package_name": "{platform}-{arch}.tar.gz",
// "host": "https://node-webrtc.s3.amazonaws.com"
// },
for (const platform of nodePlatforms) {
const archs = nodeArchs[platform];
for (const arch of archs) {
const binaryFileName = binaryFile
.replace('{platform}', platform)
.replace('{arch}', arch);
remotePath = remotePath
.replace('{module_name}', moduleName)
.replace('{name}', binaryName)
.replace('{version}', version)
.replace('{configuration}', 'Release');
const binaryFilePath = join('/', remotePath, binaryFileName);
const remoteUrl = `${binaryConfig.distUrl}${binaryFilePath}`;
currentDir.push({
name: binaryFileName,
date,
size: '-',
isDir: false,
url: remoteUrl,
ignoreDownloadStatuses: [404],
});
}
}
}
}
return { items: this.dirItems[dir] };
return { items: dirItems[dir] };
}
}

View File

@@ -1,14 +1,27 @@
import { FetchResult, BinaryItem } from './AbstractBinary';
import { BucketBinary } from './BucketBinary';
import { SingletonProto } from 'egg';
import binaries from '../../../../config/binaries.ts';
import { BinaryType } from '../../enum/Binary.ts';
import {
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
import { BucketBinary } from './BucketBinary.ts';
@SingletonProto()
@BinaryAdapter(BinaryType.Nwjs)
export class NwjsBinary extends BucketBinary {
private s3Url = 'https://nwjs2.s3.amazonaws.com/?delimiter=/&prefix=';
async fetch(dir: string): Promise<FetchResult | undefined> {
const binaryConfig = binaries.nwjs;
const isRootDir = dir === '/';
// /foo/ => foo/
const subDir = dir.substring(1);
const url = isRootDir ? this.binaryConfig.distUrl : `${this.s3Url}${encodeURIComponent(subDir)}`;
const subDir = dir.slice(1);
const url = isRootDir
? binaryConfig.distUrl
: `${this.s3Url}${encodeURIComponent(subDir)}`;
const xml = await this.requestXml(url);
if (!xml) return;
@@ -19,7 +32,8 @@ export class NwjsBinary extends BucketBinary {
// <tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="v0.15.0-rc1/">v0.15.0-rc1/</a></td><td align="right">06-May-2016 12:24 </td><td align="right"> - </td><td>&nbsp;</td></tr>
// <tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="v0.15.0-rc2/">v0.15.0-rc2/</a></td><td align="right">13-May-2016 20:13 </td><td align="right"> - </td><td>&nbsp;</td></tr>
const items: BinaryItem[] = [];
const re = /<td><a [^>]+?>([^<]+?\/)<\/a><\/td><td [^>]+?>([^>]+?)<\/td>/ig;
const re =
/<td><a [^>]+?>([^<]+?\/)<\/a><\/td><td [^>]+?>([^>]+?)<\/td>/gi;
const matchs = xml.matchAll(re);
for (const m of matchs) {
const name = m[1].trim();
@@ -37,6 +51,6 @@ export class NwjsBinary extends BucketBinary {
return { items, nextParams: null };
}
return { items: this.parseItems(xml, dir), nextParams: null };
return { items: this.parseItems(xml, dir, binaryConfig), nextParams: null };
}
}

View File

@@ -1,7 +1,15 @@
import util from 'node:util';
import path from 'node:path';
import { AbstractBinary, BinaryItem, FetchResult } from './AbstractBinary';
import util from 'util';
import path from 'path';
import { SingletonProto } from 'egg';
import { BinaryType } from '../../enum/Binary.ts';
import {
AbstractBinary,
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
const PACKAGE_URL = 'https://registry.npmjs.com/playwright-core';
const DOWNLOAD_HOST = 'https://playwright.azureedge.net/';
@@ -9,164 +17,408 @@ const DOWNLOAD_HOST = 'https://playwright.azureedge.net/';
// https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/registry/index.ts
/* eslint-disable quote-props */
const DOWNLOAD_PATHS = {
'chromium': {
chromium: {
'<unknown>': undefined,
'generic-linux': 'builds/chromium/%s/chromium-linux.zip',
'generic-linux-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip',
'ubuntu18.04': 'builds/chromium/%s/chromium-linux.zip',
'ubuntu20.04': 'builds/chromium/%s/chromium-linux.zip',
'ubuntu22.04': 'builds/chromium/%s/chromium-linux.zip',
'ubuntu18.04-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip',
'ubuntu18.04-x64': undefined,
'ubuntu20.04-x64': 'builds/chromium/%s/chromium-linux.zip',
'ubuntu22.04-x64': 'builds/chromium/%s/chromium-linux.zip',
'ubuntu24.04-x64': 'builds/chromium/%s/chromium-linux.zip',
'ubuntu18.04-arm64': undefined,
'ubuntu20.04-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip',
'ubuntu22.04-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip',
'ubuntu24.04-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip',
'debian11-x64': 'builds/chromium/%s/chromium-linux.zip',
'debian11-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip',
'debian12-x64': 'builds/chromium/%s/chromium-linux.zip',
'debian12-arm64': 'builds/chromium/%s/chromium-linux-arm64.zip',
'mac10.13': 'builds/chromium/%s/chromium-mac.zip',
'mac10.14': 'builds/chromium/%s/chromium-mac.zip',
'mac10.15': 'builds/chromium/%s/chromium-mac.zip',
'mac11': 'builds/chromium/%s/chromium-mac.zip',
mac11: 'builds/chromium/%s/chromium-mac.zip',
'mac11-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip',
'mac12': 'builds/chromium/%s/chromium-mac.zip',
mac12: 'builds/chromium/%s/chromium-mac.zip',
'mac12-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip',
'win64': 'builds/chromium/%s/chromium-win64.zip',
mac13: 'builds/chromium/%s/chromium-mac.zip',
'mac13-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip',
mac14: 'builds/chromium/%s/chromium-mac.zip',
'mac14-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip',
mac15: 'builds/chromium/%s/chromium-mac.zip',
'mac15-arm64': 'builds/chromium/%s/chromium-mac-arm64.zip',
win64: 'builds/chromium/%s/chromium-win64.zip',
},
'chromium-headless-shell': {
'<unknown>': undefined,
'ubuntu18.04-x64': undefined,
'ubuntu20.04-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip',
'ubuntu22.04-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip',
'ubuntu24.04-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip',
'ubuntu18.04-arm64': undefined,
'ubuntu20.04-arm64':
'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip',
'ubuntu22.04-arm64':
'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip',
'ubuntu24.04-arm64':
'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip',
'debian11-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip',
'debian11-arm64':
'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip',
'debian12-x64': 'builds/chromium/%s/chromium-headless-shell-linux.zip',
'debian12-arm64':
'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip',
'mac10.13': undefined,
'mac10.14': undefined,
'mac10.15': undefined,
mac11: 'builds/chromium/%s/chromium-headless-shell-mac.zip',
'mac11-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip',
mac12: 'builds/chromium/%s/chromium-headless-shell-mac.zip',
'mac12-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip',
mac13: 'builds/chromium/%s/chromium-headless-shell-mac.zip',
'mac13-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip',
mac14: 'builds/chromium/%s/chromium-headless-shell-mac.zip',
'mac14-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip',
mac15: 'builds/chromium/%s/chromium-headless-shell-mac.zip',
'mac15-arm64': 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip',
win64: 'builds/chromium/%s/chromium-headless-shell-win64.zip',
},
'chromium-tip-of-tree': {
'<unknown>': undefined,
'generic-linux': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip',
'generic-linux-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip',
'ubuntu18.04': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip',
'ubuntu20.04': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip',
'ubuntu22.04': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip',
'ubuntu18.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip',
'ubuntu20.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip',
'ubuntu22.04-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip',
'ubuntu18.04-x64': undefined,
'ubuntu20.04-x64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip',
'ubuntu22.04-x64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip',
'ubuntu24.04-x64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip',
'ubuntu18.04-arm64': undefined,
'ubuntu20.04-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip',
'ubuntu22.04-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip',
'ubuntu24.04-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip',
'debian11-x64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip',
'debian11-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip',
'debian12-x64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip',
'debian12-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip',
'mac10.13': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip',
'mac10.14': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip',
'mac10.15': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip',
'mac11': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip',
'mac11-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip',
'mac12': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip',
'mac12-arm64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip',
'win64': 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-win64.zip',
mac11: 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip',
'mac11-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip',
mac12: 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip',
'mac12-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip',
mac13: 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip',
'mac13-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip',
mac14: 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip',
'mac14-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip',
mac15: 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip',
'mac15-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip',
win64: 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-win64.zip',
},
'chromium-with-symbols': {
'chromium-tip-of-tree-headless-shell': {
'<unknown>': undefined,
'generic-linux': 'builds/chromium/%s/chromium-with-symbols-linux.zip',
'generic-linux-arm64': 'builds/chromium/%s/chromium-with-symbols-linux-arm64.zip',
'ubuntu18.04': 'builds/chromium/%s/chromium-with-symbols-linux.zip',
'ubuntu20.04': 'builds/chromium/%s/chromium-with-symbols-linux.zip',
'ubuntu22.04': 'builds/chromium/%s/chromium-with-symbols-linux.zip',
'ubuntu18.04-arm64': 'builds/chromium/%s/chromium-with-symbols-linux-arm64.zip',
'ubuntu20.04-arm64': 'builds/chromium/%s/chromium-with-symbols-linux-arm64.zip',
'ubuntu22.04-arm64': 'builds/chromium/%s/chromium-with-symbols-linux-arm64.zip',
'mac10.13': 'builds/chromium/%s/chromium-with-symbols-mac.zip',
'mac10.14': 'builds/chromium/%s/chromium-with-symbols-mac.zip',
'mac10.15': 'builds/chromium/%s/chromium-with-symbols-mac.zip',
'mac11': 'builds/chromium/%s/chromium-with-symbols-mac.zip',
'mac11-arm64': 'builds/chromium/%s/chromium-with-symbols-mac-arm64.zip',
'mac12': 'builds/chromium/%s/chromium-with-symbols-mac.zip',
'mac12-arm64': 'builds/chromium/%s/chromium-with-symbols-mac-arm64.zip',
'win64': 'builds/chromium/%s/chromium-with-symbols-win64.zip',
'ubuntu18.04-x64': undefined,
'ubuntu20.04-x64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip',
'ubuntu22.04-x64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip',
'ubuntu24.04-x64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip',
'ubuntu18.04-arm64': undefined,
'ubuntu20.04-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip',
'ubuntu22.04-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip',
'ubuntu24.04-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip',
'debian11-x64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip',
'debian11-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip',
'debian12-x64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip',
'debian12-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip',
'mac10.13': undefined,
'mac10.14': undefined,
'mac10.15': undefined,
mac11:
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip',
'mac11-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip',
mac12:
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip',
'mac12-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip',
mac13:
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip',
'mac13-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip',
mac14:
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip',
'mac14-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip',
mac15:
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip',
'mac15-arm64':
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip',
win64:
'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-win64.zip',
},
'firefox': {
firefox: {
'<unknown>': undefined,
'generic-linux': 'builds/firefox/%s/firefox-ubuntu-20.04.zip',
'generic-linux-arm64': 'builds/firefox/%s/firefox-ubuntu-20.04-arm64.zip',
'ubuntu18.04': 'builds/firefox/%s/firefox-ubuntu-18.04.zip',
'ubuntu20.04': 'builds/firefox/%s/firefox-ubuntu-20.04.zip',
'ubuntu22.04': 'builds/firefox/%s/firefox-ubuntu-22.04.zip',
'ubuntu18.04-x64': undefined,
'ubuntu20.04-x64': 'builds/firefox/%s/firefox-ubuntu-20.04.zip',
'ubuntu22.04-x64': 'builds/firefox/%s/firefox-ubuntu-22.04.zip',
'ubuntu24.04-x64': 'builds/firefox/%s/firefox-ubuntu-24.04.zip',
'ubuntu18.04-arm64': undefined,
'ubuntu20.04-arm64': 'builds/firefox/%s/firefox-ubuntu-20.04-arm64.zip',
'ubuntu22.04-arm64': 'builds/firefox/%s/firefox-ubuntu-22.04-arm64.zip',
'mac10.13': 'builds/firefox/%s/firefox-mac-11.zip',
'mac10.14': 'builds/firefox/%s/firefox-mac-11.zip',
'mac10.15': 'builds/firefox/%s/firefox-mac-11.zip',
'mac11': 'builds/firefox/%s/firefox-mac-11.zip',
'mac11-arm64': 'builds/firefox/%s/firefox-mac-11-arm64.zip',
'mac12': 'builds/firefox/%s/firefox-mac-11.zip',
'mac12-arm64': 'builds/firefox/%s/firefox-mac-11-arm64.zip',
'win64': 'builds/firefox/%s/firefox-win64.zip',
'ubuntu24.04-arm64': 'builds/firefox/%s/firefox-ubuntu-24.04-arm64.zip',
'debian11-x64': 'builds/firefox/%s/firefox-debian-11.zip',
'debian11-arm64': 'builds/firefox/%s/firefox-debian-11-arm64.zip',
'debian12-x64': 'builds/firefox/%s/firefox-debian-12.zip',
'debian12-arm64': 'builds/firefox/%s/firefox-debian-12-arm64.zip',
'mac10.13': 'builds/firefox/%s/firefox-mac.zip',
'mac10.14': 'builds/firefox/%s/firefox-mac.zip',
'mac10.15': 'builds/firefox/%s/firefox-mac.zip',
mac11: 'builds/firefox/%s/firefox-mac.zip',
'mac11-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip',
mac12: 'builds/firefox/%s/firefox-mac.zip',
'mac12-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip',
mac13: 'builds/firefox/%s/firefox-mac.zip',
'mac13-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip',
mac14: 'builds/firefox/%s/firefox-mac.zip',
'mac14-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip',
mac15: 'builds/firefox/%s/firefox-mac.zip',
'mac15-arm64': 'builds/firefox/%s/firefox-mac-arm64.zip',
win64: 'builds/firefox/%s/firefox-win64.zip',
},
'firefox-beta': {
'<unknown>': undefined,
'generic-linux': 'builds/firefox-beta/%s/firefox-beta-ubuntu-20.04.zip',
'generic-linux-arm64': undefined,
'ubuntu18.04': 'builds/firefox-beta/%s/firefox-beta-ubuntu-18.04.zip',
'ubuntu20.04': 'builds/firefox-beta/%s/firefox-beta-ubuntu-20.04.zip',
'ubuntu22.04': 'builds/firefox-beta/%s/firefox-beta-ubuntu-22.04.zip',
'ubuntu18.04-x64': undefined,
'ubuntu20.04-x64': 'builds/firefox-beta/%s/firefox-beta-ubuntu-20.04.zip',
'ubuntu22.04-x64': 'builds/firefox-beta/%s/firefox-beta-ubuntu-22.04.zip',
'ubuntu24.04-x64': 'builds/firefox-beta/%s/firefox-beta-ubuntu-24.04.zip',
'ubuntu18.04-arm64': undefined,
'ubuntu20.04-arm64': undefined,
'ubuntu22.04-arm64': 'builds/firefox-beta/%s/firefox-beta-ubuntu-22.04-arm64.zip',
'mac10.13': 'builds/firefox-beta/%s/firefox-beta-mac-11.zip',
'mac10.14': 'builds/firefox-beta/%s/firefox-beta-mac-11.zip',
'mac10.15': 'builds/firefox-beta/%s/firefox-beta-mac-11.zip',
'mac11': 'builds/firefox-beta/%s/firefox-beta-mac-11.zip',
'mac11-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-11-arm64.zip',
'mac12': 'builds/firefox-beta/%s/firefox-beta-mac-11.zip',
'mac12-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-11-arm64.zip',
'win64': 'builds/firefox-beta/%s/firefox-beta-win64.zip',
'ubuntu22.04-arm64':
'builds/firefox-beta/%s/firefox-beta-ubuntu-22.04-arm64.zip',
'ubuntu24.04-arm64':
'builds/firefox-beta/%s/firefox-beta-ubuntu-24.04-arm64.zip',
'debian11-x64': 'builds/firefox-beta/%s/firefox-beta-debian-11.zip',
'debian11-arm64': 'builds/firefox-beta/%s/firefox-beta-debian-11-arm64.zip',
'debian12-x64': 'builds/firefox-beta/%s/firefox-beta-debian-12.zip',
'debian12-arm64': 'builds/firefox-beta/%s/firefox-beta-debian-12-arm64.zip',
'mac10.13': 'builds/firefox-beta/%s/firefox-beta-mac.zip',
'mac10.14': 'builds/firefox-beta/%s/firefox-beta-mac.zip',
'mac10.15': 'builds/firefox-beta/%s/firefox-beta-mac.zip',
mac11: 'builds/firefox-beta/%s/firefox-beta-mac.zip',
'mac11-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip',
mac12: 'builds/firefox-beta/%s/firefox-beta-mac.zip',
'mac12-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip',
mac13: 'builds/firefox-beta/%s/firefox-beta-mac.zip',
'mac13-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip',
mac14: 'builds/firefox-beta/%s/firefox-beta-mac.zip',
'mac14-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip',
mac15: 'builds/firefox-beta/%s/firefox-beta-mac.zip',
'mac15-arm64': 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip',
win64: 'builds/firefox-beta/%s/firefox-beta-win64.zip',
},
'webkit': {
webkit: {
'<unknown>': undefined,
'generic-linux': 'builds/webkit/%s/webkit-ubuntu-20.04.zip',
'generic-linux-arm64': 'builds/webkit/%s/webkit-ubuntu-20.04-arm64.zip',
'ubuntu18.04': 'builds/webkit/%s/webkit-ubuntu-18.04.zip',
'ubuntu20.04': 'builds/webkit/%s/webkit-ubuntu-20.04.zip',
'ubuntu22.04': 'builds/webkit/%s/webkit-ubuntu-22.04.zip',
'ubuntu18.04-x64': undefined,
'ubuntu20.04-x64': 'builds/webkit/%s/webkit-ubuntu-20.04.zip',
'ubuntu22.04-x64': 'builds/webkit/%s/webkit-ubuntu-22.04.zip',
'ubuntu24.04-x64': 'builds/webkit/%s/webkit-ubuntu-24.04.zip',
'ubuntu18.04-arm64': undefined,
'ubuntu20.04-arm64': 'builds/webkit/%s/webkit-ubuntu-20.04-arm64.zip',
'ubuntu22.04-arm64': 'builds/webkit/%s/webkit-ubuntu-22.04-arm64.zip',
'ubuntu24.04-arm64': 'builds/webkit/%s/webkit-ubuntu-24.04-arm64.zip',
'debian11-x64': 'builds/webkit/%s/webkit-debian-11.zip',
'debian11-arm64': 'builds/webkit/%s/webkit-debian-11-arm64.zip',
'debian12-x64': 'builds/webkit/%s/webkit-debian-12.zip',
'debian12-arm64': 'builds/webkit/%s/webkit-debian-12-arm64.zip',
'mac10.13': undefined,
'mac10.14': 'builds/deprecated-webkit-mac-10.14/%s/deprecated-webkit-mac-10.14.zip',
'mac10.15': 'builds/webkit/%s/webkit-mac-10.15.zip',
'mac11': 'builds/webkit/%s/webkit-mac-11.zip',
'mac10.14':
'builds/deprecated-webkit-mac-10.14/%s/deprecated-webkit-mac-10.14.zip',
'mac10.15':
'builds/deprecated-webkit-mac-10.15/%s/deprecated-webkit-mac-10.15.zip',
mac11: 'builds/webkit/%s/webkit-mac-11.zip',
'mac11-arm64': 'builds/webkit/%s/webkit-mac-11-arm64.zip',
'mac12': 'builds/webkit/%s/webkit-mac-12.zip',
mac12: 'builds/webkit/%s/webkit-mac-12.zip',
'mac12-arm64': 'builds/webkit/%s/webkit-mac-12-arm64.zip',
'win64': 'builds/webkit/%s/webkit-win64.zip',
mac13: 'builds/webkit/%s/webkit-mac-13.zip',
'mac13-arm64': 'builds/webkit/%s/webkit-mac-13-arm64.zip',
mac14: 'builds/webkit/%s/webkit-mac-14.zip',
'mac14-arm64': 'builds/webkit/%s/webkit-mac-14-arm64.zip',
mac15: 'builds/webkit/%s/webkit-mac-15.zip',
'mac15-arm64': 'builds/webkit/%s/webkit-mac-15-arm64.zip',
win64: 'builds/webkit/%s/webkit-win64.zip',
},
'ffmpeg': {
ffmpeg: {
'<unknown>': undefined,
'generic-linux': 'builds/ffmpeg/%s/ffmpeg-linux.zip',
'generic-linux-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip',
'ubuntu18.04': 'builds/ffmpeg/%s/ffmpeg-linux.zip',
'ubuntu20.04': 'builds/ffmpeg/%s/ffmpeg-linux.zip',
'ubuntu22.04': 'builds/ffmpeg/%s/ffmpeg-linux.zip',
'ubuntu18.04-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip',
'ubuntu18.04-x64': undefined,
'ubuntu20.04-x64': 'builds/ffmpeg/%s/ffmpeg-linux.zip',
'ubuntu22.04-x64': 'builds/ffmpeg/%s/ffmpeg-linux.zip',
'ubuntu24.04-x64': 'builds/ffmpeg/%s/ffmpeg-linux.zip',
'ubuntu18.04-arm64': undefined,
'ubuntu20.04-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip',
'ubuntu22.04-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip',
'ubuntu24.04-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip',
'debian11-x64': 'builds/ffmpeg/%s/ffmpeg-linux.zip',
'debian11-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip',
'debian12-x64': 'builds/ffmpeg/%s/ffmpeg-linux.zip',
'debian12-arm64': 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip',
'mac10.13': 'builds/ffmpeg/%s/ffmpeg-mac.zip',
'mac10.14': 'builds/ffmpeg/%s/ffmpeg-mac.zip',
'mac10.15': 'builds/ffmpeg/%s/ffmpeg-mac.zip',
'mac11': 'builds/ffmpeg/%s/ffmpeg-mac.zip',
mac11: 'builds/ffmpeg/%s/ffmpeg-mac.zip',
'mac11-arm64': 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip',
'mac12': 'builds/ffmpeg/%s/ffmpeg-mac.zip',
mac12: 'builds/ffmpeg/%s/ffmpeg-mac.zip',
'mac12-arm64': 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip',
'win64': 'builds/ffmpeg/%s/ffmpeg-win64.zip',
mac13: 'builds/ffmpeg/%s/ffmpeg-mac.zip',
'mac13-arm64': 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip',
mac14: 'builds/ffmpeg/%s/ffmpeg-mac.zip',
'mac14-arm64': 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip',
mac15: 'builds/ffmpeg/%s/ffmpeg-mac.zip',
'mac15-arm64': 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip',
win64: 'builds/ffmpeg/%s/ffmpeg-win64.zip',
},
};
winldd: {
'<unknown>': undefined,
'ubuntu18.04-x64': undefined,
'ubuntu20.04-x64': undefined,
'ubuntu22.04-x64': undefined,
'ubuntu24.04-x64': undefined,
'ubuntu18.04-arm64': undefined,
'ubuntu20.04-arm64': undefined,
'ubuntu22.04-arm64': undefined,
'ubuntu24.04-arm64': undefined,
'debian11-x64': undefined,
'debian11-arm64': undefined,
'debian12-x64': undefined,
'debian12-arm64': undefined,
'mac10.13': undefined,
'mac10.14': undefined,
'mac10.15': undefined,
mac11: undefined,
'mac11-arm64': undefined,
mac12: undefined,
'mac12-arm64': undefined,
mac13: undefined,
'mac13-arm64': undefined,
mac14: undefined,
'mac14-arm64': undefined,
mac15: undefined,
'mac15-arm64': undefined,
win64: 'builds/winldd/%s/winldd-win64.zip',
},
android: {
'<unknown>': 'builds/android/%s/android.zip',
'ubuntu18.04-x64': undefined,
'ubuntu20.04-x64': 'builds/android/%s/android.zip',
'ubuntu22.04-x64': 'builds/android/%s/android.zip',
'ubuntu24.04-x64': 'builds/android/%s/android.zip',
'ubuntu18.04-arm64': undefined,
'ubuntu20.04-arm64': 'builds/android/%s/android.zip',
'ubuntu22.04-arm64': 'builds/android/%s/android.zip',
'ubuntu24.04-arm64': 'builds/android/%s/android.zip',
'debian11-x64': 'builds/android/%s/android.zip',
'debian11-arm64': 'builds/android/%s/android.zip',
'debian12-x64': 'builds/android/%s/android.zip',
'debian12-arm64': 'builds/android/%s/android.zip',
'mac10.13': 'builds/android/%s/android.zip',
'mac10.14': 'builds/android/%s/android.zip',
'mac10.15': 'builds/android/%s/android.zip',
mac11: 'builds/android/%s/android.zip',
'mac11-arm64': 'builds/android/%s/android.zip',
mac12: 'builds/android/%s/android.zip',
'mac12-arm64': 'builds/android/%s/android.zip',
mac13: 'builds/android/%s/android.zip',
'mac13-arm64': 'builds/android/%s/android.zip',
mac14: 'builds/android/%s/android.zip',
'mac14-arm64': 'builds/android/%s/android.zip',
mac15: 'builds/android/%s/android.zip',
'mac15-arm64': 'builds/android/%s/android.zip',
win64: 'builds/android/%s/android.zip',
},
} as const;
@SingletonProto()
@BinaryAdapter(BinaryType.Playwright)
export class PlaywrightBinary extends AbstractBinary {
private dirItems?: Record<string, BinaryItem[]>;
async initFetch() {
this.dirItems = undefined;
}
async fetch(dir: string): Promise<FetchResult | undefined> {
if (!this.dirItems) {
const packageData = await this.requestJSON(PACKAGE_URL);
const nowDateISO = new Date().toISOString();
const buildDirs: BinaryItem[] = [];
for (const browserName of Object.keys(DOWNLOAD_PATHS)) {
if (
browserName === 'chromium-headless-shell' ||
browserName === 'chromium-tip-of-tree-headless-shell'
) {
continue;
}
buildDirs.push({
name: `${browserName}/`,
isDir: true,
url: '',
size: '-',
date: nowDateISO,
});
}
this.dirItems = {
'/': [{ name: 'builds/', isDir: true, url: '', size: '-', date: nowDateISO }],
'/builds/': Object.keys(DOWNLOAD_PATHS).map(
dist => ({ name: `${dist}/`, isDir: true, url: '', size: '-', date: nowDateISO })),
...Object.fromEntries(Object.keys(DOWNLOAD_PATHS).map(dist => [ `/builds/${dist}/`, []])),
'/': [
{
name: 'builds/',
isDir: true,
url: '',
size: '-',
date: nowDateISO,
},
],
'/builds/': buildDirs,
};
for (const browserName of Object.keys(DOWNLOAD_PATHS)) {
if (
browserName === 'chromium-headless-shell' ||
browserName === 'chromium-tip-of-tree-headless-shell'
) {
continue;
}
this.dirItems[`/builds/${browserName}/`] = [];
}
// Only download beta and release versions of packages to reduce amount of request
const packageVersions = Object.keys(packageData.versions)
.filter(version => version.match(/^(?:\d+\.\d+\.\d+)(?:-beta-\d+)?$/))
// select recently update 20 items
.slice(-20);
const browsers: { name: string; revision: string; browserVersion: string; revisionOverrides?: Record<string, string> }[] = [];
const browsers: {
name: keyof typeof DOWNLOAD_PATHS;
revision: string;
browserVersion: string;
revisionOverrides?: Record<string, string>;
}[] = [];
await Promise.all(
packageVersions.map(version =>
this.requestJSON(
`https://unpkg.com/playwright-core@${version}/browsers.json`,
`https://unpkg.com/playwright-core@${version}/browsers.json`
)
.then(data => {
// browsers: [
@@ -179,25 +431,76 @@ export class PlaywrightBinary extends AbstractBinary {
// },
// ]
browsers.push(...data.browsers);
return data;
})
.catch(err => {
this.logger.warn('[PlaywrightBinary.fetch:error] Playwright version %s browser data request failed: %s', version, err);
}),
),
/* c8 ignore next 2 */
this.logger.warn(
'[PlaywrightBinary.fetch:error] Playwright version %s browser data request failed: %s',
version,
err
);
})
)
);
// if chromium-headless-shell not exists on browsers, copy chromium to chromium-headless-shell
if (
!browsers.some(browser => browser.name === 'chromium-headless-shell')
) {
const chromium = browsers.find(browser => browser.name === 'chromium');
// {
// "name": "chromium",
// "revision": "1155",
// "installByDefault": true,
// "browserVersion": "133.0.6943.16"
// }
if (chromium) {
browsers.push({
...chromium,
name: 'chromium-headless-shell',
});
}
}
// if chromium-tip-of-tree-headless-shell not exists on browsers, copy chromium-tip-of-tree to chromium-tip-of-tree-headless-shell
if (
!browsers.some(
browser => browser.name === 'chromium-tip-of-tree-headless-shell'
)
) {
const chromiumTipOfTree = browsers.find(
browser => browser.name === 'chromium-tip-of-tree'
);
if (chromiumTipOfTree) {
browsers.push({
...chromiumTipOfTree,
name: 'chromium-tip-of-tree-headless-shell',
});
}
}
for (const browser of browsers) {
const downloadPaths = DOWNLOAD_PATHS[browser.name];
if (!downloadPaths) continue;
for (const [ platform, remotePath ] of Object.entries(downloadPaths)) {
let browserDirname = browser.name;
if (browser.name === 'chromium-headless-shell') {
// chromium-headless-shell should be under chromium
// https://playwright.azureedge.net/builds/chromium/1155/chromium-headless-shell-mac-arm64.zip
browserDirname = 'chromium';
} else if (browser.name === 'chromium-tip-of-tree-headless-shell') {
// chromium-tip-of-tree-headless-shell should be under chromium-tip-of-tree
// https://playwright.azureedge.net/builds/chromium-tip-of-tree/1293/chromium-tip-of-tree-headless-shell-mac-arm64.zip
browserDirname = 'chromium-tip-of-tree';
}
for (const [platform, remotePath] of Object.entries(downloadPaths)) {
if (typeof remotePath !== 'string') continue;
const revision = browser.revisionOverrides?.[platform] ?? browser.revision;
const revision =
browser.revisionOverrides?.[platform] ?? browser.revision;
const itemDate = browser.browserVersion || revision;
const url = DOWNLOAD_HOST + util.format(remotePath, revision);
const name = path.basename(remotePath);
const dir = `/builds/${browser.name}/${revision}/`;
const dir = `/builds/${browserDirname}/${revision}/`;
if (!this.dirItems[dir]) {
this.dirItems[`/builds/${browser.name}/`].push({
this.dirItems[`/builds/${browserDirname}/`].push({
name: `${revision}/`,
isDir: true,
url: '',
@@ -206,8 +509,14 @@ export class PlaywrightBinary extends AbstractBinary {
});
this.dirItems[dir] = [];
}
if (!this.dirItems[dir].find(item => item.name === name)) {
this.dirItems[dir].push({ name, isDir: false, url, size: '-', date: itemDate });
if (!this.dirItems[dir].some(item => item.name === name)) {
this.dirItems[dir].push({
name,
isDir: false,
url,
size: '-',
date: itemDate,
});
}
}
}
@@ -216,4 +525,3 @@ export class PlaywrightBinary extends AbstractBinary {
return { items: this.dirItems[dir] ?? [], nextParams: null };
}
}

View File

@@ -0,0 +1,145 @@
import path from 'node:path';
import { SingletonProto } from 'egg';
import binaries, { type BinaryName } from '../../../../config/binaries.ts';
import { BinaryType } from '../../enum/Binary.ts';
import {
AbstractBinary,
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
@SingletonProto()
@BinaryAdapter(BinaryType.Prisma)
export class PrismaBinary extends AbstractBinary {
private dirItems: {
[key: string]: BinaryItem[];
} = {};
async initFetch() {
// https://github.com/cnpm/cnpmcore/issues/473#issuecomment-1562115738
const pkgUrl = 'https://registry.npmjs.com/@prisma/engines';
const data = await this.requestJSON(pkgUrl);
const modified = data.time.modified;
this.dirItems = {};
this.dirItems['/'] = [
{
name: 'all_commits/',
date: modified,
size: '-',
isDir: true,
url: '',
},
];
this.dirItems['/all_commits/'] = [];
const commitIdMap: Record<string, boolean> = {};
// https://list-binaries.prisma-orm.workers.dev/?delimiter=/&prefix=all_commits/61023c35d2c8762f66f09bc4183d2f630b541d08/
for (const version in data.versions) {
const major = Number.parseInt(version.split('.', 1)[0]);
// need >= 3.0.0
if (major < 3) continue;
const date = data.time[version];
const pkg = data.versions[version];
// https://registry.npmjs.com/@prisma/engines/4.14.1
// https://registry.npmjs.com/@prisma/engines/5.7.0 should read from dependencies
const enginesVersion =
pkg.devDependencies?.['@prisma/engines-version'] ||
pkg.dependencies?.['@prisma/engines-version'] ||
'';
// "@prisma/engines-version": "4.14.0-67.d9a4c5988f480fa576d43970d5a23641aa77bc9c"
// "@prisma/engines-version": "5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9"
const matched = /\.(\w{30,})$/.exec(enginesVersion);
if (!matched) continue;
const commitId = matched[1];
if (commitIdMap[commitId]) continue;
commitIdMap[commitId] = true;
this.dirItems['/all_commits/'].push({
name: `${commitId}/`,
date,
size: '-',
isDir: true,
url: '',
});
}
}
async fetch(
dir: string,
binaryName: BinaryName
): Promise<FetchResult | undefined> {
const existsItems = this.dirItems[dir];
if (existsItems) {
return { items: existsItems, nextParams: null };
}
// /foo/ => foo/
const binaryConfig = binaries[binaryName];
const subDir = dir.slice(1);
const url = `${binaryConfig.distUrl}?delimiter=/&prefix=${encodeURIComponent(subDir)}`;
const result = await this.requestJSON(url);
return { items: this.#parseItems(result), nextParams: null };
}
// oxlint-disable-next-line typescript-eslint/no-explicit-any
#parseItems(result: any): BinaryItem[] {
const items: BinaryItem[] = [];
// objects": [
// {
// "uploaded": "2023-05-23T15:43:05.772Z",
// "checksums": {
// "md5": "d41d8cd98f00b204e9800998ecf8427e"
// },
// "httpEtag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
// "etag": "d41d8cd98f00b204e9800998ecf8427e",
// "size": 0,
// "version": "7e77b6b8c1d214f2c6be3c959749b5a6",
// "key": "all_commits/61023c35d2c8762f66f09bc4183d2f630b541d08/darwin-arm64/.finished"
// },
// {
// "uploaded": "2023-05-23T15:41:33.861Z",
// "checksums": {
// "md5": "4822215a13ae372ae82afd12689fce37"
// },
// "httpEtag": "\"4822215a13ae372ae82afd12689fce37\"",
// "etag": "4822215a13ae372ae82afd12689fce37",
// "size": 96,
// "version": "7e77b6ba29d4e776023e4fa62825c13a",
// "key": "all_commits/61023c35d2c8762f66f09bc4183d2f630b541d08/darwin-arm64/libquery_engine.dylib.node.gz.sha256"
// },
// https://list-binaries.prisma-orm.workers.dev/?delimiter=/&prefix=all_commits/61023c35d2c8762f66f09bc4183d2f630b541d08/darwin-arm64/
const objects: {
uploaded: string;
size: number;
key: string;
}[] = result.objects || [];
for (const o of objects) {
const fullname = o.key;
// ignore size = 0
if (o.size === 0) continue;
const name = path.basename(fullname);
items.push({
name,
isDir: false,
// https://binaries.prisma.sh/all_commits/2452cc6313d52b8b9a96999ac0e974d0aedf88db/darwin-arm64/prisma-fmt.gz
url: `https://binaries.prisma.sh/${fullname}`,
size: o.size,
date: o.uploaded,
});
}
// delimitedPrefixes: [ 'all_commits/61023c35d2c8762f66f09bc4183d2f630b541d08/darwin-arm64/' ]
// https://list-binaries.prisma-orm.workers.dev/?delimiter=/&prefix=all_commits/61023c35d2c8762f66f09bc4183d2f630b541d08/
const delimitedPrefixes: string[] = result.delimitedPrefixes || [];
for (const fullname of delimitedPrefixes) {
const name = `${path.basename(fullname)}/`;
items.push({
name,
isDir: true,
url: '',
size: '-',
date: new Date().toISOString(),
});
}
return items;
}
}

View File

@@ -1,76 +1,51 @@
import { AbstractBinary, FetchResult, BinaryItem } from './AbstractBinary';
import { SingletonProto } from 'egg';
import { XMLParser } from 'fast-xml-parser';
import { BinaryType } from '../../enum/Binary.ts';
import {
AbstractBinary,
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
export const platforms = ['Linux_x64', 'Mac', 'Mac_Arm', 'Win', 'Win_x64'];
const MAX_DEPTH = 1;
@SingletonProto()
@BinaryAdapter(BinaryType.Puppeteer)
export class PuppeteerBinary extends AbstractBinary {
private dirItems: {
private dirItems?: {
[key: string]: BinaryItem[];
};
async fetch(dir: string): Promise<FetchResult | undefined> {
async initFetch() {
this.dirItems = undefined;
}
async fetch(
dir: string,
_binaryName: string,
lastData?: Record<string, unknown>
): Promise<FetchResult | undefined> {
if (!this.dirItems) {
const pkgUrl = 'https://registry.npmjs.com/puppeteer';
const data = await this.requestJSON(pkgUrl);
const s3Url = 'https://chromium-browser-snapshots.storage.googleapis.com';
const chromiumRevisions = new Map<string, string>();
this.dirItems = {};
this.dirItems['/'] = [];
const chromiumRevisions = new Map<string, string>();
for (const version in data.versions) {
// find chromium versions
const pkg = data.versions[version];
const revision = pkg.puppeteer?.chromium_revision ? String(pkg.puppeteer.chromium_revision) : '';
if (revision && !chromiumRevisions.has(revision)) {
chromiumRevisions.set(revision, data.time[version]);
}
}
// https://unpkg.com/puppeteer@5.1.0/lib/cjs/revisions.js
// https://unpkg.com/puppeteer@latest/lib/cjs/puppeteer/revisions.js
// exports.PUPPETEER_REVISIONS = {
// chromium: '768783',
// firefox: 'latest',
// };
const unpkgURL = 'https://unpkg.com/puppeteer-core@latest/lib/cjs/puppeteer/revisions.js';
const text = await this.requestXml(unpkgURL);
const m = /chromium:\s+\'(\d+)\'\,/.exec(text);
if (m && !chromiumRevisions.has(m[1])) {
chromiumRevisions.set(m[1], new Date().toISOString());
}
// download LAST_CHANGE
// https://github.com/chaopeng/chromium-downloader/blob/master/get-chromium#L28
const LAST_CHANGE_URL = 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2FLAST_CHANGE?alt=media';
const lastRevision = await this.requestXml(LAST_CHANGE_URL);
if (lastRevision) {
chromiumRevisions.set(lastRevision, new Date().toISOString());
}
// old versions
// v5.0.0
chromiumRevisions.set('756035', data.time['5.0.0']);
// v5.2.0
chromiumRevisions.set('768783', data.time['5.2.0']);
// v5.2.1
chromiumRevisions.set('782078', data.time['5.2.1']);
// v5.3.0
chromiumRevisions.set('800071', data.time['5.3.0']);
// v5.4.0
chromiumRevisions.set('809590', data.time['5.4.0']);
// v5.5.0
chromiumRevisions.set('818858', data.time['5.5.0']);
// v6.0.0
chromiumRevisions.set('843427', data.time['6.0.0']);
// "7.0.0"
chromiumRevisions.set('848005', data.time['7.0.0']);
// https://github.com/puppeteer/puppeteer/blob/v8.0.0/src/revisions.ts#L23
// "8.0.0":"2021-02-26T08:36:50.107Z"
chromiumRevisions.set('856583', data.time['8.0.0']);
// "9.0.0":"2021-04-21T11:27:32.513Z"
chromiumRevisions.set('869685', data.time['9.0.0']);
// "10.0.0":"2021-05-31T12:42:27.486Z"
chromiumRevisions.set('884014', data.time['10.0.0']);
// "11.0.0":"2021-11-03T09:29:12.751Z"
chromiumRevisions.set('901912', data.time['11.0.0']);
const platforms = [ 'Linux_x64', 'Mac', 'Mac_Arm', 'Win', 'Win_x64' ];
for (const platform of platforms) {
const revision = lastData?.[platform] as string;
if (!revision) {
// 丢弃库中历史不带 lastData 的任务,防止遍历任务过多
this.logger.info(
'drop puppeteer task if has no last data for platform %s, lastPlatform',
platform,
lastData
);
return;
}
let marker = revision ? `${platform}/${revision}/REVISIONS` : undefined;
this.dirItems['/'].push({
name: `${platform}/`,
date: new Date().toISOString(),
@@ -79,8 +54,35 @@ export class PuppeteerBinary extends AbstractBinary {
url: '',
});
this.dirItems[`/${platform}/`] = [];
let i = 0;
do {
let requestUrl = `${s3Url}?prefix=${platform}&max-keys=100`;
if (marker) {
requestUrl += `&marker=${marker}`;
}
const xml = await this.requestXml(requestUrl);
const parser = new XMLParser();
const obj = parser.parse(xml);
if (
obj.ListBucketResult.IsTruncated === true &&
obj.ListBucketResult.NextMarker
) {
marker = obj.ListBucketResult.NextMarker;
} else {
marker = undefined;
}
for (const content of obj.ListBucketResult.Contents) {
// /Linux_x64/1041455/REVISIONS
if (content.Key.endsWith('/REVISIONS')) {
const revision = content.Key.split('/')[1].trim();
chromiumRevisions.set(revision, content.LastModified);
}
}
// 最多遍历 100 次防止内存爆炸,下次同步任务会继续
} while (i++ < MAX_DEPTH && marker !== undefined);
}
for (const [ revision, date ] of chromiumRevisions.entries()) {
for (const [revision, date] of chromiumRevisions.entries()) {
// https://github.com/puppeteer/puppeteer/blob/eebf452d38b79bb2ea1a1ba84c3d2ea6f2f9f899/src/node/BrowserFetcher.ts#L40
// chrome: {
// linux: '%s/chromium-browser-snapshots/Linux_x64/%d/%s.zip',
@@ -105,7 +107,7 @@ export class PuppeteerBinary extends AbstractBinary {
size: '-',
isDir: false,
url: `https://storage.googleapis.com/chromium-browser-snapshots/${platform}/${revision}/${name}`,
ignoreDownloadStatuses: [ 404 ],
ignoreDownloadStatuses: [404],
},
];
}
@@ -116,15 +118,14 @@ export class PuppeteerBinary extends AbstractBinary {
}
// https://github.com/puppeteer/puppeteer/blob/eebf452d38b79bb2ea1a1ba84c3d2ea6f2f9f899/src/node/BrowserFetcher.ts#L72
private archiveName(
platform: string,
revision: string,
): string {
private archiveName(platform: string, revision: string): string {
if (platform === 'Linux_x64') return 'chrome-linux';
if (platform === 'Mac' || platform === 'Mac_Arm') return 'chrome-mac';
if (platform === 'Win' || platform === 'Win_x64') {
// Windows archive name changed at r591479.
return parseInt(revision, 10) > 591479 ? 'chrome-win' : 'chrome-win32';
return Number.parseInt(revision, 10) > 591_479
? 'chrome-win'
: 'chrome-win32';
}
return '';
}

View File

@@ -1,79 +1,92 @@
import { AbstractBinary, FetchResult, BinaryItem } from './AbstractBinary';
import { SingletonProto } from 'egg';
import { BinaryType } from '../../enum/Binary.ts';
import {
AbstractBinary,
BinaryAdapter,
type BinaryItem,
type FetchResult,
} from './AbstractBinary.ts';
@SingletonProto()
@BinaryAdapter(BinaryType.Sqlcipher)
export class SqlcipherBinary extends AbstractBinary {
private dirItems: {
[key: string]: BinaryItem[];
};
async initFetch() {
// do nothing
return;
}
async fetch(dir: string): Promise<FetchResult | undefined> {
if (!this.dirItems) {
this.dirItems = {};
const s3Url = 'https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher';
const pkgUrl = 'https://registry.npmjs.com/@journeyapps/sqlcipher';
const data = await this.requestJSON(pkgUrl);
this.dirItems = {};
this.dirItems['/'] = [];
// https://github.com/journeyapps/node-sqlcipher/blob/master/.circleci/config.yml#L407
// https://github.com/journeyapps/node-sqlcipher/issues/35#issuecomment-698924173
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v3-darwin-arm64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v6-darwin-arm64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v3-darwin-x64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v6-darwin-x64.tar.gz
const dirItems: {
[key: string]: BinaryItem[];
} = {
'/': [],
};
const s3Url =
'https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher';
const pkgUrl = 'https://registry.npmjs.com/@journeyapps/sqlcipher';
const data = await this.requestJSON(pkgUrl);
// https://github.com/journeyapps/node-sqlcipher/blob/master/.circleci/config.yml#L407
// https://github.com/journeyapps/node-sqlcipher/issues/35#issuecomment-698924173
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v3-darwin-arm64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v6-darwin-arm64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v3-darwin-x64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v6-darwin-x64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v6-linux-x64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v3-linux-x64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v6-linux-x64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v3-linux-x64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v6-win32-arm64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v3-win32-arm64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v3-win32-ia32.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v6-win32-ia32.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v3-win32-x64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v6-win32-x64.tar.gz
const nodePlatformAndArchs = [
'linux-x64',
'darwin-x64',
'darwin-arm64',
'win32-x64',
'win32-arm64',
'win32-ia32',
];
for (const version in data.versions) {
const major = parseInt(version.split('.', 1)[0]);
if (major < 5) continue;
// >= 5.0.0
const pkgVersion = data.versions[version];
const napiVersions = pkgVersion.binary && pkgVersion.binary.napi_versions || [];
const date = data.time[version];
this.dirItems['/'].push({
name: `v${version}/`,
date,
size: '-',
isDir: true,
url: '',
});
const versionDir = `/v${version}/`;
this.dirItems[versionDir] = [];
for (const nodePlatformAndArch of nodePlatformAndArchs) {
// napi
for (const napiVersion of napiVersions) {
// >= 5.0.0
// "package_name": "napi-v{napi_build_version}-{platform}-{arch}.tar.gz",
// "napi_versions": [
// 3, 6
// ]
const name = `napi-v${napiVersion}-${nodePlatformAndArch}.tar.gz`;
this.dirItems[versionDir].push({
name,
date,
size: '-',
isDir: false,
url: `${s3Url}/v${version}/${name}`,
ignoreDownloadStatuses: [ 404, 403 ],
});
}
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v6-win32-arm64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v3-win32-arm64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v3-win32-ia32.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v6-win32-ia32.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v3-win32-x64.tar.gz
// https://journeyapps-node-binary.s3.amazonaws.com/@journeyapps/sqlcipher/v5.3.0/napi-v6-win32-x64.tar.gz
const nodePlatformAndArchs = [
'linux-x64',
'darwin-x64',
'darwin-arm64',
'win32-x64',
'win32-arm64',
'win32-ia32',
];
for (const version in data.versions) {
const major = Number.parseInt(version.split('.', 1)[0]);
if (major < 5) continue;
// >= 5.0.0
const pkgVersion = data.versions[version];
const napiVersions =
(pkgVersion.binary && pkgVersion.binary.napi_versions) || [];
const date = data.time[version];
dirItems['/'].push({
name: `v${version}/`,
date,
size: '-',
isDir: true,
url: '',
});
const versionDir = `/v${version}/`;
dirItems[versionDir] = [];
for (const nodePlatformAndArch of nodePlatformAndArchs) {
// napi
for (const napiVersion of napiVersions) {
// >= 5.0.0
// "package_name": "napi-v{napi_build_version}-{platform}-{arch}.tar.gz",
// "napi_versions": [
// 3, 6
// ]
const name = `napi-v${napiVersion}-${nodePlatformAndArch}.tar.gz`;
dirItems[versionDir].push({
name,
date,
size: '-',
isDir: false,
url: `${s3Url}/v${version}/${name}`,
ignoreDownloadStatuses: [404, 403],
});
}
}
}
return { items: this.dirItems[dir] };
return { items: dirItems[dir] };
}
}

View File

@@ -1,32 +1,38 @@
import {
ImplDecorator,
Inject,
QualifierImplDecoratorUtil,
} from '@eggjs/tegg';
import { RegistryType } from '../../../common/enum/Registry';
import { Registry } from '../../../core/entity/Registry';
import {
EggHttpClient,
EggLogger,
type ImplDecorator,
Logger,
HttpClient,
} from 'egg';
import type { RegistryType } from '../../../common/enum/Registry.ts';
import type { Registry } from '../../../core/entity/Registry.ts';
export const CHANGE_STREAM_ATTRIBUTE = 'CHANGE_STREAM_ATTRIBUTE';
export type ChangesStreamChange = {
export interface ChangesStreamChange {
seq: string;
fullname: string;
};
}
export abstract class AbstractChangeStream {
@Inject()
protected logger: EggLogger;
protected logger: Logger;
@Inject()
protected httpclient: EggHttpClient;
protected httpClient: HttpClient;
abstract getInitialSince(registry: Registry): Promise<string>;
abstract fetchChanges(registry: Registry, since: string): AsyncGenerator<ChangesStreamChange>;
abstract fetchChanges(
registry: Registry,
since: string
): AsyncGenerator<ChangesStreamChange>;
getChangesStreamUrl(registry: Registry, since: string, limit?: number): string {
getChangesStreamUrl(
registry: Registry,
since: string,
limit?: number
): string {
const url = new URL(registry.changeStream);
url.searchParams.set('since', since);
if (limit) {
@@ -36,5 +42,10 @@ export abstract class AbstractChangeStream {
}
}
export const RegistryChangesStream: ImplDecorator<AbstractChangeStream, typeof RegistryType> =
QualifierImplDecoratorUtil.generatorDecorator(AbstractChangeStream, CHANGE_STREAM_ATTRIBUTE);
export const RegistryChangesStream: ImplDecorator<
AbstractChangeStream,
typeof RegistryType
> = QualifierImplDecoratorUtil.generatorDecorator(
AbstractChangeStream,
CHANGE_STREAM_ATTRIBUTE
);

View File

@@ -1,35 +1,43 @@
import { ContextProto } from '@eggjs/tegg';
import { RegistryType } from '../../../common/enum/Registry';
import { Registry } from '../../../core/entity/Registry';
import { E500 } from 'egg-errors';
import { AbstractChangeStream, RegistryChangesStream } from './AbstractChangesStream';
import { SingletonProto } from 'egg';
import { E500 } from 'egg/errors';
@ContextProto()
import { RegistryType } from '../../../common/enum/Registry.ts';
import type { Registry } from '../../../core/entity/Registry.ts';
import {
AbstractChangeStream,
RegistryChangesStream,
} from './AbstractChangesStream.ts';
@SingletonProto()
@RegistryChangesStream(RegistryType.Cnpmcore)
export class CnpmcoreChangesStream extends AbstractChangeStream {
async getInitialSince(registry: Registry): Promise<string> {
const db = (new URL(registry.changeStream)).origin;
const { status, data } = await this.httpclient.request(db, {
const db = new URL(registry.changeStream).origin;
const { status, data } = await this.httpClient.request(db, {
followRedirect: true,
timeout: 10000,
timeout: 10_000,
dataType: 'json',
});
if (!data.update_seq) {
throw new E500(`get getInitialSince failed: ${data.update_seq}`);
}
const since = String(data.update_seq - 10);
this.logger.warn('[NpmChangesStream.getInitialSince:firstSeq] GET %s status: %s, data: %j, since: %s',
registry.name, status, data, since);
this.logger.warn(
'[NpmChangesStream.getInitialSince:firstSeq] GET %s status: %s, data: %j, since: %s',
registry.name,
status,
data,
since
);
return since;
}
async* fetchChanges(registry: Registry, since: string) {
async *fetchChanges(registry: Registry, since: string) {
const db = this.getChangesStreamUrl(registry, since);
// json mode
const { data } = await this.httpclient.request(db, {
const { data } = await this.httpClient.request(db, {
followRedirect: true,
timeout: 30000,
timeout: 30_000,
dataType: 'json',
gzip: true,
});

View File

@@ -1,40 +1,61 @@
import { ContextProto } from '@eggjs/tegg';
import { RegistryType } from '../../../common/enum/Registry';
import { Registry } from '../../../core/entity/Registry';
import { E500 } from 'egg-errors';
import { AbstractChangeStream, RegistryChangesStream } from './AbstractChangesStream';
import { SingletonProto } from 'egg';
import { E500 } from 'egg/errors';
const MAX_LIMIT = 10000;
import { RegistryType } from '../../../common/enum/Registry.ts';
import type { Registry } from '../../../core/entity/Registry.ts';
import {
AbstractChangeStream,
RegistryChangesStream,
} from './AbstractChangesStream.ts';
@ContextProto()
const MAX_LIMIT = 10_000;
interface FetchResults {
results: {
seq: number;
type: string;
id: string;
changes: Record<string, string>[];
gmt_modified: Date;
}[];
}
@SingletonProto()
@RegistryChangesStream(RegistryType.Cnpmjsorg)
export class CnpmjsorgChangesStream extends AbstractChangeStream {
// cnpmjsorg 未实现 update_seq 字段
// 默认返回当前时间戳字符串
async getInitialSince(registry: Registry): Promise<string> {
const since = String((new Date()).getTime());
this.logger.warn(`[CnpmjsorgChangesStream.getInitialSince] since: ${since}, skip query ${registry.changeStream}`);
const since = String(Date.now());
this.logger.warn(
`[CnpmjsorgChangesStream.getInitialSince] since: ${since}, skip query ${registry.changeStream}`
);
return since;
}
private async tryFetch(registry: Registry, since: string, limit = 1000) {
private async tryFetch(
registry: Registry,
since: string,
limit = 1000
): Promise<{ data: FetchResults }> {
if (limit > MAX_LIMIT) {
throw new E500(`limit too large, current since: ${since}, limit: ${limit}`);
throw new E500(
`limit too large, current since: ${since}, limit: ${limit}`
);
}
const db = this.getChangesStreamUrl(registry, since, limit);
// json mode
const res = await this.httpclient.request(db, {
const res = await this.httpClient.request<FetchResults>(db, {
followRedirect: true,
timeout: 30000,
timeout: 30_000,
dataType: 'json',
gzip: true,
});
const { results = [] } = res.data;
if (results?.length >= limit) {
const [ first ] = results;
const [first] = results;
const last = results[results.length - 1];
if (first.gmt_modified === last.gmt_modified) {
if (first.gmt_modified === last?.gmt_modified) {
return await this.tryFetch(registry, since, limit + 1000);
}
}
@@ -42,7 +63,7 @@ export class CnpmjsorgChangesStream extends AbstractChangeStream {
return res;
}
async* fetchChanges(registry: Registry, since: string) {
async *fetchChanges(registry: Registry, since: string) {
// ref: https://github.com/cnpm/cnpmjs.org/pull/1734
// 由于 cnpmjsorg 无法计算准确的 seq
// since 是一个时间戳,需要确保一次返回的结果中首尾两个 gmtModified 不相等
@@ -50,7 +71,7 @@ export class CnpmjsorgChangesStream extends AbstractChangeStream {
if (data.results?.length > 0) {
for (const change of data.results) {
const seq = new Date(change.gmt_modified).getTime() + '';
const seq = `${new Date(change.gmt_modified).getTime()}`;
const fullname = change.id;
if (seq && fullname && seq !== since) {
const change = {

View File

@@ -1,55 +1,84 @@
import { ContextProto } from '@eggjs/tegg';
import { E500 } from 'egg-errors';
import { RegistryType } from '../../../common/enum/Registry';
import { Registry } from '../../../core/entity/Registry';
import { AbstractChangeStream, ChangesStreamChange, RegistryChangesStream } from './AbstractChangesStream';
import { SingletonProto } from 'egg';
import { E500 } from 'egg/errors';
@ContextProto()
import { RegistryType } from '../../../common/enum/Registry.ts';
import type { Registry } from '../../../core/entity/Registry.ts';
import {
AbstractChangeStream,
RegistryChangesStream,
type ChangesStreamChange,
} from './AbstractChangesStream.ts';
@SingletonProto()
@RegistryChangesStream(RegistryType.Npm)
export class NpmChangesStream extends AbstractChangeStream {
async getInitialSince(registry: Registry): Promise<string> {
const db = (new URL(registry.changeStream)).origin;
const { status, data } = await this.httpclient.request(db, {
const db = new URL(registry.changeStream).origin;
const { status, data } = await this.httpClient.request(db, {
followRedirect: true,
timeout: 10000,
timeout: 10_000,
dataType: 'json',
headers: {
'npm-replication-opt-in': 'true',
},
});
const since = String(data.update_seq - 10);
if (!data.update_seq) {
throw new E500(`get getInitialSince failed: ${data.update_seq}`);
}
this.logger.warn('[NpmChangesStream.getInitialSince] GET %s status: %s, data: %j, since: %s',
registry.name, registry.changeStream, status, data, since);
this.logger.warn(
'[NpmChangesStream.getInitialSince] GET %s status: %s, data: %j, since: %s',
registry.name,
registry.changeStream,
status,
data,
since
);
return since;
}
async* fetchChanges(registry: Registry, since: string) {
async *fetchChanges(
registry: Registry,
since: string
): AsyncGenerator<ChangesStreamChange> {
// https://github.com/orgs/community/discussions/152515
const db = this.getChangesStreamUrl(registry, since);
const { res } = await this.httpclient.request(db, {
streaming: true,
timeout: 10000,
const { data, headers } = await this.httpClient.request(db, {
timeout: 60_000,
headers: {
'npm-replication-opt-in': 'true',
},
dataType: 'json',
gzip: true,
});
const count = data.results?.length;
const last_seq = data.last_seq;
this.logger.info(
'[NpmChangesStream.fetchChanges] %s, count: %s, last_seq: %s, headers: %j',
db,
count,
last_seq,
headers
);
let buf = '';
for await (const chunk of res) {
const text = chunk.toString();
const lines = text.split('\n');
for (const line of lines) {
const content = buf + line;
const match = /"seq":(\d+),"id":"([^"]+)"/g.exec(content);
const seq = match?.[1];
const fullname = match?.[2];
if (seq && fullname) {
buf = '';
const change: ChangesStreamChange = { fullname, seq };
if (data.results?.length > 0) {
for (const change of data.results) {
// {
// seq: 2495018,
// id: 'ng-create-all-project',
// changes: [ { rev: '3-be3a014aab8e379ba28a28adb8e10142' }, [length]: 1 ],
// deleted: true
// },
const seq = String(change.seq);
const fullname = change.id;
if (seq && fullname && seq !== since) {
const change = {
fullname,
seq,
};
yield change;
} else {
buf += line;
}
}
}
}
}

View File

@@ -1,27 +1,34 @@
import { performance } from 'perf_hooks';
import { Advice, AdviceContext, IAdvice } from '@eggjs/tegg/aop';
import { Inject } from '@eggjs/tegg';
import { EggLogger } from 'egg';
import { performance } from 'node:perf_hooks';
import { Advice, type AdviceContext, type IAdvice } from 'egg/aop';
import { Inject, Logger } from 'egg';
const START = Symbol('AsyncTimer#start');
const SUCCEED = Symbol('AsyncTimer#succeed');
// auto print async function call performance timer log into logger
@Advice()
export class AsyncTimer implements IAdvice {
@Inject()
private readonly logger: EggLogger;
private start: number;
private succeed = true;
private readonly logger: Logger;
async beforeCall() {
this.start = performance.now();
async beforeCall(ctx: AdviceContext) {
ctx.set(START, performance.now());
ctx.set(SUCCEED, true);
}
async afterThrow() {
this.succeed = false;
async afterThrow(ctx: AdviceContext) {
ctx.set(SUCCEED, false);
}
async afterFinally(ctx: AdviceContext) {
const ms = Math.floor((performance.now() - this.start) * 1000) / 1000;
this.logger.info('[%s] [%s:%s|%s]',
ms, ctx.that.constructor.name, ctx.method, this.succeed ? 'T' : 'F');
const ms = Math.floor((performance.now() - ctx.get(START)) * 1000) / 1000;
this.logger.info(
'[%s] [%s:%s|%s]',
ms,
ctx.that.constructor.name,
ctx.method,
ctx.get(SUCCEED) ? 'T' : 'F'
);
}
}

View File

@@ -1,2 +1,33 @@
export const BUG_VERSIONS = 'bug-versions';
export const LATEST_TAG = 'latest';
export const GLOBAL_WORKER = 'GLOBAL_WORKER';
export const PROXY_CACHE_DIR_NAME = 'proxy-cache-packages';
export const ABBREVIATED_META_TYPE = 'application/vnd.npm.install-v1+json';
export const NOT_IMPLEMENTED_PATH = [ '/-/npm/v1/security/audits/quick', '/-/npm/v1/security/advisories/bulk' ];
export enum SyncMode {
none = 'none',
admin = 'admin',
proxy = 'proxy',
exist = 'exist',
all = 'all',
}
export enum ChangesStreamMode {
json = 'json',
streaming = 'streaming',
}
export enum SyncDeleteMode {
ignore = 'ignore',
block = 'block',
delete = 'delete',
}
export enum PresetRegistryName {
default = 'default',
self = 'self',
}
export enum PackageAccessLevel {
write = 'write',
read = 'read',
}

View File

@@ -1,5 +1,5 @@
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import customParseFormat from 'dayjs/plugin/customParseFormat.js';
dayjs.extend(customParseFormat);
export default dayjs;

18
app/common/enum/Binary.ts Normal file
View File

@@ -0,0 +1,18 @@
export enum BinaryType {
Api = 'api',
Bucket = 'bucket',
Cypress = 'cypress',
Electron = 'electron',
Firefox = 'firefox',
GitHub = 'github',
Imagemin = 'imagemin',
Node = 'node',
NodePreGyp = 'nodePreGyp',
Nwjs = 'nwjs',
Playwright = 'playwright',
Puppeteer = 'puppeteer',
Prisma = 'prisma',
Sqlcipher = 'sqlcipher',
ChromeForTesting = 'chromeForTesting',
Edgedriver = 'edgedriver',
}

View File

@@ -2,6 +2,7 @@ export enum TaskType {
SyncPackage = 'sync_package',
ChangesStream = 'changes_stream',
SyncBinary = 'sync_binary',
UpdateProxyCache = 'update_proxy_cache',
CreateHook = 'create_hook',
TriggerHook = 'trigger_hook',
}

4
app/common/enum/Total.ts Normal file
View File

@@ -0,0 +1,4 @@
export enum TotalType {
PackageCount = 'packageCount',
PackageVersionCount = 'packageVersionCount',
}

View File

@@ -1,5 +1,11 @@
export enum LoginResultCode {
UserNotFound,
Success,
Fail,
UserNotFound = 0,
Success = 1,
Fail = 2,
}
export enum WanStatusCode {
UserNotFound = 0,
Unbound = 1,
Bound = 2,
}

View File

@@ -2,5 +2,6 @@
"name": "cnpmcore-common",
"eggModule": {
"name": "cnpmcoreCommon"
}
},
"type": "module"
}

View File

@@ -1,5 +1,9 @@
import { Readable } from 'stream';
import { IncomingHttpHeaders } from 'http';
import type { Readable } from 'node:stream';
import type { IncomingHttpHeaders } from 'node:http';
import type { Context } from 'egg';
import type { estypes } from '@elastic/elasticsearch';
import type { CnpmcoreConfig } from '../port/config.ts';
export interface UploadResult {
key: string;
@@ -16,8 +20,12 @@ export interface UploadOptions {
export interface AppendOptions {
key: string;
position?: string,
headers?: IncomingHttpHeaders,
position?: string;
headers?: IncomingHttpHeaders;
}
export interface DownloadOptions {
timeout: number;
}
export interface NFSClient {
@@ -33,6 +41,12 @@ export interface NFSClient {
createDownloadStream(key: string): Promise<Readable | undefined>;
download(
key: string,
filepath: string,
options: DownloadOptions
): Promise<void>;
url?(key: string): string;
}
@@ -41,3 +55,30 @@ export interface QueueAdapter {
pop<T>(key: string): Promise<T | null>;
length(key: string): Promise<number>;
}
export interface SearchAdapter {
// oxlint-disable-next-line typescript-eslint/no-explicit-any
search<T>(query: any): Promise<estypes.SearchHitsMetadata<T>>;
upsert<T>(id: string, document: T): Promise<string>;
delete(id: string): Promise<string>;
}
export interface AuthUrlResult {
loginUrl: string;
doneUrl: string;
}
export interface userResult {
name: string;
email: string;
}
export interface AuthClient {
getAuthUrl(ctx: Context): Promise<AuthUrlResult>;
ensureCurrentUser(): Promise<userResult | null>;
}
declare module 'egg' {
interface EggAppConfig {
cnpmcore: CnpmcoreConfig;
}
}

View File

@@ -1,5 +1,5 @@
import { Entity, EntityData } from './Entity';
import { EasyData, EntityUtil } from '../util/EntityUtil';
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
interface BinaryData extends EntityData {
binaryId: string;
@@ -21,7 +21,7 @@ export class Binary extends Entity {
isDir: boolean;
size: number;
date: string;
sourceUrl?: string;
sourceUrl: string;
ignoreDownloadStatuses?: number[];
constructor(data: BinaryData) {

View File

@@ -8,15 +8,15 @@ export type BugVersionPackages = Record<string, BugVersionPackage>;
export class BugVersion {
private readonly data: BugVersionPackages;
constructor(data) {
constructor(data: BugVersionPackages) {
this.data = data;
}
listAllPackagesHasBugs(): Array<string> {
listAllPackagesHasBugs(): string[] {
return Object.keys(this.data);
}
listBugVersions(pkgName: string): Array<string> {
listBugVersions(pkgName: string): string[] {
const bugVersionPackage = this.data[pkgName];
if (!bugVersionPackage) {
return [];
@@ -31,18 +31,24 @@ export class BugVersion {
}
// TODO manifest typing
// oxlint-disable-next-line typescript-eslint/no-explicit-any
fixManifest(bugVersionManifest: any, fixVersionManifest: any): any {
// If the tarball is same, manifest has fixed.
if (bugVersionManifest.dist.tarball === fixVersionManifest.dist.tarball) {
return;
}
const advice = this.fixVersion(bugVersionManifest.name, bugVersionManifest.version);
const advice = this.fixVersion(
bugVersionManifest.name,
bugVersionManifest.version
);
if (!advice) {
return;
}
const newManifest = JSON.parse(JSON.stringify(fixVersionManifest));
const newManifest = structuredClone(fixVersionManifest);
const hotfixDeprecated = `[WARNING] Use ${advice.version} instead of ${bugVersionManifest.version}, reason: ${advice.reason}`;
newManifest.deprecated = bugVersionManifest.deprecated ? `${bugVersionManifest.deprecated} (${hotfixDeprecated})` : hotfixDeprecated;
newManifest.deprecated = bugVersionManifest.deprecated
? `${bugVersionManifest.deprecated} (${hotfixDeprecated})`
: hotfixDeprecated;
// don't change version
newManifest.version = bugVersionManifest.version;
return newManifest;

View File

@@ -1,10 +1,11 @@
import { Entity, EntityData } from './Entity';
import { EasyData, EntityUtil } from '../util/EntityUtil';
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
interface ChangeData extends EntityData {
changeId: string;
type: string;
targetName: string;
// oxlint-disable-next-line typescript-eslint/no-explicit-any
data: any;
}
@@ -12,6 +13,7 @@ export class Change extends Entity {
changeId: string;
type: string;
targetName: string;
// oxlint-disable-next-line typescript-eslint/no-explicit-any
data: any;
constructor(data: ChangeData) {

View File

@@ -1,5 +1,5 @@
import { Entity, EntityData } from './Entity';
import { EasyData, EntityUtil } from '../util/EntityUtil';
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
interface DistData extends EntityData {
distId: string;

View File

@@ -1,9 +1,13 @@
import { Entity, EntityData } from './Entity';
import { EasyData, EntityUtil } from '../util/EntityUtil';
import { HookType } from '../../common/enum/Hook';
import crypto from 'crypto';
import crypto from 'node:crypto';
export type CreateHookData = Omit<EasyData<HookData, 'hookId'>, 'enable' | 'latestTaskId'>;
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
import type { HookType } from '../../common/enum/Hook.ts';
export type CreateHookData = Omit<
EasyData<HookData, 'hookId'>,
'enable' | 'latestTaskId'
>;
export interface HookData extends EntityData {
hookId: string;
@@ -39,18 +43,20 @@ export class Hook extends Entity {
}
static create(data: CreateHookData): Hook {
const hookData: EasyData<HookData, 'hookId'> = Object.assign({}, data, {
const hookData: EasyData<HookData, 'hookId'> = {
...data,
enable: true,
latestTaskId: undefined,
});
};
const newData = EntityUtil.defaultData(hookData, 'hookId');
return new Hook(newData);
}
// payload 可能会特别大,如果做多次 stringify 浪费太多 cpu
signPayload(payload: object): { digest, payloadStr } {
signPayload(payload: object) {
const payloadStr = JSON.stringify(payload);
const digest = crypto.createHmac('sha256', this.secret)
const digest = crypto
.createHmac('sha256', this.secret)
.update(JSON.stringify(payload))
.digest('hex');
return {

View File

@@ -1,4 +1,4 @@
import { HookEventType } from '../../common/enum/Hook';
import { HookEventType } from '../../common/enum/Hook.ts';
export interface PublishChangePayload {
'dist-tag'?: string;

View File

@@ -1,7 +1,7 @@
import { Entity, EntityData } from './Entity';
import { EasyData, EntityUtil } from '../util/EntityUtil';
import { Dist } from './Dist';
import { getFullname } from '../../common/PackageUtil';
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
import { Dist } from './Dist.ts';
import { getFullname } from '../../common/PackageUtil.ts';
interface PackageData extends EntityData {
scope: string;
@@ -22,6 +22,13 @@ export enum DIST_NAMES {
ABBREVIATED_MANIFESTS = 'abbreviated_manifests.json',
}
export function isPkgManifest(fileType: DIST_NAMES) {
return (
fileType === DIST_NAMES.FULL_MANIFESTS ||
fileType === DIST_NAMES.ABBREVIATED_MANIFESTS
);
}
interface FileInfo {
size: number;
shasum: string;
@@ -84,6 +91,11 @@ export class Package extends Entity {
return this.createDist(DIST_NAMES.ABBREVIATED_MANIFESTS, info);
}
createPackageVersionFile(path: string, version: string, info: FileInfo) {
// path should starts with `/`, e.g.: '/foo/bar/index.js'
return this.createDist(`files${path}`, info, version);
}
private distDir(filename: string, version?: string) {
if (version) {
return `/packages/${this.fullname}/${version}/${filename}`;

View File

@@ -1,5 +1,5 @@
import { Entity, EntityData } from './Entity';
import { EasyData, EntityUtil } from '../util/EntityUtil';
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
interface PackageTagData extends EntityData {
packageId: string;

View File

@@ -1,6 +1,7 @@
import { Dist } from './Dist';
import { Entity, EntityData } from './Entity';
import { EasyData, EntityUtil } from '../util/EntityUtil';
import type { Dist } from './Dist.ts';
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
import { PaddingSemVer } from './PaddingSemVer.ts';
interface PackageVersionData extends EntityData {
packageId: string;
@@ -11,6 +12,8 @@ interface PackageVersionData extends EntityData {
tarDist: Dist;
readmeDist: Dist;
publishTime: Date;
paddingVersion?: string | null;
isPreRelease?: boolean | null;
}
export class PackageVersion extends Entity {
@@ -22,6 +25,8 @@ export class PackageVersion extends Entity {
tarDist: Dist;
readmeDist: Dist;
publishTime: Date;
paddingVersion: string;
isPreRelease: boolean;
constructor(data: PackageVersionData) {
super(data);
@@ -33,9 +38,19 @@ export class PackageVersion extends Entity {
this.tarDist = data.tarDist;
this.readmeDist = data.readmeDist;
this.publishTime = data.publishTime;
if (data.paddingVersion && typeof data.isPreRelease === 'boolean') {
this.paddingVersion = data.paddingVersion;
this.isPreRelease = data.isPreRelease;
} else {
const paddingSemVer = new PaddingSemVer(this.version);
this.paddingVersion = paddingSemVer.paddingVersion;
this.isPreRelease = paddingSemVer.isPreRelease;
}
}
static create(data: EasyData<PackageVersionData, 'packageVersionId'>): PackageVersion {
static create(
data: EasyData<PackageVersionData, 'packageVersionId'>
): PackageVersion {
const newData = EntityUtil.defaultData(data, 'packageVersionId');
return new PackageVersion(newData);
}

View File

@@ -1,5 +1,5 @@
import { Entity, EntityData } from './Entity';
import { EasyData, EntityUtil } from '../util/EntityUtil';
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
interface PackageVersionBlockData extends EntityData {
packageVersionBlockId: string;
@@ -22,7 +22,9 @@ export class PackageVersionBlock extends Entity {
this.reason = data.reason;
}
static create(data: EasyData<PackageVersionBlockData, 'packageVersionBlockId'>): PackageVersionBlock {
static create(
data: EasyData<PackageVersionBlockData, 'packageVersionBlockId'>
): PackageVersionBlock {
const newData = EntityUtil.defaultData(data, 'packageVersionBlockId');
return new PackageVersionBlock(newData);
}

View File

@@ -0,0 +1,47 @@
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
import type { Dist } from './Dist.ts';
interface PackageVersionFileData extends EntityData {
packageVersionFileId: string;
packageVersionId: string;
dist: Dist;
directory: string;
name: string;
contentType: string;
mtime: Date;
}
export class PackageVersionFile extends Entity {
packageVersionFileId: string;
packageVersionId: string;
dist: Dist;
directory: string;
name: string;
contentType: string;
mtime: Date;
constructor(data: PackageVersionFileData) {
super(data);
this.packageVersionFileId = data.packageVersionFileId;
this.packageVersionId = data.packageVersionId;
this.dist = data.dist;
this.directory = data.directory;
this.name = data.name;
this.contentType = data.contentType;
this.mtime = data.mtime;
}
get path() {
return this.directory === '/'
? `/${this.name}`
: `${this.directory}/${this.name}`;
}
static create(
data: EasyData<PackageVersionFileData, 'packageVersionFileId'>
): PackageVersionFile {
const newData = EntityUtil.defaultData(data, 'packageVersionFileId');
return new PackageVersionFile(newData);
}
}

View File

@@ -1,10 +1,11 @@
import { Entity, EntityData } from './Entity';
import { EasyData, EntityUtil } from '../util/EntityUtil';
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
interface PackageVersionManifestData extends EntityData {
packageId: string;
packageVersionId: string;
packageVersionManifestId: string;
// oxlint-disable-next-line typescript-eslint/no-explicit-any
manifest: any;
}
@@ -12,6 +13,7 @@ export class PackageVersionManifest extends Entity {
packageId: string;
packageVersionId: string;
packageVersionManifestId: string;
// oxlint-disable-next-line typescript-eslint/no-explicit-any
manifest: any;
constructor(data: PackageVersionManifestData) {
@@ -22,7 +24,9 @@ export class PackageVersionManifest extends Entity {
this.manifest = data.manifest;
}
static create(data: EasyData<PackageVersionManifestData, 'packageVersionManifestId'>): PackageVersionManifest {
static create(
data: EasyData<PackageVersionManifestData, 'packageVersionManifestId'>
): PackageVersionManifest {
const newData = EntityUtil.defaultData(data, 'packageVersionManifestId');
return new PackageVersionManifest(newData);
}

View File

@@ -0,0 +1,53 @@
import { SemVer, valid } from 'semver';
export class PaddingSemVer {
private readonly semver: SemVer;
// 跳过 semver 中的 buildInfo, buildInfo 不参与版本比较
private _paddingVersion: string;
readonly isPreRelease: boolean;
constructor(semver: string | SemVer) {
// ignore invalid version, e.g.: '1000000000000000000.0.0' on https://registry.npmjs.com/latentflip-test
if (!valid(semver)) {
this.isPreRelease = true;
this._paddingVersion = PaddingSemVer.anyVersion();
return;
}
this.semver = new SemVer(semver);
// @ts-expect-error type definition is not correct
if (this.semver.includePrerelease) {
this.isPreRelease = true;
} else if (this.semver.prerelease && this.semver.prerelease.length > 0) {
this.isPreRelease = true;
} else {
this.isPreRelease = false;
}
}
get paddingVersion(): string {
if (!this._paddingVersion) {
this._paddingVersion =
PaddingSemVer.paddingVersion(this.semver.major) +
PaddingSemVer.paddingVersion(this.semver.minor) +
PaddingSemVer.paddingVersion(this.semver.patch);
}
return this._paddingVersion;
}
// 版本信息中为纯数字, JS 中支持的最大整型为 16 位
// 因此填充成 16 位对齐,如果版本号超过 16 位,则抛出异常
static paddingVersion(v: number) {
const t = String(v);
if (t.length <= 16) {
const padding = Array.from({ length: 16 - t.length })
.fill(0)
.join('');
return padding + t;
}
throw new Error(`v ${v} too long`);
}
static anyVersion() {
return '000000000000000000000000000000000000000000000000';
}
}

View File

@@ -0,0 +1,43 @@
import { Entity, type EntityData } from './Entity.ts';
import { isPkgManifest, type DIST_NAMES } from './Package.ts';
import type { EasyData } from '../util/EntityUtil.ts';
import { PROXY_CACHE_DIR_NAME } from '../../common/constants.ts';
interface ProxyCacheData extends EntityData {
fullname: string;
fileType: DIST_NAMES;
version?: string;
}
export type CreateProxyCacheData = Omit<
EasyData<ProxyCacheData, 'id'>,
'id' | 'filePath'
>;
export class ProxyCache extends Entity {
readonly fullname: string;
readonly fileType: DIST_NAMES;
readonly filePath: string;
readonly version?: string;
constructor(data: ProxyCacheData) {
super(data);
this.fullname = data.fullname;
this.fileType = data.fileType;
this.version = data.version;
if (isPkgManifest(data.fileType)) {
this.filePath = `/${PROXY_CACHE_DIR_NAME}/${data.fullname}/${data.fileType}`;
} else {
this.filePath = `/${PROXY_CACHE_DIR_NAME}/${data.fullname}/${data.version}/${data.fileType}`;
}
}
public static create(data: CreateProxyCacheData): ProxyCache {
const newData = { ...data, createdAt: new Date(), updatedAt: new Date() };
return new ProxyCache(newData);
}
public static update(data: ProxyCache): ProxyCache {
data.updatedAt = new Date();
return data;
}
}

View File

@@ -1,6 +1,6 @@
import { Entity, EntityData } from './Entity';
import { EasyData, EntityUtil } from '../util/EntityUtil';
import type { RegistryType } from '../../common/enum/Registry';
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
import type { RegistryType } from '../../common/enum/Registry.ts';
interface RegistryData extends EntityData {
name: string;
@@ -9,9 +9,13 @@ interface RegistryData extends EntityData {
changeStream: string;
userPrefix: string;
type: RegistryType;
authToken?: string;
}
export type CreateRegistryData = Omit<EasyData<RegistryData, 'registryId'>, 'id'>;
export type CreateRegistryData = Omit<
EasyData<RegistryData, 'registryId'>,
'id'
>;
export class Registry extends Entity {
name: string;
@@ -20,6 +24,7 @@ export class Registry extends Entity {
changeStream: string;
userPrefix: string;
type: RegistryType;
authToken?: string;
constructor(data: RegistryData) {
super(data);
@@ -29,10 +34,14 @@ export class Registry extends Entity {
this.changeStream = data.changeStream;
this.userPrefix = data.userPrefix;
this.type = data.type;
this.authToken = data.authToken;
}
public static create(data: CreateRegistryData): Registry {
const newData = EntityUtil.defaultData(data, 'registryId');
const newData = EntityUtil.defaultData<RegistryData, 'registryId'>(
data,
'registryId'
);
return new Registry(newData);
}
}

View File

@@ -1,5 +1,5 @@
import { Entity, EntityData } from './Entity';
import { EasyData, EntityUtil } from '../util/EntityUtil';
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
interface ScopeData extends EntityData {
name: string;

View File

@@ -0,0 +1,84 @@
import { Comparator, Range } from 'semver';
import { PaddingSemVer } from './PaddingSemVer.ts';
const OPERATOR_MAP = {
'<': '$lt',
'<=': '$lte',
'>': '$gt',
'>=': '$gte',
'': '$eq',
};
export class SqlRange {
private readonly range: Range;
private _containPreRelease: boolean;
readonly condition: object;
constructor(range: string | Range) {
this.range = new Range(range);
this._containPreRelease = false;
this.condition = this.generateWhere();
}
private comparatorToSql(comparator: Comparator) {
// @ts-expect-error type definition is not correct
if (comparator.semver === Comparator.ANY) {
return {
$and: [
{
isPreRelease: {
$lte: 0,
},
},
{
paddingVersion: {
$gte: PaddingSemVer.anyVersion(),
},
},
],
};
}
const paddingSemver = new PaddingSemVer(comparator.semver);
const operator =
OPERATOR_MAP[comparator.operator as keyof typeof OPERATOR_MAP];
if (!operator) {
throw new Error(`unknown operator ${comparator.operator}`);
}
this._containPreRelease =
this._containPreRelease || paddingSemver.isPreRelease;
return {
$and: [
{
isPreRelease: {
$lte: paddingSemver.isPreRelease ? 1 : 0,
},
},
{
paddingVersion: {
[operator]: paddingSemver.paddingVersion,
},
},
],
};
}
private comparatorSetToSql(comparatorSet: Comparator[]) {
const condition: object[] = [];
for (const comparator of comparatorSet) {
condition.push(this.comparatorToSql(comparator));
}
return { $and: condition };
}
private generateWhere() {
const conditions: object[] = [];
for (const rangeSet of this.range.set) {
conditions.push(this.comparatorSetToSql(rangeSet as Comparator[]));
}
return { $or: conditions };
}
get containPreRelease(): boolean {
return this._containPreRelease;
}
}

View File

@@ -1,16 +1,22 @@
import os from 'os';
import path from 'path';
import { Entity, EntityData } from './Entity';
import { EasyData, EntityUtil } from '../util/EntityUtil';
import { TaskType, TaskState } from '../../common/enum/Task';
import dayjs from '../../common/dayjs';
import { HookEvent } from './HookEvent';
import os from 'node:os';
import path from 'node:path';
import { InternalServerError } from 'egg/errors';
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
import { TaskState, TaskType } from '../../common/enum/Task.ts';
import { PROXY_CACHE_DIR_NAME } from '../../common/constants.ts';
import dayjs from '../../common/dayjs.ts';
import type { HookEvent } from './HookEvent.ts';
import { isPkgManifest, type DIST_NAMES } from './Package.ts';
export const HOST_NAME = os.hostname();
export const PID = process.pid;
export interface TaskBaseData {
taskWorker: string;
shouldNotMerge?: boolean;
}
export interface TaskData<T = TaskBaseData> extends EntityData {
@@ -28,7 +34,7 @@ export interface TaskData<T = TaskBaseData> extends EntityData {
bizId?: string;
}
export type SyncPackageTaskOptions = {
export interface SyncPackageTaskOptions {
authorId?: string;
authorIp?: string;
tips?: string;
@@ -37,7 +43,14 @@ export type SyncPackageTaskOptions = {
// force sync history version
forceSyncHistory?: boolean;
registryId?: string;
};
specificVersions?: string[];
}
export interface UpdateProxyCacheTaskOptions {
fullname: string;
version?: string;
fileType: DIST_NAMES;
}
export interface CreateHookTaskData extends TaskBaseData {
hookEvent: HookEvent;
@@ -54,14 +67,24 @@ export interface CreateSyncPackageTaskData extends TaskBaseData {
skipDependencies?: boolean;
syncDownloadData?: boolean;
forceSyncHistory?: boolean;
specificVersions?: string[];
}
export interface CreateUpdateProxyCacheTaskData extends TaskBaseData {
fullname: string;
version?: string;
fileType: DIST_NAMES;
filePath: string;
}
export type SyncBinaryTaskData = Record<string, unknown> & TaskBaseData;
export interface ChangesStreamTaskData extends TaskBaseData {
since: string;
last_package?: string,
last_package_created?: Date,
task_count?: number,
registryId?: string,
last_package?: string;
last_package_created?: Date;
task_count?: number;
registryId?: string;
}
export interface TaskUpdateCondition {
@@ -73,6 +96,8 @@ export type CreateHookTask = Task<CreateHookTaskData>;
export type TriggerHookTask = Task<TriggerHookTaskData>;
export type CreateSyncPackageTask = Task<CreateSyncPackageTaskData>;
export type ChangesStreamTask = Task<ChangesStreamTaskData>;
export type CreateUpdateProxyCacheTask = Task<CreateUpdateProxyCacheTaskData>;
export type SyncBinaryTask = Task<SyncBinaryTaskData>;
export class Task<T extends TaskBaseData = TaskBaseData> extends Entity {
taskId: string;
@@ -114,12 +139,17 @@ export class Task<T extends TaskBaseData = TaskBaseData> extends Entity {
this.data.taskWorker = `${HOST_NAME}:${PID}`;
}
private static create<T extends TaskBaseData>(data: EasyData<TaskData<T>, 'taskId'>): Task<T> {
private static create<T extends TaskBaseData>(
data: EasyData<TaskData<T>, 'taskId'>
): Task<T> {
const newData = EntityUtil.defaultData(data, 'taskId');
return new Task(newData);
}
public static createSyncPackage(fullname: string, options?: SyncPackageTaskOptions): CreateSyncPackageTask {
public static createSyncPackage(
fullname: string,
options?: SyncPackageTaskOptions
): CreateSyncPackageTask {
const data = {
type: TaskType.SyncPackage,
state: TaskState.Waiting,
@@ -134,6 +164,7 @@ export class Task<T extends TaskBaseData = TaskBaseData> extends Entity {
skipDependencies: options?.skipDependencies,
syncDownloadData: options?.syncDownloadData,
forceSyncHistory: options?.forceSyncHistory,
specificVersions: options?.specificVersions,
},
};
const task = this.create(data);
@@ -141,7 +172,11 @@ export class Task<T extends TaskBaseData = TaskBaseData> extends Entity {
return task;
}
public static createChangesStream(targetName: string, registryId = '', since = ''): ChangesStreamTask {
public static createChangesStream(
targetName: string,
registryId = '',
since = ''
): ChangesStreamTask {
const data = {
type: TaskType.ChangesStream,
state: TaskState.Waiting,
@@ -189,7 +224,10 @@ export class Task<T extends TaskBaseData = TaskBaseData> extends Entity {
return task;
}
public static createTriggerHookTask(hookEvent: HookEvent, hookId: string): TriggerHookTask {
public static createTriggerHookTask(
hookEvent: HookEvent,
hookId: string
): TriggerHookTask {
const data = {
type: TaskType.TriggerHook,
state: TaskState.Waiting,
@@ -209,13 +247,17 @@ export class Task<T extends TaskBaseData = TaskBaseData> extends Entity {
return task;
}
public static createSyncBinary(targetName: string, lastData: any): Task {
public static createSyncBinary(
targetName: string,
lastData?: Record<string, unknown>
): Task {
const data = {
type: TaskType.SyncBinary,
state: TaskState.Waiting,
targetName,
authorId: `pid_${PID}`,
authorIp: HOST_NAME,
bizId: `SyncBinary:${targetName}`,
data: {
// task execute worker
taskWorker: '',
@@ -227,6 +269,45 @@ export class Task<T extends TaskBaseData = TaskBaseData> extends Entity {
return task;
}
needMergeWhenWaiting(): boolean {
// 历史任务补偿时,将 shouldNotMerge 设置为 true避免合并
// 补偿任务单独执行
if (this.data.shouldNotMerge === true) {
return false;
}
// 仅合并二进制镜像与 npm 包
return [TaskType.SyncBinary, TaskType.SyncPackage].includes(this.type);
}
public static createUpdateProxyCache(
targetName: string,
options: UpdateProxyCacheTaskOptions
): CreateUpdateProxyCacheTask {
if (!isPkgManifest(options.fileType)) {
throw new InternalServerError(
'should not update package version manifest.'
);
}
const filePath = `/${PROXY_CACHE_DIR_NAME}/${options.fullname}/${options.fileType}`;
const data = {
type: TaskType.UpdateProxyCache,
state: TaskState.Waiting,
targetName,
authorId: `pid_${PID}`,
authorIp: HOST_NAME,
data: {
taskWorker: '',
fullname: options.fullname,
version: options?.version,
fileType: options.fileType,
filePath,
},
};
const task = this.create(data);
task.logPath = `/${PROXY_CACHE_DIR_NAME}/${options.fullname}/update-manifest-log/${options.fileType.split('.json')[0]}-${dayjs().format('YYYY/MM/DDHHmm')}-${task.taskId}.log`;
return task;
}
start(): TaskUpdateCondition {
const condition = {
taskId: this.taskId,
@@ -239,8 +320,8 @@ export class Task<T extends TaskBaseData = TaskBaseData> extends Entity {
}
}
export type SyncInfo = {
export interface SyncInfo {
lastSince: string;
taskCount: number;
lastPackage?: string;
};
}

View File

@@ -1,14 +1,41 @@
import { Entity, EntityData } from './Entity';
import { EasyData, EntityUtil } from '../util/EntityUtil';
import dayjs from 'dayjs';
interface TokenData extends EntityData {
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
export enum TokenType {
granular = 'granular',
classic = 'classic',
}
interface BaseTokenData extends EntityData {
tokenId: string;
tokenMark: string;
tokenKey: string;
cidrWhitelist: string[];
cidrWhitelist?: string[];
userId: string;
isReadonly: boolean;
isAutomation: boolean;
isReadonly?: boolean;
type?: TokenType | string;
lastUsedAt?: Date;
}
interface ClassicTokenData extends BaseTokenData {
isAutomation?: boolean;
}
interface GranularTokenData extends BaseTokenData {
name: string;
description?: string;
allowedScopes?: string[];
allowedPackages?: string[];
expires: number;
expiredAt: Date;
}
type TokenData = ClassicTokenData | GranularTokenData;
export function isGranularToken(
data: TokenData | Token
): data is GranularTokenData {
return data.type === TokenType.granular;
}
export class Token extends Entity {
@@ -19,7 +46,15 @@ export class Token extends Entity {
readonly userId: string;
readonly isReadonly: boolean;
readonly isAutomation: boolean;
token?: string;
readonly type?: TokenType;
readonly name?: string;
readonly description?: string;
readonly allowedScopes?: string[];
readonly expiredAt?: Date;
readonly expires?: number;
lastUsedAt: Date | null;
allowedPackages?: string[];
token: string;
constructor(data: TokenData) {
super(data);
@@ -27,13 +62,30 @@ export class Token extends Entity {
this.tokenId = data.tokenId;
this.tokenMark = data.tokenMark;
this.tokenKey = data.tokenKey;
this.cidrWhitelist = data.cidrWhitelist;
this.isReadonly = data.isReadonly;
this.isAutomation = data.isAutomation;
this.cidrWhitelist = data.cidrWhitelist || [];
this.isReadonly = data.isReadonly || false;
this.type = (data.type as TokenType) || TokenType.classic;
this.lastUsedAt = data.lastUsedAt || null;
if (isGranularToken(data)) {
this.name = data.name;
this.description = data.description;
this.allowedScopes = data.allowedScopes;
this.expiredAt = data.expiredAt;
this.allowedPackages = data.allowedPackages;
this.isAutomation = false;
} else {
this.isAutomation = data.isAutomation || false;
}
}
static create(data: EasyData<TokenData, 'tokenId'>): Token {
const newData = EntityUtil.defaultData(data, 'tokenId');
if (isGranularToken(newData) && !newData.expiredAt) {
newData.expiredAt = dayjs(newData.createdAt)
.add(newData.expires, 'days')
.toDate();
}
return new Token(newData);
}
}

View File

@@ -1,5 +1,6 @@
import { Entity, EntityData } from './Entity';
import { EasyData, EntityUtil } from '../util/EntityUtil';
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
import { cleanUserPrefix } from '../../common/PackageUtil.ts';
interface UserData extends EntityData {
userId: string;
@@ -15,6 +16,7 @@ interface UserData extends EntityData {
export class User extends Entity {
userId: string;
name: string;
displayName: string;
email: string;
passwordSalt: string;
passwordIntegrity: string;
@@ -26,6 +28,7 @@ export class User extends Entity {
super(data);
this.userId = data.userId;
this.name = data.name;
this.displayName = cleanUserPrefix(this.name);
this.email = data.email;
this.passwordSalt = data.passwordSalt;
this.passwordIntegrity = data.passwordIntegrity;

View File

@@ -0,0 +1,34 @@
import { Entity, type EntityData } from './Entity.ts';
import { EntityUtil, type EasyData } from '../util/EntityUtil.ts';
interface WebauthnCredentialData extends EntityData {
wancId: string;
userId: string;
credentialId: string;
publicKey: string;
browserType?: string;
}
export class WebauthnCredential extends Entity {
wancId: string;
userId: string;
credentialId: string;
publicKey: string;
browserType?: string;
constructor(data: WebauthnCredentialData) {
super(data);
this.wancId = data.wancId;
this.userId = data.userId;
this.credentialId = data.credentialId;
this.publicKey = data.publicKey;
this.browserType = data.browserType;
}
static create(
data: EasyData<WebauthnCredentialData, 'wancId'>
): WebauthnCredential {
const newData = EntityUtil.defaultData(data, 'wancId');
return new WebauthnCredential(newData);
}
}

View File

@@ -1,24 +1,21 @@
import { Event, Inject } from '@eggjs/tegg';
import { EggLogger } from 'egg';
import { PACKAGE_VERSION_ADDED } from './index';
import { BUG_VERSIONS } from '../../common/constants';
import { PackageManagerService } from '../service/PackageManagerService';
import { BugVersionService } from '../service/BugVersionService';
import { Event, Inject, Logger } from 'egg';
import { PACKAGE_VERSION_ADDED } from './index.ts';
import { BUG_VERSIONS } from '../../common/constants.ts';
import type { BugVersionService } from '../service/BugVersionService.ts';
@Event(PACKAGE_VERSION_ADDED)
export class BugVersionFixHandler {
@Inject()
private readonly bugVersionService: BugVersionService;
@Inject()
private readonly packageManagerService: PackageManagerService;
@Inject()
private readonly logger: EggLogger;
private readonly logger: Logger;
async handle(fullname: string) {
if (fullname !== BUG_VERSIONS) return;
try {
const bugVersion = await this.packageManagerService.getBugVersion();
const bugVersion = await this.bugVersionService.getBugVersion();
if (!bugVersion) return;
await this.bugVersionService.cleanBugVersionPackageCaches(bugVersion);
} catch (e) {

View File

@@ -1,18 +1,19 @@
import { Event, Inject } from '@eggjs/tegg';
import { Event, Inject } from 'egg';
import {
PACKAGE_UNPUBLISHED,
PACKAGE_BLOCKED,
PACKAGE_UNBLOCKED,
PACKAGE_VERSION_ADDED,
PACKAGE_VERSION_REMOVED,
PACKAGE_TAG_ADDED,
PACKAGE_TAG_CHANGED,
PACKAGE_TAG_REMOVED,
PACKAGE_MAINTAINER_CHANGED,
PACKAGE_MAINTAINER_REMOVED,
PACKAGE_META_CHANGED,
} from './index';
import { CacheService } from '../../core/service/CacheService';
PACKAGE_TAG_ADDED,
PACKAGE_TAG_CHANGED,
PACKAGE_TAG_REMOVED,
PACKAGE_UNBLOCKED,
PACKAGE_UNPUBLISHED,
PACKAGE_VERSION_ADDED,
PACKAGE_VERSION_REMOVED,
} from './index.ts';
import type { CacheService } from '../../core/service/CacheService.ts';
class CacheCleanerEvent {
@Inject()
@@ -24,77 +25,77 @@ class CacheCleanerEvent {
}
@Event(PACKAGE_UNPUBLISHED)
export class PackageUnpublished extends CacheCleanerEvent {
export class PackageUnpublishedCacheCleanEvent extends CacheCleanerEvent {
async handle(fullname: string) {
await this.removeCache(fullname);
}
}
@Event(PACKAGE_BLOCKED)
export class PackageBlocked extends CacheCleanerEvent {
export class PackageBlockedCacheCleanEvent extends CacheCleanerEvent {
async handle(fullname: string) {
await this.removeCache(fullname);
}
}
@Event(PACKAGE_UNBLOCKED)
export class PackageUnblocked extends CacheCleanerEvent {
export class PackageUnblockedCacheCleanEvent extends CacheCleanerEvent {
async handle(fullname: string) {
await this.removeCache(fullname);
}
}
@Event(PACKAGE_VERSION_ADDED)
export class PackageVersionAdded extends CacheCleanerEvent {
export class PackageVersionAddedCacheCleanEvent extends CacheCleanerEvent {
async handle(fullname: string) {
await this.removeCache(fullname);
}
}
@Event(PACKAGE_VERSION_REMOVED)
export class PackageVersionRemoved extends CacheCleanerEvent {
export class PackageVersionRemovedCacheCleanEvent extends CacheCleanerEvent {
async handle(fullname: string) {
await this.removeCache(fullname);
}
}
@Event(PACKAGE_TAG_ADDED)
export class PackageTagAdded extends CacheCleanerEvent {
export class PackageTagAddedCacheCleanEvent extends CacheCleanerEvent {
async handle(fullname: string) {
await this.removeCache(fullname);
}
}
@Event(PACKAGE_TAG_CHANGED)
export class PackageTagChanged extends CacheCleanerEvent {
export class PackageTagChangedCacheCleanEvent extends CacheCleanerEvent {
async handle(fullname: string) {
await this.removeCache(fullname);
}
}
@Event(PACKAGE_TAG_REMOVED)
export class PackageTagRemoved extends CacheCleanerEvent {
export class PackageTagRemovedCacheCleanEvent extends CacheCleanerEvent {
async handle(fullname: string) {
await this.removeCache(fullname);
}
}
@Event(PACKAGE_MAINTAINER_CHANGED)
export class PackageMaintainerChanged extends CacheCleanerEvent {
export class PackageMaintainerChangedCacheCleanEvent extends CacheCleanerEvent {
async handle(fullname: string) {
await this.removeCache(fullname);
}
}
@Event(PACKAGE_MAINTAINER_REMOVED)
export class PackageMaintainerRemoved extends CacheCleanerEvent {
export class PackageMaintainerRemovedCacheCleanEvent extends CacheCleanerEvent {
async handle(fullname: string) {
await this.removeCache(fullname);
}
}
@Event(PACKAGE_META_CHANGED)
export class PackageMetaChanged extends CacheCleanerEvent {
export class PackageMetaChangedCacheCleanEvent extends CacheCleanerEvent {
async handle(fullname: string) {
await this.removeCache(fullname);
}

View File

@@ -1,22 +1,24 @@
import { EggAppConfig } from 'egg';
import { Event, Inject } from '@eggjs/tegg';
import { Event, Inject, Config } from 'egg';
import {
PACKAGE_UNPUBLISHED,
PACKAGE_VERSION_ADDED,
PACKAGE_VERSION_REMOVED,
type PackageMetaChange,
PACKAGE_MAINTAINER_CHANGED,
PACKAGE_MAINTAINER_REMOVED,
PACKAGE_META_CHANGED,
PACKAGE_TAG_ADDED,
PACKAGE_TAG_CHANGED,
PACKAGE_TAG_REMOVED,
PACKAGE_MAINTAINER_CHANGED,
PACKAGE_MAINTAINER_REMOVED,
PACKAGE_META_CHANGED, PackageMetaChange,
} from './index';
import { ChangeRepository } from '../../repository/ChangeRepository';
import { Change } from '../entity/Change';
import { HookEvent } from '../entity/HookEvent';
import { Task } from '../entity/Task';
import { User } from '../entity/User';
import { TaskService } from '../service/TaskService';
PACKAGE_UNPUBLISHED,
PACKAGE_VERSION_ADDED,
PACKAGE_VERSION_REMOVED,
} from './index.ts';
import type { ChangeRepository } from '../../repository/ChangeRepository.ts';
import { Change } from '../entity/Change.ts';
import { HookEvent } from '../entity/HookEvent.ts';
import { Task } from '../entity/Task.ts';
import type { User } from '../entity/User.ts';
import type { TaskService } from '../service/TaskService.ts';
class ChangesStreamEvent {
@Inject()
@@ -26,13 +28,17 @@ class ChangesStreamEvent {
protected readonly taskService: TaskService;
@Inject()
protected readonly config: EggAppConfig;
protected readonly config: Config;
protected get hookEnable() {
return this.config.hookEnable;
return this.config.cnpmcore.hookEnable;
}
protected async addChange(type: string, fullname: string, data: object): Promise<Change> {
protected async addChange(
type: string,
fullname: string,
data: object
): Promise<Change> {
const change = Change.create({
type,
targetName: fullname,
@@ -44,79 +50,101 @@ class ChangesStreamEvent {
}
@Event(PACKAGE_UNPUBLISHED)
export class PackageUnpublished extends ChangesStreamEvent {
export class PackageUnpublishedChangesStreamEvent extends ChangesStreamEvent {
async handle(fullname: string) {
const change = await this.addChange(PACKAGE_UNPUBLISHED, fullname, {});
if (this.hookEnable) {
const task = Task.createCreateHookTask(HookEvent.createUnpublishEvent(fullname, change.changeId));
const task = Task.createCreateHookTask(
HookEvent.createUnpublishEvent(fullname, change.changeId)
);
await this.taskService.createTask(task, true);
}
}
}
@Event(PACKAGE_VERSION_ADDED)
export class PackageVersionAdded extends ChangesStreamEvent {
export class PackageVersionAddedChangesStreamEvent extends ChangesStreamEvent {
async handle(fullname: string, version: string, tag?: string) {
const change = await this.addChange(PACKAGE_VERSION_ADDED, fullname, { version });
const change = await this.addChange(PACKAGE_VERSION_ADDED, fullname, {
version,
});
if (this.hookEnable) {
const task = Task.createCreateHookTask(HookEvent.createPublishEvent(fullname, change.changeId, version, tag));
const task = Task.createCreateHookTask(
HookEvent.createPublishEvent(fullname, change.changeId, version, tag)
);
await this.taskService.createTask(task, true);
}
}
}
@Event(PACKAGE_VERSION_REMOVED)
export class PackageVersionRemoved extends ChangesStreamEvent {
export class PackageVersionRemovedChangesStreamEvent extends ChangesStreamEvent {
async handle(fullname: string, version: string, tag?: string) {
const change = await this.addChange(PACKAGE_VERSION_REMOVED, fullname, { version });
const change = await this.addChange(PACKAGE_VERSION_REMOVED, fullname, {
version,
});
if (this.hookEnable) {
const task = Task.createCreateHookTask(HookEvent.createUnpublishEvent(fullname, change.changeId, version, tag));
const task = Task.createCreateHookTask(
HookEvent.createUnpublishEvent(fullname, change.changeId, version, tag)
);
await this.taskService.createTask(task, true);
}
}
}
@Event(PACKAGE_TAG_ADDED)
export class PackageTagAdded extends ChangesStreamEvent {
export class PackageTagAddedChangesStreamEvent extends ChangesStreamEvent {
async handle(fullname: string, tag: string) {
const change = await this.addChange(PACKAGE_TAG_ADDED, fullname, { tag });
if (this.hookEnable) {
const task = Task.createCreateHookTask(HookEvent.createDistTagEvent(fullname, change.changeId, tag));
const task = Task.createCreateHookTask(
HookEvent.createDistTagEvent(fullname, change.changeId, tag)
);
await this.taskService.createTask(task, true);
}
}
}
@Event(PACKAGE_TAG_CHANGED)
export class PackageTagChanged extends ChangesStreamEvent {
export class PackageTagChangedChangesStreamEvent extends ChangesStreamEvent {
async handle(fullname: string, tag: string) {
const change = await this.addChange(PACKAGE_TAG_CHANGED, fullname, { tag });
if (this.hookEnable) {
const task = Task.createCreateHookTask(HookEvent.createDistTagEvent(fullname, change.changeId, tag));
const task = Task.createCreateHookTask(
HookEvent.createDistTagEvent(fullname, change.changeId, tag)
);
await this.taskService.createTask(task, true);
}
}
}
@Event(PACKAGE_TAG_REMOVED)
export class PackageTagRemoved extends ChangesStreamEvent {
export class PackageTagRemovedChangesStreamEvent extends ChangesStreamEvent {
async handle(fullname: string, tag: string) {
const change = await this.addChange(PACKAGE_TAG_REMOVED, fullname, { tag });
if (this.hookEnable) {
const task = Task.createCreateHookTask(HookEvent.createDistTagRmEvent(fullname, change.changeId, tag));
const task = Task.createCreateHookTask(
HookEvent.createDistTagRmEvent(fullname, change.changeId, tag)
);
await this.taskService.createTask(task, true);
}
}
}
@Event(PACKAGE_MAINTAINER_CHANGED)
export class PackageMaintainerChanged extends ChangesStreamEvent {
export class PackageMaintainerChangedChangesStreamEvent extends ChangesStreamEvent {
async handle(fullname: string, maintainers: User[]) {
const change = await this.addChange(PACKAGE_MAINTAINER_CHANGED, fullname, {});
const change = await this.addChange(
PACKAGE_MAINTAINER_CHANGED,
fullname,
{}
);
// TODO 应该比较差值,而不是全量推送
if (this.hookEnable) {
for (const maintainer of maintainers) {
const task = Task.createCreateHookTask(HookEvent.createOwnerEvent(fullname, change.changeId, maintainer.name));
const task = Task.createCreateHookTask(
HookEvent.createOwnerEvent(fullname, change.changeId, maintainer.name)
);
await this.taskService.createTask(task, true);
}
}
@@ -124,24 +152,36 @@ export class PackageMaintainerChanged extends ChangesStreamEvent {
}
@Event(PACKAGE_MAINTAINER_REMOVED)
export class PackageMaintainerRemoved extends ChangesStreamEvent {
export class PackageMaintainerRemovedChangesStreamEvent extends ChangesStreamEvent {
async handle(fullname: string, maintainer: string) {
const change = await this.addChange(PACKAGE_MAINTAINER_REMOVED, fullname, { maintainer });
const change = await this.addChange(PACKAGE_MAINTAINER_REMOVED, fullname, {
maintainer,
});
if (this.hookEnable) {
const task = Task.createCreateHookTask(HookEvent.createOwnerRmEvent(fullname, change.changeId, maintainer));
const task = Task.createCreateHookTask(
HookEvent.createOwnerRmEvent(fullname, change.changeId, maintainer)
);
await this.taskService.createTask(task, true);
}
}
}
@Event(PACKAGE_META_CHANGED)
export class PackageMetaChanged extends ChangesStreamEvent {
export class PackageMetaChangedChangesStreamEvent extends ChangesStreamEvent {
async handle(fullname: string, meta: PackageMetaChange) {
const change = await this.addChange(PACKAGE_META_CHANGED, fullname, { ...meta });
const change = await this.addChange(PACKAGE_META_CHANGED, fullname, {
...meta,
});
const { deprecateds } = meta;
if (this.hookEnable) {
for (const deprecated of deprecateds || []) {
const task = Task.createCreateHookTask(HookEvent.createDeprecatedEvent(fullname, change.changeId, deprecated.version));
const task = Task.createCreateHookTask(
HookEvent.createDeprecatedEvent(
fullname,
change.changeId,
deprecated.version
)
);
await this.taskService.createTask(task, true);
}
}

View File

@@ -1,30 +1,38 @@
import { Event, Inject } from '@eggjs/tegg';
import {
EggAppConfig,
} from 'egg';
import { PACKAGE_VERSION_ADDED } from './index';
import { getScopeAndName } from '../../common/PackageUtil';
import { PackageVersionManifest as PackageVersionManifestEntity } from '../entity/PackageVersionManifest';
import { PackageRepository } from '../../repository/PackageRepository';
import { DistRepository } from '../../repository/DistRepository';
import { Config, Event, Inject } from 'egg';
import { PACKAGE_VERSION_ADDED } from './index.ts';
import { getScopeAndName } from '../../common/PackageUtil.ts';
import { PackageVersionManifest as PackageVersionManifestEntity } from '../entity/PackageVersionManifest.ts';
import type { PackageRepository } from '../../repository/PackageRepository.ts';
import type { DistRepository } from '../../repository/DistRepository.ts';
class StoreManifestEvent {
@Inject()
protected readonly config: EggAppConfig;
protected readonly config: Config;
@Inject()
private readonly packageRepository: PackageRepository;
@Inject()
private readonly distRepository: DistRepository;
protected async savePackageVersionManifest(fullname: string, version: string) {
if (!this.config.cnpmcore.enableStoreFullPackageVersionManifestsToDatabase) return;
protected async savePackageVersionManifest(
fullname: string,
version: string
) {
if (!this.config.cnpmcore.enableStoreFullPackageVersionManifestsToDatabase)
return;
const [ scope, name ] = getScopeAndName(fullname);
const [scope, name] = getScopeAndName(fullname);
const packageId = await this.packageRepository.findPackageId(scope, name);
if (!packageId) return;
const packageVersion = await this.packageRepository.findPackageVersion(packageId, version);
const packageVersion = await this.packageRepository.findPackageVersion(
packageId,
version
);
if (!packageVersion) return;
const manifest = await this.distRepository.findPackageVersionManifest(packageId, version);
const manifest = await this.distRepository.findPackageVersionManifest(
packageId,
version
);
if (!manifest) return;
const entity = PackageVersionManifestEntity.create({
packageId,
@@ -36,7 +44,7 @@ class StoreManifestEvent {
}
@Event(PACKAGE_VERSION_ADDED)
export class PackageVersionAdded extends StoreManifestEvent {
export class PackageVersionAddedStoreManifestEvent extends StoreManifestEvent {
async handle(fullname: string, version: string) {
await this.savePackageVersionManifest(fullname, version);
}

View File

@@ -0,0 +1,53 @@
// TODO sync event
import { Config, Event, Inject } from 'egg';
import {
PACKAGE_BLOCKED,
PACKAGE_MAINTAINER_CHANGED,
PACKAGE_MAINTAINER_REMOVED,
PACKAGE_META_CHANGED,
PACKAGE_TAG_ADDED,
PACKAGE_TAG_CHANGED,
PACKAGE_TAG_REMOVED,
PACKAGE_UNBLOCKED,
PACKAGE_UNPUBLISHED,
PACKAGE_VERSION_ADDED,
PACKAGE_VERSION_REMOVED,
} from './index.ts';
import type { PackageSearchService } from '../service/PackageSearchService.ts';
class SyncESPackage {
@Inject()
protected readonly packageSearchService: PackageSearchService;
@Inject()
protected readonly config: Config;
protected async syncPackage(fullname: string) {
if (!this.config.cnpmcore.enableElasticsearch) return;
await this.packageSearchService.syncPackage(fullname, true);
}
}
@Event(PACKAGE_UNPUBLISHED)
@Event(PACKAGE_BLOCKED)
export class PackageUnpublishedSyncESEvent extends SyncESPackage {
async handle(fullname: string) {
if (!this.config.cnpmcore.enableElasticsearch) return;
await this.packageSearchService.removePackage(fullname);
}
}
@Event(PACKAGE_VERSION_ADDED)
@Event(PACKAGE_META_CHANGED)
@Event(PACKAGE_VERSION_REMOVED)
@Event(PACKAGE_TAG_ADDED)
@Event(PACKAGE_TAG_CHANGED)
@Event(PACKAGE_TAG_REMOVED)
@Event(PACKAGE_MAINTAINER_CHANGED)
@Event(PACKAGE_MAINTAINER_REMOVED)
@Event(PACKAGE_UNBLOCKED)
export class PackageVersionAddedSyncESEvent extends SyncESPackage {
async handle(fullname: string) {
await this.syncPackage(fullname);
}
}

View File

@@ -0,0 +1,91 @@
import { Event, Inject, Config, Logger } from 'egg';
import { ForbiddenError } from 'egg/errors';
import {
PACKAGE_TAG_ADDED,
PACKAGE_TAG_CHANGED,
PACKAGE_VERSION_ADDED,
} from './index.ts';
import { getScopeAndName } from '../../common/PackageUtil.ts';
import type { PackageManagerService } from '../service/PackageManagerService.ts';
import type { PackageVersionFileService } from '../service/PackageVersionFileService.ts';
class SyncPackageVersionFileEvent {
@Inject()
protected readonly config: Config;
@Inject()
protected readonly logger: Logger;
@Inject()
private readonly packageManagerService: PackageManagerService;
@Inject()
private readonly packageVersionFileService: PackageVersionFileService;
protected async syncPackageVersionFile(fullname: string, version: string) {
// must set enableUnpkg and enableSyncUnpkgFiles = true both
if (!this.config.cnpmcore.enableUnpkg) return;
if (!this.config.cnpmcore.enableSyncUnpkgFiles) return;
// ignore sync on unittest
if (
this.config.env === 'unittest' &&
fullname !== '@cnpm/unittest-unpkg-demo'
)
return;
const [scope, name] = getScopeAndName(fullname);
const { packageVersion } =
await this.packageManagerService.showPackageVersionByVersionOrTag(
scope,
name,
version
);
if (!packageVersion) return;
try {
await this.packageVersionFileService.syncPackageVersionFiles(
packageVersion
);
} catch (err) {
if (err instanceof ForbiddenError) {
this.logger.info(
'[SyncPackageVersionFileEvent.syncPackageVersionFile] ignore sync files, cause: %s',
err.message
);
return;
}
throw err;
}
}
protected async syncPackageReadmeToLatestVersion(fullname: string) {
const [scope, name] = getScopeAndName(fullname);
const { pkg, packageVersion } =
await this.packageManagerService.showPackageVersionByVersionOrTag(
scope,
name,
'latest'
);
if (!pkg || !packageVersion) return;
await this.packageVersionFileService.syncPackageReadme(pkg, packageVersion);
}
}
@Event(PACKAGE_VERSION_ADDED)
export class PackageVersionAddedSyncPackageVersionFileEvent extends SyncPackageVersionFileEvent {
async handle(fullname: string, version: string) {
await this.syncPackageVersionFile(fullname, version);
}
}
@Event(PACKAGE_TAG_ADDED)
export class PackageTagAddedSyncPackageVersionFileEvent extends SyncPackageVersionFileEvent {
async handle(fullname: string, tag: string) {
if (tag !== 'latest') return;
await this.syncPackageReadmeToLatestVersion(fullname);
}
}
@Event(PACKAGE_TAG_CHANGED)
export class PackageTagChangedSyncPackageVersionFileEvent extends SyncPackageVersionFileEvent {
async handle(fullname: string, tag: string) {
if (tag !== 'latest') return;
await this.syncPackageReadmeToLatestVersion(fullname);
}
}

View File

@@ -0,0 +1,23 @@
import { Event, Inject } from 'egg';
import { PACKAGE_ADDED, PACKAGE_VERSION_ADDED } from './index.ts';
import type { TotalRepository } from '../../repository/TotalRepository.ts';
class TotalHandlerEvent {
@Inject()
protected readonly totalRepository: TotalRepository;
}
@Event(PACKAGE_ADDED)
export class PackageAddedTotalHandlerEvent extends TotalHandlerEvent {
async handle() {
await this.totalRepository.incrementPackageCount();
}
}
@Event(PACKAGE_VERSION_ADDED)
export class PackageVersionAddedTotalHandlerEvent extends TotalHandlerEvent {
async handle() {
await this.totalRepository.incrementPackageVersionCount();
}
}

View File

@@ -1,6 +1,6 @@
import '@eggjs/tegg';
import { User } from '../entity/User';
import type { User } from '../entity/User.ts';
export const PACKAGE_ADDED = 'PACKAGE_ADDED';
export const PACKAGE_UNPUBLISHED = 'PACKAGE_UNPUBLISHED';
export const PACKAGE_BLOCKED = 'PACKAGE_BLOCKED';
export const PACKAGE_UNBLOCKED = 'PACKAGE_UNBLOCKED';
@@ -15,26 +15,43 @@ export const PACKAGE_META_CHANGED = 'PACKAGE_META_CHANGED';
export interface PackageDeprecated {
version: string;
deprecated: string;
deprecated?: string;
}
export interface PackageMetaChange {
deprecateds?: Array<PackageDeprecated>;
deprecateds?: PackageDeprecated[];
}
declare module '@eggjs/tegg' {
declare module 'egg' {
interface Events {
[PACKAGE_ADDED]: (fullname: string) => Promise<void>;
[PACKAGE_UNPUBLISHED]: (fullname: string) => Promise<void>;
[PACKAGE_BLOCKED]: (fullname: string) => Promise<void>;
[PACKAGE_UNBLOCKED]: (fullname: string) => Promise<void>;
[PACKAGE_VERSION_ADDED]: (fullname: string, version: string, tag?: string) => Promise<void>;
[PACKAGE_VERSION_REMOVED]: (fullname: string, version: string, tag?: string) => Promise<void>;
[PACKAGE_VERSION_ADDED]: (
fullname: string,
version: string,
tag?: string
) => Promise<void>;
[PACKAGE_VERSION_REMOVED]: (
fullname: string,
version: string,
tag?: string
) => Promise<void>;
[PACKAGE_TAG_ADDED]: (fullname: string, tag: string) => Promise<void>;
[PACKAGE_TAG_CHANGED]: (fullname: string, tag: string) => Promise<void>;
[PACKAGE_TAG_REMOVED]: (fullname: string, tag: string) => Promise<void>;
[PACKAGE_MAINTAINER_CHANGED]: (fullname: string, maintainers: User[]) => Promise<void>;
[PACKAGE_MAINTAINER_REMOVED]: (fullname: string, maintainer: string) => Promise<void>;
[PACKAGE_META_CHANGED]: (fullname: string, meta: PackageMetaChange) => Promise<void>;
[PACKAGE_MAINTAINER_CHANGED]: (
fullname: string,
maintainers: User[]
) => Promise<void>;
[PACKAGE_MAINTAINER_REMOVED]: (
fullname: string,
maintainer: string
) => Promise<void>;
[PACKAGE_META_CHANGED]: (
fullname: string,
meta: PackageMetaChange
) => Promise<void>;
}
}

View File

@@ -2,5 +2,6 @@
"name": "cnpmcore-core",
"eggModule": {
"name": "cnpmcoreCore"
}
},
"type": "module"
}

View File

@@ -1,55 +1,39 @@
import { rm } from 'fs/promises';
import fs from 'node:fs/promises';
import {
AccessLevel,
ContextProto,
Inject,
} from '@eggjs/tegg';
import {
EggContextHttpClient,
SingletonProto,
type EggObjectFactory,
HttpClient,
} from 'egg';
import fs from 'fs/promises';
import binaries, { SyncerClass } from '../../../config/binaries';
import { NFSAdapter } from '../../common/adapter/NFSAdapter';
import { TaskType, TaskState } from '../../common/enum/Task';
import { downloadToTempfile } from '../../common/FileUtil';
import { BinaryRepository } from '../../repository/BinaryRepository';
import { Task } from '../entity/Task';
import { Binary } from '../entity/Binary';
import { TaskService } from './TaskService';
import { AbstractBinary, BinaryItem } from '../../common/adapter/binary/AbstractBinary';
import { ApiBinary } from '../../common/adapter/binary/ApiBinary';
import { AbstractService } from '../../common/AbstractService';
import { NodeBinary } from '../../common/adapter/binary/NodeBinary';
import { NwjsBinary } from '../../common/adapter/binary/NwjsBinary';
import { BucketBinary } from '../../common/adapter/binary/BucketBinary';
import { CypressBinary } from '../../common/adapter/binary/CypressBinary';
import { SqlcipherBinary } from '../../common/adapter/binary/SqlcipherBinary';
import { PuppeteerBinary } from '../../common/adapter/binary/PuppeteerBinary';
import { GithubBinary } from '../../common/adapter/binary/GithubBinary';
import { ElectronBinary } from '../../common/adapter/binary/ElectronBinary';
import { NodePreGypBinary } from '../../common/adapter/binary/NodePreGypBinary';
import { ImageminBinary } from '../../common/adapter/binary/ImageminBinary';
import { PlaywrightBinary } from '../../common/adapter/binary/PlaywrightBinary';
import { sortBy } from 'lodash-es';
const BinaryClasses = {
[SyncerClass.NodeBinary]: NodeBinary,
[SyncerClass.NwjsBinary]: NwjsBinary,
[SyncerClass.BucketBinary]: BucketBinary,
[SyncerClass.CypressBinary]: CypressBinary,
[SyncerClass.SqlcipherBinary]: SqlcipherBinary,
[SyncerClass.PuppeteerBinary]: PuppeteerBinary,
[SyncerClass.GithubBinary]: GithubBinary,
[SyncerClass.ElectronBinary]: ElectronBinary,
[SyncerClass.NodePreGypBinary]: NodePreGypBinary,
[SyncerClass.ImageminBinary]: ImageminBinary,
[SyncerClass.PlaywrightBinary]: PlaywrightBinary,
};
import binaries, {
type BinaryName,
type CategoryName,
} from '../../../config/binaries.ts';
import type { BinaryRepository } from '../../repository/BinaryRepository.ts';
import { Task, type SyncBinaryTask } from '../entity/Task.ts';
import { Binary } from '../entity/Binary.ts';
import type { TaskService } from './TaskService.ts';
import type { NFSAdapter } from '../../common/adapter/NFSAdapter.ts';
import { downloadToTempfile } from '../../common/FileUtil.ts';
import { isTimeoutError } from '../../common/ErrorUtil.ts';
import {
AbstractBinary,
type BinaryItem,
} from '../../common/adapter/binary/AbstractBinary.ts';
import { AbstractService } from '../../common/AbstractService.ts';
import { BinaryType } from '../../common/enum/Binary.ts';
import { TaskState, TaskType } from '../../common/enum/Task.ts';
import { platforms } from '../../common/adapter/binary/PuppeteerBinary.ts';
function isoNow() {
return new Date().toISOString();
}
@ContextProto({
@SingletonProto({
accessLevel: AccessLevel.PUBLIC,
})
export class BinarySyncerService extends AbstractService {
@@ -58,134 +42,310 @@ export class BinarySyncerService extends AbstractService {
@Inject()
private readonly taskService: TaskService;
@Inject()
private readonly httpclient: EggContextHttpClient;
private readonly httpClient: HttpClient;
@Inject()
private readonly nfsAdapter: NFSAdapter;
@Inject()
private readonly eggObjectFactory: EggObjectFactory;
public async findBinary(binaryName: string, parent: string, name: string) {
return await this.binaryRepository.findBinary(binaryName, parent, name);
// canvas/v2.6.1/canvas-v2.6.1-node-v57-linux-glibc-x64.tar.gz
// -> node-canvas-prebuilt/v2.6.1/node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz
// canvas 历史版本的 targetName 可能是 category 需要兼容
public async findBinary(
targetName: BinaryName | CategoryName,
parent: string,
name: string
) {
return await this.binaryRepository.findBinary(targetName, parent, name);
}
public async listDirBinaries(binary: Binary) {
return await this.binaryRepository.listBinaries(binary.category, `${binary.parent}${binary.name}`);
public async listDirBinaries(
binary: Binary,
options?: {
limit: number;
since: string;
}
) {
return await this.binaryRepository.listBinaries(
binary.category,
`${binary.parent}${binary.name}`,
options
);
}
public async listRootBinaries(binaryName: string) {
return await this.binaryRepository.listBinaries(binaryName, '/');
public async listRootBinaries(binaryName: BinaryName) {
// 通常 binaryName 和 category 是一样的,但是有些特殊的 binaryName 会有多个 category比如 canvas
// 所以查询 canvas 的时候,需要将 binaryName 和 category 的数据都查出来
const { category } = binaries[binaryName];
const reqs = [this.binaryRepository.listBinaries(binaryName, '/')];
if (category && category !== binaryName) {
reqs.push(this.binaryRepository.listBinaries(category, '/'));
}
const [rootBinary, categoryBinary] = await Promise.all(reqs);
const versions = new Set(rootBinary.map(b => b.name));
if (categoryBinary) {
for (const b of categoryBinary) {
const version = b.name;
// 只将没有的版本添加进去
if (!versions.has(version)) {
rootBinary.push(b);
}
}
}
return rootBinary;
}
public async downloadBinary(binary: Binary) {
return await this.nfsAdapter.getDownloadUrlOrStream(binary.storePath);
}
public async createTask(binaryName: string, lastData?: any) {
return await this.taskService.createTask(Task.createSyncBinary(binaryName, lastData), false);
public async createTask(
binaryName: BinaryName,
lastData?: Record<string, unknown>
) {
// chromium-browser-snapshots 产物极大,完整遍历 s3 bucket 耗时会太长
// 必须从上次同步的 revision 之后开始遍历
// 如果需要补偿数据,可以
if (binaryName === 'chromium-browser-snapshots') {
lastData = lastData || {};
for (const platform of platforms) {
if (lastData[platform]) continue;
const binaryDir = await this.binaryRepository.findLatestBinaryDir(
'chromium-browser-snapshots',
`/${platform}/`
);
if (binaryDir) {
lastData[platform] = binaryDir.name.slice(0, -1);
}
}
const latestBinary = await this.binaryRepository.findLatestBinary(
'chromium-browser-snapshots'
);
if (latestBinary && !lastData.lastSyncTime) {
lastData.lastSyncTime = latestBinary.date;
}
}
try {
return await this.taskService.createTask(
Task.createSyncBinary(binaryName, lastData),
false
);
} catch (e) {
this.logger.error(
'[BinarySyncerService.createTask] binaryName: %s, error: %s',
binaryName,
e
);
}
}
public async findTask(taskId: string) {
return await this.taskService.findTask(taskId);
public async findTask(taskId: string): Promise<SyncBinaryTask | null> {
return (await this.taskService.findTask(taskId)) as SyncBinaryTask;
}
public async findTaskLog(task: Task) {
public async findTaskLog(task: SyncBinaryTask) {
return await this.taskService.findTaskLog(task);
}
public async findExecuteTask() {
return await this.taskService.findExecuteTask(TaskType.SyncBinary);
public async findExecuteTask(): Promise<SyncBinaryTask | null> {
return (await this.taskService.findExecuteTask(
TaskType.SyncBinary
)) as SyncBinaryTask;
}
public async executeTask(task: Task) {
const binaryName = task.targetName;
const binaryInstance = this.createBinaryInstance(binaryName);
public async executeTask(task: SyncBinaryTask) {
const binaryName = task.targetName as BinaryName;
const binaryAdapter = await this.getBinaryAdapter(binaryName);
const logUrl = `${this.config.cnpmcore.registry}/-/binary/${binaryName}/syncs/${task.taskId}/log`;
let logs: string[] = [];
logs.push(`[${isoNow()}] 🚧🚧🚧🚧🚧 Start sync binary "${binaryName}" 🚧🚧🚧🚧🚧`);
if (!binaryInstance) {
logs.push(
`[${isoNow()}] 🚧🚧🚧🚧🚧 Start sync binary "${binaryName}" 🚧🚧🚧🚧🚧`
);
if (!binaryAdapter) {
task.error = 'unknow binaryName';
logs.push(`[${isoNow()}] ❌ Synced "${binaryName}" fail, ${task.error}, log: ${logUrl}`);
logs.push(
`[${isoNow()}] ❌ Synced "${binaryName}" fail, ${task.error}, log: ${logUrl}`
);
logs.push(`[${isoNow()}] ❌❌❌❌❌ "${binaryName}" ❌❌❌❌❌`);
this.logger.error('[BinarySyncerService.executeTask:fail] taskId: %s, targetName: %s, %s',
task.taskId, task.targetName, task.error);
this.logger.error(
'[BinarySyncerService.executeTask:fail] taskId: %s, targetName: %s, %s',
task.taskId,
task.targetName,
task.error
);
await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n'));
return;
}
await this.taskService.appendTaskLog(task, logs.join('\n'));
logs = [];
this.logger.info('[BinarySyncerService.executeTask:start] taskId: %s, targetName: %s, log: %s',
task.taskId, task.targetName, logUrl);
this.logger.info(
'[BinarySyncerService.executeTask:start] taskId: %s, targetName: %s, log: %s',
task.taskId,
task.targetName,
logUrl
);
try {
await this.syncDir(binaryInstance, task, '/');
const [hasDownloadError] = await this.syncDir(binaryAdapter, task, '/');
logs.push(`[${isoNow()}] 🟢 log: ${logUrl}`);
logs.push(`[${isoNow()}] 🟢🟢🟢🟢🟢 "${binaryName}" 🟢🟢🟢🟢🟢`);
await this.taskService.finishTask(task, TaskState.Success, logs.join('\n'));
this.logger.info('[BinarySyncerService.executeTask:success] taskId: %s, targetName: %s, log: %s',
task.taskId, task.targetName, logUrl);
} catch (err: any) {
task.error = err.message;
logs.push(`[${isoNow()}] ❌ Synced "${binaryName}" fail, ${task.error}, log: ${logUrl}`);
await this.taskService.finishTask(
task,
TaskState.Success,
logs.join('\n')
);
// 确保没有下载异常才算 success
await binaryAdapter.finishFetch(!hasDownloadError, binaryName);
this.logger.info(
'[BinarySyncerService.executeTask:success] taskId: %s, targetName: %s, log: %s, hasDownloadError: %s',
task.taskId,
task.targetName,
logUrl,
hasDownloadError
);
} catch (err) {
task.error = `${err.name}: ${err.message}`;
logs.push(
`[${isoNow()}] ❌ Synced "${binaryName}" fail, ${task.error}, log: ${logUrl}`
);
logs.push(`[${isoNow()}] ❌❌❌❌❌ "${binaryName}" ❌❌❌❌❌`);
this.logger.error('[BinarySyncerService.executeTask:fail] taskId: %s, targetName: %s, %s',
task.taskId, task.targetName, task.error);
this.logger.error(err);
if (isTimeoutError(err)) {
this.logger.warn(
'[BinarySyncerService.executeTask:fail] taskId: %s, targetName: %s, %s',
task.taskId,
task.targetName,
task.error
);
this.logger.warn(err);
} else {
this.logger.error(
'[BinarySyncerService.executeTask:fail] taskId: %s, targetName: %s, %s',
task.taskId,
task.targetName,
task.error
);
this.logger.error(err);
}
await binaryAdapter.finishFetch(false, binaryName);
await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n'));
}
}
private async syncDir(binaryInstance: AbstractBinary, task: Task, dir: string, parentIndex = '') {
const binaryName = task.targetName;
const result = await binaryInstance.fetch(dir, task.data);
private async syncDir(
binaryAdapter: AbstractBinary,
task: SyncBinaryTask,
dir: string,
parentIndex = '',
latestVersionParent = '/'
) {
const binaryName = task.targetName as BinaryName;
const result = await binaryAdapter.fetch(dir, binaryName, task.data);
let hasDownloadError = false;
let hasItems = false;
if (result && result.items.length > 0) {
hasItems = true;
let logs: string[] = [];
const newItems = await this.diff(binaryName, dir, result.items);
logs.push(`[${isoNow()}][${dir}] 🚧 Syncing diff: ${result.items.length} => ${newItems.length}, Binary class: ${binaryInstance.constructor.name}`);
for (const [ index, { item, reason }] of newItems.entries()) {
const { newItems, latestVersionDir } = await this.diff(
binaryName,
dir,
result.items,
latestVersionParent
);
logs.push(
`[${isoNow()}][${dir}] 🚧 Syncing diff: ${result.items.length} => ${newItems.length}, Binary class: ${binaryAdapter.constructor.name}`
);
// re-check latest version
for (const [index, { item, reason }] of newItems.entries()) {
if (item.isDir) {
logs.push(`[${isoNow()}][${dir}] 🚧 [${parentIndex}${index}] Start sync dir ${JSON.stringify(item)}, reason: ${reason}`);
logs.push(
`[${isoNow()}][${dir}] 🚧 [${parentIndex}${index}] Start sync dir ${JSON.stringify(item)}, reason: ${reason}`
);
await this.taskService.appendTaskLog(task, logs.join('\n'));
logs = [];
const [ hasError, hasSubItems ] = await this.syncDir(binaryInstance, task, `${dir}${item.name}`, `${parentIndex}${index}.`);
const [hasError, hasSubItems] = await this.syncDir(
binaryAdapter,
task,
`${dir}${item.name}`,
`${parentIndex}${index}.`,
latestVersionDir
);
if (hasError) {
hasDownloadError = true;
} else {
} else if (hasSubItems) {
// if any file download error, let dir sync again next time
// if empty dir, don't save it
if (hasSubItems) {
await this.saveBinaryItem(item);
}
await this.saveBinaryItem(item);
}
} else {
// download to nfs
logs.push(`[${isoNow()}][${dir}] 🚧 [${parentIndex}${index}] Downloading ${JSON.stringify(item)}, reason: ${reason}`);
logs.push(
`[${isoNow()}][${dir}] 🚧 [${parentIndex}${index}] Downloading ${JSON.stringify(item)}, reason: ${reason}`
);
// skip exists binary file
const existsBinary = await this.binaryRepository.findBinary(
item.category,
item.parent,
item.name
);
if (existsBinary && existsBinary.date === item.date) {
logs.push(
`[${isoNow()}][${dir}] 🟢 [${parentIndex}${index}] binary file exists, skip download, binaryId: ${existsBinary.binaryId}`
);
this.logger.info(
'[BinarySyncerService.syncDir:skipDownload] binaryId: %s exists, storePath: %s',
existsBinary.binaryId,
existsBinary.storePath
);
continue;
}
await this.taskService.appendTaskLog(task, logs.join('\n'));
logs = [];
let localFile = '';
try {
const { tmpfile, headers, timing } =
await downloadToTempfile(this.httpclient, this.config.dataDir, item.sourceUrl!, item.ignoreDownloadStatuses);
logs.push(`[${isoNow()}][${dir}] 🟢 [${parentIndex}${index}] HTTP content-length: ${headers['content-length']}, timing: ${JSON.stringify(timing)}, ${item.sourceUrl} => ${tmpfile}`);
const { tmpfile, headers, timing } = await downloadToTempfile(
this.httpClient,
this.config.dataDir,
item.sourceUrl,
{ ignoreDownloadStatuses: item.ignoreDownloadStatuses }
);
const log = `[${isoNow()}][${dir}] 🟢 [${parentIndex}${index}] HTTP content-length: ${headers['content-length']}, timing: ${JSON.stringify(timing)}, ${item.sourceUrl} => ${tmpfile}`;
logs.push(log);
this.logger.info(
'[BinarySyncerService.syncDir:downloadToTempfile] %s',
log
);
localFile = tmpfile;
const binary = await this.saveBinaryItem(item, tmpfile);
logs.push(`[${isoNow()}][${dir}] 🟢 [${parentIndex}${index}] Synced file success, binaryId: ${binary.binaryId}`);
logs.push(
`[${isoNow()}][${dir}] 🟢 [${parentIndex}${index}] Synced file success, binaryId: ${binary.binaryId}`
);
await this.taskService.appendTaskLog(task, logs.join('\n'));
logs = [];
} catch (err: any) {
} catch (err) {
if (err.name === 'DownloadNotFoundError') {
this.logger.info('Not found %s, skip it', item.sourceUrl);
logs.push(`[${isoNow()}][${dir}] 🧪️ [${parentIndex}${index}] Download ${item.sourceUrl} not found, skip it`);
logs.push(
`[${isoNow()}][${dir}] 🧪️ [${parentIndex}${index}] Download ${item.sourceUrl} not found, skip it`
);
} else {
this.logger.error('Download binary %s %s', item.sourceUrl, err);
if (err.name === 'DownloadStatusInvalidError') {
this.logger.warn('Download binary %s %s', item.sourceUrl, err);
} else {
this.logger.error('Download binary %s %s', item.sourceUrl, err);
}
hasDownloadError = true;
logs.push(`[${isoNow()}][${dir}] ❌ [${parentIndex}${index}] Download ${item.sourceUrl} error: ${err}`);
logs.push(
`[${isoNow()}][${dir}] ❌ [${parentIndex}${index}] Download ${item.sourceUrl} error: ${err}`
);
}
await this.taskService.appendTaskLog(task, logs.join('\n'));
logs = [];
} finally {
if (localFile) {
await rm(localFile, { force: true });
await fs.rm(localFile, { force: true });
}
}
}
@@ -193,20 +353,35 @@ export class BinarySyncerService extends AbstractService {
if (hasDownloadError) {
logs.push(`[${isoNow()}][${dir}] ❌ Synced dir fail`);
} else {
logs.push(`[${isoNow()}][${dir}] 🟢 Synced dir success`);
logs.push(
`[${isoNow()}][${dir}] 🟢 Synced dir success, hasItems: ${hasItems}`
);
}
await this.taskService.appendTaskLog(task, logs.join('\n'));
}
return [ hasDownloadError, hasItems ];
return [hasDownloadError, hasItems];
}
private async diff(binaryName: string, dir: string, fetchItems: BinaryItem[]) {
const existsItems = await this.binaryRepository.listBinaries(binaryName, dir);
// see https://github.com/cnpm/cnpmcore/issues/556
// 上游可能正在发布新版本、同步流程中断,导致同步的时候,文件列表不一致
// 如果的当前目录命中 latestVersionParent 父目录,那么就再校验一下当前目录
// 如果 existsItems 为空或者经过修改,那么就不需要 revalidate 了
private async diff(
binaryName: BinaryName,
dir: string,
fetchItems: BinaryItem[],
latestVersionParent = '/'
) {
const existsItems = await this.binaryRepository.listBinaries(
binaryName,
dir
);
const existsMap = new Map<string, Binary>();
for (const item of existsItems) {
existsMap.set(item.name, item);
}
const diffItems: { item: Binary; reason: string }[] = [];
let latestItem: BinaryItem | undefined;
for (const item of fetchItems) {
const existsItem = existsMap.get(item.name);
if (!existsItem) {
@@ -231,9 +406,25 @@ export class BinarySyncerService extends AbstractService {
existsItem.sourceUrl = item.url;
existsItem.ignoreDownloadStatuses = item.ignoreDownloadStatuses;
existsItem.date = item.date;
} else if (dir.endsWith(latestVersionParent)) {
if (!latestItem) {
latestItem = sortBy(fetchItems, ['date']).pop();
}
const isLatestItem = latestItem?.name === item.name;
if (isLatestItem && existsItem.isDir) {
diffItems.push({
item: existsItem,
reason: `revalidate latest version, latest parent dir is ${latestVersionParent}, current dir is ${dir}, current name is ${existsItem.name}`,
});
latestVersionParent = `${latestVersionParent}${existsItem.name}`;
}
}
}
return diffItems;
return {
newItems: diffItems,
latestVersionDir: latestVersionParent,
};
}
private async saveBinaryItem(binary: Binary, tmpfile?: string) {
@@ -241,24 +432,37 @@ export class BinarySyncerService extends AbstractService {
const stat = await fs.stat(tmpfile);
binary.size = stat.size;
await this.nfsAdapter.uploadFile(binary.storePath, tmpfile);
this.logger.info('[BinarySyncerService.saveBinaryItem:uploadFile] binaryId: %s, size: %d, %s => %s',
binary.binaryId, stat.size, tmpfile, binary.storePath);
this.logger.info(
'[BinarySyncerService.saveBinaryItem:uploadFile] binaryId: %s, size: %d, %s => %s',
binary.binaryId,
stat.size,
tmpfile,
binary.storePath
);
}
await this.binaryRepository.saveBinary(binary);
return binary;
}
private createBinaryInstance(binaryName: string): AbstractBinary | undefined {
private async getBinaryAdapter(
binaryName: BinaryName
): Promise<AbstractBinary | undefined> {
const config = this.config.cnpmcore;
const binaryConfig = binaries[binaryName];
let binaryAdapter: AbstractBinary;
if (config.sourceRegistryIsCNpm) {
const binaryConfig = binaries[binaryName];
const syncBinaryFromAPISource = config.syncBinaryFromAPISource || `${config.sourceRegistry}/-/binary`;
return new ApiBinary(this.httpclient, this.logger, binaryConfig, syncBinaryFromAPISource);
}
for (const binaryConfig of Object.values(binaries)) {
if (binaryConfig.category === binaryName) {
return new BinaryClasses[binaryConfig.syncer](this.httpclient, this.logger, binaryConfig);
}
binaryAdapter = await this.eggObjectFactory.getEggObject(
AbstractBinary,
BinaryType.Api
);
} else {
binaryAdapter = await this.eggObjectFactory.getEggObject(
AbstractBinary,
binaryConfig.type
);
}
await binaryAdapter.initFetch(binaryName);
return binaryAdapter;
}
}

Some files were not shown because too many files have changed in this diff Show More