wyvrnpm 1.1.2 → 1.2.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wyvrnpm",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "A simple, static-hosting-compatible C++ package manager",
5
5
  "keywords": [
6
6
  "c++",
@@ -43,11 +43,17 @@ class FileProvider extends BaseProvider {
43
43
  async publish(files, options) {
44
44
  const { source, platform, name, version } = options;
45
45
 
46
- const destDir = path.join(this._resolvePath(source), platform, name, version);
46
+ const packageDir = path.join(this._resolvePath(source), platform, name);
47
+ const destDir = path.join(packageDir, version);
47
48
  fs.mkdirSync(destDir, { recursive: true });
48
49
 
49
50
  this._copy(files.manifest, path.join(destDir, 'wyvrn.json'));
50
51
  this._copy(files.zip, path.join(destDir, 'wyvrn.zip'));
52
+
53
+ // Write latest.json at the package root so consumers can resolve "latest"
54
+ const latestPath = path.join(packageDir, 'latest.json');
55
+ console.log(`[wyvrn] → ${latestPath}`);
56
+ fs.writeFileSync(latestPath, JSON.stringify({ version }), 'utf8');
51
57
  }
52
58
 
53
59
  _copy(src, dest) {
@@ -18,9 +18,13 @@ class HttpProvider extends BaseProvider {
18
18
  const { source, platform, name, version, token } = options;
19
19
  const base = source.replace(/\/$/, '');
20
20
  const urlBase = `${base}/${platform}/${name}/${version}`;
21
+ const packageBase = `${base}/${platform}/${name}`;
21
22
 
22
23
  await this._put(`${urlBase}/wyvrn.json`, files.manifest, 'application/json', token);
23
24
  await this._put(`${urlBase}/wyvrn.zip`, files.zip, 'application/zip', token);
25
+
26
+ // Write latest.json at the package root so consumers can resolve "latest"
27
+ await this._putBuffer(`${packageBase}/latest.json`, Buffer.from(JSON.stringify({ version })), 'application/json', token);
24
28
  }
25
29
 
26
30
  _put(url, filePath, contentType, token) {
@@ -58,6 +62,41 @@ class HttpProvider extends BaseProvider {
58
62
  req.end();
59
63
  });
60
64
  }
65
+
66
+ _putBuffer(url, buffer, contentType, token) {
67
+ return new Promise((resolve, reject) => {
68
+ const parsed = new URL(url);
69
+ const lib = parsed.protocol === 'https:' ? https : http;
70
+
71
+ const headers = {
72
+ 'Content-Type': contentType,
73
+ 'Content-Length': buffer.length,
74
+ };
75
+ if (token) headers['Authorization'] = `Bearer ${token}`;
76
+
77
+ console.log(`[wyvrn] → PUT ${url}`);
78
+ const req = lib.request(
79
+ {
80
+ hostname: parsed.hostname,
81
+ port: parsed.port || undefined,
82
+ path: parsed.pathname,
83
+ method: 'PUT',
84
+ headers,
85
+ },
86
+ (res) => {
87
+ res.resume();
88
+ if (res.statusCode >= 200 && res.statusCode < 300) {
89
+ resolve();
90
+ } else {
91
+ reject(new Error(`HTTP PUT ${url} failed: ${res.statusCode} ${res.statusMessage}`));
92
+ }
93
+ },
94
+ );
95
+ req.on('error', reject);
96
+ req.write(buffer);
97
+ req.end();
98
+ });
99
+ }
61
100
  }
62
101
 
63
102
  module.exports = HttpProvider;
@@ -67,12 +67,18 @@ class S3Provider extends BaseProvider {
67
67
 
68
68
  // Destination key base: [prefix/]platform/name/version
69
69
  const keyBase = [prefix, platform, name, version].filter(Boolean).join('/');
70
+ // Package root (without version) for latest.json
71
+ const packageBase = [prefix, platform, name].filter(Boolean).join('/');
70
72
 
71
73
  const clientConfig = profile ? { credentials: fromSSO({ profile }) } : {};
72
74
  const client = new S3Client(clientConfig);
73
75
 
74
76
  await this._put(client, bucket, `${keyBase}/wyvrn.json`, files.manifest, 'application/json', PutObjectCommand);
75
77
  await this._put(client, bucket, `${keyBase}/wyvrn.zip`, files.zip, 'application/zip', PutObjectCommand);
78
+
79
+ // Write latest.json at the package root so consumers can resolve "latest"
80
+ const latestContent = Buffer.from(JSON.stringify({ version }));
81
+ await this._putBuffer(client, bucket, `${packageBase}/latest.json`, latestContent, 'application/json', PutObjectCommand);
76
82
  }
77
83
 
78
84
  async _put(client, bucket, key, filePath, contentType, PutObjectCommand) {
@@ -86,6 +92,18 @@ class S3Provider extends BaseProvider {
86
92
  }),
87
93
  );
88
94
  }
95
+
96
+ async _putBuffer(client, bucket, key, content, contentType, PutObjectCommand) {
97
+ console.log(`[wyvrn] → s3://${bucket}/${key}`);
98
+ await client.send(
99
+ new PutObjectCommand({
100
+ Bucket: bucket,
101
+ Key: key,
102
+ Body: content,
103
+ ContentType: contentType,
104
+ }),
105
+ );
106
+ }
89
107
  }
90
108
 
91
109
  module.exports = S3Provider;
package/src/resolve.js CHANGED
@@ -22,6 +22,36 @@ function compareVersions(a, b) {
22
22
  return 0;
23
23
  }
24
24
 
25
+ /**
26
+ * Resolves the "latest" tag for a package by fetching latest.json from the registry.
27
+ *
28
+ * @param {string} name
29
+ * @param {string[]} packageSources
30
+ * @param {string} platform
31
+ * @param {Function} httpClient
32
+ * @returns {Promise<string>} The resolved version string.
33
+ */
34
+ async function resolveLatestVersion(name, packageSources, platform, httpClient) {
35
+ for (const source of packageSources) {
36
+ const base = source.replace(/\/$/, '');
37
+ const candidates = [
38
+ `${base}/${platform}/${name}/latest.json`,
39
+ `${base}/common/${name}/latest.json`,
40
+ ];
41
+ for (const url of candidates) {
42
+ try {
43
+ const response = await httpClient(url);
44
+ if (!response.ok) continue;
45
+ const data = await response.json();
46
+ if (data && data.version) return data.version;
47
+ } catch {
48
+ // Try next candidate.
49
+ }
50
+ }
51
+ }
52
+ throw new Error(`Could not resolve "latest" version for "${name}" from any source`);
53
+ }
54
+
25
55
  /**
26
56
  * Fetches razer.json for a single package from the first source that responds.
27
57
  *
@@ -96,6 +126,17 @@ async function resolveDependencies(rootDeps, packageSources, platform, httpClien
96
126
  version = lockedVersions.get(name);
97
127
  }
98
128
 
129
+ // Resolve the "latest" tag to a concrete version before proceeding.
130
+ if (version === 'latest') {
131
+ try {
132
+ version = await resolveLatestVersion(name, packageSources, platform, httpClient);
133
+ console.log(`[wyvrn] Resolved "${name}@latest" → ${version}`);
134
+ } catch (err) {
135
+ console.warn(`[wyvrn] Warning: ${err.message}`);
136
+ continue;
137
+ }
138
+ }
139
+
99
140
  if (resolved.has(name)) {
100
141
  const existing = resolved.get(name);
101
142
  if (existing === version) {
@@ -154,5 +195,6 @@ async function resolveDependencies(rootDeps, packageSources, platform, httpClien
154
195
 
155
196
  module.exports = {
156
197
  compareVersions,
198
+ resolveLatestVersion,
157
199
  resolveDependencies,
158
200
  };