import Joi from 'joi'
-import { BaseJsonService } from '../index.js'
+import { BaseJsonService, NotFound } from '../index.js'
+import { isStable, latest } from '../php-version.js'
-const packageSchema = Joi.object()
- .pattern(
- /^/,
- Joi.object({
- 'default-branch': Joi.bool(),
- version: Joi.string(),
- require: Joi.object({
- php: Joi.string(),
- }),
- }).required()
- )
- .required()
+const packageSchema = Joi.array().items(
+ Joi.object({
+ version: Joi.string().required(),
+ require: Joi.object({
+ php: Joi.string(),
+ }),
+ })
+)
const allVersionsSchema = Joi.object({
packages: Joi.object().pattern(/^/, packageSchema).required(),
@@ -64,7 +61,31 @@ class BasePackagistService extends BaseJsonService {
* @returns {object} Parsed response
*/
async fetch({ user, repo, schema, server = 'https://packagist.org' }) {
- const url = `${server}/p/${user.toLowerCase()}/${repo.toLowerCase()}.json`
+ const url = `${server}/p2/${user.toLowerCase()}/${repo.toLowerCase()}.json`
+
+ return this._requestJson({
+ schema,
+ url,
+ })
+ }
+
+ /**
+ * Fetch dev releases method.
+ *
+ * This method utilize composer metadata API which
+ * "... is the preferred way to access the data as it is always up to date,
+ * and dumped to static files so it is very efficient on our end." (comment from official documentation).
+ * For more information please refer to https://packagist.org/apidoc#get-package-data.
+ *
+ * @param {object} attrs Refer to individual attrs
+ * @param {string} attrs.user package user
+ * @param {string} attrs.repo package repository
+ * @param {Joi} attrs.schema Joi schema to validate the response transformed to JSON
+ * @param {string} attrs.server URL for the packagist registry server (Optional)
+ * @returns {object} Parsed response
+ */
+ async fetchDev({ user, repo, schema, server = 'https://packagist.org' }) {
+ const url = `${server}/p2/${user.toLowerCase()}/${repo.toLowerCase()}~dev.json`
return this._requestJson({
schema,
@@ -101,16 +122,74 @@ class BasePackagistService extends BaseJsonService {
})
}
- getDefaultBranch(json, user, repo) {
- const packageName = this.getPackageName(user, repo)
- return Object.values(json.packages[packageName]).find(
- b => b['default-branch'] === true
- )
- }
-
getPackageName(user, repo) {
return `${user.toLowerCase()}/${repo.toLowerCase()}`
}
+
+ /**
+ * Extract the array of minified versions of the given packageName,
+ * expand them back to their original format then return.
+ *
+ * @param {object} json The response of Packagist v2 API.
+ * @param {string} packageName The package name.
+ *
+ * @returns {object[]} An array of version metadata object.
+ *
+ * @see https://github.com/composer/metadata-minifier/blob/c549d23829536f0d0e984aaabbf02af91f443207/src/MetadataMinifier.php#L16-L46
+ */
+ static expandPackageVersions(json, packageName) {
+ const versions = json.packages[packageName]
+ const expanded = []
+ let expandedVersion = null
+
+ for (const i in versions) {
+ const versionData = versions[i]
+ if (!expandedVersion) {
+ expandedVersion = { ...versionData }
+ expanded.push(expandedVersion)
+ continue
+ }
+
+ expandedVersion = { ...expandedVersion, ...versionData }
+ for (const key in expandedVersion) {
+ if (expandedVersion[key] === '__unset') {
+ delete expandedVersion[key]
+ }
+ }
+ expanded.push(expandedVersion)
+ }
+
+ return expanded
+ }
+
+ /**
+ * Find the object representation of the latest release.
+ *
+ * @param {object[]} versions An array of object representing a version.
+ * @param {boolean} includePrereleases Includes pre-release semver for the search.
+ *
+ * @returns {object} The object of the latest version.
+ * @throws {NotFound} Thrown if there is no item from the version array.
+ */
+ findLatestRelease(versions, includePrereleases = false) {
+ // Find the latest version string, if not found, throw NotFound.
+ const versionStrings = versions
+ .filter(
+ version =>
+ typeof version.version === 'string' ||
+ version.version instanceof String
+ )
+ .map(version => version.version)
+ if (versionStrings.length < 1) {
+ throw new NotFound({ prettyMessage: 'no released version found' })
+ }
+
+ let release = latest(versionStrings)
+ if (!includePrereleases) {
+ release = latest(versionStrings.filter(isStable)) || release
+ }
+ return versions.filter(version => version.version === release)[0]
+ }
}
const customServerDocumentationFragment = `
@@ -150,7 +229,7 @@ export {
diff --git a/services_steam_steam-base.js.html b/services_steam_steam-base.js.html
index e1d8c672c4..f138b69e59 100644
--- a/services_steam_steam-base.js.html
+++ b/services_steam_steam-base.js.html
@@ -98,7 +98,7 @@ export default BaseSteamAPI
diff --git a/services_test-validators.js.html b/services_test-validators.js.html
index 6eb7198872..fb57755e59 100644
--- a/services_test-validators.js.html
+++ b/services_test-validators.js.html
@@ -229,7 +229,7 @@ export {
diff --git a/tutorial-TUTORIAL.html b/tutorial-TUTORIAL.html
index 98b22390dc..d156fa7170 100644
--- a/tutorial-TUTORIAL.html
+++ b/tutorial-TUTORIAL.html
@@ -377,7 +377,7 @@ will review your contribution.
diff --git a/tutorial-adding-new-config-values.html b/tutorial-adding-new-config-values.html
index 4e85c050ef..e643a1c957 100644
--- a/tutorial-adding-new-config-values.html
+++ b/tutorial-adding-new-config-values.html
@@ -60,7 +60,7 @@
diff --git a/tutorial-badge-urls.html b/tutorial-badge-urls.html
index d4748fd5cb..9683113c37 100644
--- a/tutorial-badge-urls.html
+++ b/tutorial-badge-urls.html
@@ -77,7 +77,7 @@ badge is for issues, and the parameters are :user/:repo.
diff --git a/tutorial-code-walkthrough.html b/tutorial-code-walkthrough.html
index a910231760..30fb3c651c 100644
--- a/tutorial-code-walkthrough.html
+++ b/tutorial-code-walkthrough.html
@@ -240,7 +240,7 @@ result over the HTTPS connection.
diff --git a/tutorial-deprecating-badges.html b/tutorial-deprecating-badges.html
index 78dfdf8734..8fcaad7dea 100644
--- a/tutorial-deprecating-badges.html
+++ b/tutorial-deprecating-badges.html
@@ -142,7 +142,7 @@ t.create('no longer available (previously number of layers)')
diff --git a/tutorial-input-validation.html b/tutorial-input-validation.html
index eec278d464..fac02c63a4 100644
--- a/tutorial-input-validation.html
+++ b/tutorial-input-validation.html
@@ -103,7 +103,7 @@
diff --git a/tutorial-json-format.html b/tutorial-json-format.html
index fd0661f7f7..4812dda2a6 100644
--- a/tutorial-json-format.html
+++ b/tutorial-json-format.html
@@ -60,7 +60,7 @@ if you have any queries regarding the JSON format.
diff --git a/tutorial-logos.html b/tutorial-logos.html
index a2eeeba310..e63204c6d0 100644
--- a/tutorial-logos.html
+++ b/tutorial-logos.html
@@ -96,7 +96,7 @@
diff --git a/tutorial-performance-testing.html b/tutorial-performance-testing.html
index bac51ddbab..c92f75e838 100644
--- a/tutorial-performance-testing.html
+++ b/tutorial-performance-testing.html
@@ -76,7 +76,7 @@ node --prof-process --preprocess -j isolate-00000244AB6ED3B0-11920-v8.log | flam
diff --git a/tutorial-production-hosting.html b/tutorial-production-hosting.html
index dd9109e3db..17e5e7ce0e 100644
--- a/tutorial-production-hosting.html
+++ b/tutorial-production-hosting.html
@@ -243,7 +243,7 @@ the server. It's generously donated by Sent
diff --git a/tutorial-releases.html b/tutorial-releases.html
index d163a16863..fdfdc1a4e8 100644
--- a/tutorial-releases.html
+++ b/tutorial-releases.html
@@ -79,7 +79,7 @@
diff --git a/tutorial-self-hosting.html b/tutorial-self-hosting.html
index 4b976ba59b..abde1c478e 100644
--- a/tutorial-self-hosting.html
+++ b/tutorial-self-hosting.html
@@ -173,7 +173,7 @@ Set public.requireCloudflare: true.
diff --git a/tutorial-server-secrets.html b/tutorial-server-secrets.html
index f2e065a782..4be0a5a9bb 100644
--- a/tutorial-server-secrets.html
+++ b/tutorial-server-secrets.html
@@ -279,7 +279,7 @@ and create an API key for the YouTube Data API v3.
diff --git a/tutorial-service-tests.html b/tutorial-service-tests.html
index ac77fb733a..df20f2b114 100644
--- a/tutorial-service-tests.html
+++ b/tutorial-service-tests.html
@@ -246,7 +246,7 @@ comment there instead.