@preventive/upstream 1.0.0-alpha.3 → 1.0.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/semver.js CHANGED
@@ -1,9 +1,12 @@
1
1
  import assert from 'node:assert/strict'
2
2
  import { createRequire } from 'node:module'
3
3
  import { basename, dirname, resolve } from 'node:path'
4
+ import requirePeer from './semver-peer.cjs'
4
5
 
5
6
  // npm's own semver, borrowed from the npm next to node rather than added
6
- // as a dependency: the same implementation `npm audit` uses.
7
+ // as a dependency: the same implementation `npm audit` uses. Where there is
8
+ // no npm, as in a bundled serverless function, the `semver` package itself,
9
+ // an optional peer dependency.
7
10
 
8
11
  // Plain releases that semver.valid answers unchanged, so it need not be
9
12
  // loaded for them: no leading zeros, and at most 15 digits a part, under
@@ -11,26 +14,37 @@ import { basename, dirname, resolve } from 'node:path'
11
14
  // to semver.
12
15
  const PLAIN_RELEASE = /^(?:0|[1-9]\d{0,14})\.(?:0|[1-9]\d{0,14})\.(?:0|[1-9]\d{0,14})$/u
13
16
 
14
- let found
17
+ // What load() returns if it is semver, or null if it is not or throws.
18
+ function attempt(load) {
19
+ try {
20
+ const lib = load()
21
+ if (['compare', 'intersects', 'satisfies', 'valid', 'validRange'].every((name) => typeof lib?.[name] === 'function')) return lib
22
+ } catch {}
23
+ return null
24
+ }
15
25
 
16
26
  function find() {
17
27
  const argv0 = process.argv[0]
18
28
  // Another host executable's `../lib` is not npm's.
19
- if (!['node', 'node.exe'].includes(basename(argv0 ?? ''))) return null
20
- const require = createRequire(import.meta.url)
21
- // POSIX keeps npm in <prefix>/lib beside <prefix>/bin/node; Windows, beside node.exe.
22
- for (const prefix of ['../lib', '.']) {
23
- try {
24
- const lib = require(resolve(dirname(argv0), prefix, 'node_modules/npm/node_modules/semver'))
25
- if (['compare', 'intersects', 'satisfies', 'valid', 'validRange'].every((name) => typeof lib?.[name] === 'function')) return lib
26
- } catch {}
29
+ if (['node', 'node.exe'].includes(basename(argv0 ?? ''))) {
30
+ // POSIX keeps npm in <prefix>/lib beside <prefix>/bin/node; Windows, beside node.exe.
31
+ for (const prefix of ['../lib', '.']) {
32
+ // Required from itself, which a bundle of any format leaves alone.
33
+ const path = resolve(dirname(argv0), prefix, 'node_modules/npm/node_modules/semver')
34
+ const lib = attempt(() => createRequire(path)(path))
35
+ if (lib) return lib
36
+ }
27
37
  }
28
- return null
38
+ // The peer, by the plain require() a bundler follows; or, where an ESM
39
+ // bundle leaves it out, by node's own require() from beside the bundle.
40
+ return attempt(requirePeer) ?? attempt(() => createRequire(import.meta.url)('semver'))
29
41
  }
30
42
 
43
+ let found
44
+
31
45
  function semver() {
32
46
  if (found === undefined) found = find()
33
- assert.ok(found, 'semver: no npm beside node to borrow it from')
47
+ assert.ok(found, 'semver: no npm beside node to borrow it from, and the semver peer dependency is not installed')
34
48
  return found
35
49
  }
36
50
 
package/src/soldeer.js CHANGED
@@ -1,16 +1,10 @@
1
1
  import assert from 'node:assert/strict'
2
2
 
3
- import { assertSha256, assertion, matches, show } from './args.js'
3
+ import { assertSha256, assertSoldeerPackage, show } from './args.js'
4
4
  import { verifiedDownload } from './download.js'
5
5
  import { SOLDEER_API, SOLDEER_REVISIONS, buildUrl, request } from './http.js'
6
6
 
7
7
  const DIR = 'soldeer/zips' // No expiry: the registry refuses a version it already has.
8
- // Soldeer's own rule for a name: 3 to 100 of lowercase letters, digits and
9
- // `-`, starting with one of them or `@`, ending with a letter or digit.
10
- const assertName = assertion('a Soldeer package name', matches(/^(?=.{3,100}$)[@\da-z][\da-z-]*[\da-z]$/u))
11
- // Soldeer takes any version but an empty one. Its registry has semver
12
- // (`5.7.0-rc.0`, `1.0.2-solc-0.8-simulate`), bare numbers and commit hashes.
13
- const assertVersion = assertion('letters, digits, `.`, `_`, `+` and `-`', matches(/^(?=.{1,128}$)[\dA-Za-z][\w.+-]*$/u))
14
8
 
15
9
  // Where the registry keeps the version's zip; it says nothing of what the
16
10
  // zip hashes to.
@@ -26,8 +20,7 @@ async function zipUrl(name, version) {
26
20
  // The registry has no hash to check a zip against, so the caller brings
27
21
  // one, as soldeer.lock records it.
28
22
  export async function getZip(name, version, checksum) {
29
- assertName('getZip', 'name', name)
30
- assertVersion('getZip', 'version', version)
23
+ assertSoldeerPackage('getZip', name, version)
31
24
  assertSha256('getZip', 'checksum', checksum)
32
25
  return await verifiedDownload({ method: 'getZip', dir: DIR, what: `${name}@${version}`, ext: 'zip', algorithm: 'sha256', expected: checksum, locate: () => zipUrl(name, version) })
33
26
  }
package/src/tree.js CHANGED
@@ -28,7 +28,7 @@ const octal = (header, start, end) => {
28
28
  const text = field(header, start, end).trim()
29
29
  return /^[0-7]{1,12}$/u.test(text) ? Number.parseInt(text, 8) : Number.NaN
30
30
  }
31
- const objectId = (type, content) => createHash('sha1').update(`${type} ${content.length}\0`).update(content).digest()
31
+ export const objectId = (type, content) => createHash('sha1').update(`${type} ${content.length}\0`).update(content).digest()
32
32
  const checksum = (header) => header.reduce((sum, byte, i) => sum + (i >= 148 && i < 156 ? 32 : byte), 0)
33
33
  const isZero = (bytes) => bytes.every((byte) => byte === 0)
34
34
 
@@ -64,9 +64,9 @@ function emptyDirs(dir, prefix = '') {
64
64
  // Git sorts a subtree as if its name ended in `/`.
65
65
  function treeId(dir) {
66
66
  const entries = [...dir].map(([name, entry]) => (entry instanceof Map ? { mode: '40000', name, id: treeId(entry) } : { ...entry, name }))
67
- const key = ({ mode, name }) => (mode === '40000' ? `${name}/` : name)
68
- entries.sort((a, b) => (key(a) < key(b) ? -1 : Number(key(a) > key(b))))
69
- return objectId('tree', Buffer.concat(entries.flatMap(({ mode, name, id }) => [Buffer.from(`${mode} ${name}\0`, 'latin1'), id])))
67
+ const keyed = entries.map((entry) => [entry.mode === '40000' ? `${entry.name}/` : entry.name, entry])
68
+ keyed.sort(([a], [b]) => (a < b ? -1 : Number(a > b)))
69
+ return objectId('tree', Buffer.concat(keyed.flatMap(([, { mode, name, id }]) => [Buffer.from(`${mode} ${name}\0`, 'latin1'), id])))
70
70
  }
71
71
 
72
72
  const EMPTY_TREE = objectId('tree', Buffer.alloc(0)).toString('hex')
@@ -90,11 +90,10 @@ async function holdsNothing(sha, listed) {
90
90
  // names any, and one is put back only once its own listings show it holds
91
91
  // nothing.
92
92
  async function putBackEmptyTrees(dir, sha, listed) {
93
- if (treeId(dir).toString('hex') === sha) return
94
93
  for (const entry of await listed(sha)) {
95
94
  if (entry.type !== 'tree') continue
96
95
  const here = dir.get(entry.name)
97
- if (here instanceof Map) await putBackEmptyTrees(here, entry.sha, listed)
96
+ if (here instanceof Map && treeId(here).toString('hex') !== entry.sha) await putBackEmptyTrees(here, entry.sha, listed)
98
97
  else if (here === undefined && await holdsNothing(entry.sha, listed)) dir.set(entry.name, subtree(entry.sha))
99
98
  }
100
99
  }
@@ -177,6 +176,8 @@ export async function gitTreeOfTarball(gzipped, { expected, list } = {}) {
177
176
  if (!isSha1(sha)) return `no tree: an empty directory, ${JSON.stringify(path)}, and no submodule there`
178
177
  dir.set(name, { mode: '160000', id: Buffer.from(sha, 'hex') })
179
178
  }
179
+ const id = treeId(root).toString('hex')
180
+ if (id === expected) return id
180
181
  await putBackEmptyTrees(root, expected, listed)
181
182
  return treeId(root).toString('hex')
182
183
  }
File without changes