@powerhousedao/registry 6.2.3-dev.35 → 6.2.3-dev.37

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/dist/cli.d.mts CHANGED
@@ -22,6 +22,7 @@ export declare const registryCommand: Partial<import("cmd-ts/dist/cjs/argparser.
22
22
  workers: number;
23
23
  uplink: string | undefined;
24
24
  uplinkMaxage: string | undefined;
25
+ trustProxy: string | undefined;
25
26
  webEnabled: boolean;
26
27
  publicUrl: string | undefined;
27
28
  authRenown: boolean;
@@ -51,6 +52,7 @@ export declare const registryCommand: Partial<import("cmd-ts/dist/cjs/argparser.
51
52
  workers: number;
52
53
  uplink: string | undefined;
53
54
  uplinkMaxage: string | undefined;
55
+ trustProxy: string | undefined;
54
56
  webEnabled: boolean;
55
57
  publicUrl: string | undefined;
56
58
  authRenown: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.mts","names":[],"sources":["../cli.ts"],"mappings":";qBA2Ia,iBAAe,+CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAkI1B,6CAAA,iDAAA,mDAAA;qBAEW,eAAa,+CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAyCxB,6CAAA,iDAAA,mDAAA;qBAEW,gBAAc,+CAAA;;;;;;;;;;;;IAkBzB,6CAAA,iDAAA,mDAAA;qBAEW,6BAA2B,+CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAatC,6CAAA,iDAAA,mDAAA"}
1
+ {"version":3,"file":"cli.d.mts","names":[],"sources":["../cli.ts"],"mappings":";qBA2Ia,iBAAe,+CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA2I1B,6CAAA,iDAAA,mDAAA;qBAEW,eAAa,+CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAyCxB,6CAAA,iDAAA,mDAAA;qBAEW,gBAAc,+CAAA;;;;;;;;;;;;IAkBzB,6CAAA,iDAAA,mDAAA;qBAEW,6BAA2B,+CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAatC,6CAAA,iDAAA,mDAAA"}
package/dist/cli.mjs CHANGED
@@ -1919,13 +1919,19 @@ function createS3ArtifactStore(config) {
1919
1919
  },
1920
1920
  async putFile(key, file, contentType) {
1921
1921
  const { size } = await fs.promises.stat(file);
1922
- await s3.send(new PutObjectCommand({
1923
- Bucket: config.bucket,
1924
- Key: root + key,
1925
- Body: fs.createReadStream(file),
1926
- ContentLength: size,
1927
- ContentType: contentType
1928
- }));
1922
+ const body = fs.createReadStream(file);
1923
+ body.on("error", () => void 0);
1924
+ try {
1925
+ await s3.send(new PutObjectCommand({
1926
+ Bucket: config.bucket,
1927
+ Key: root + key,
1928
+ Body: body,
1929
+ ContentLength: size,
1930
+ ContentType: contentType
1931
+ }));
1932
+ } finally {
1933
+ body.destroy();
1934
+ }
1929
1935
  },
1930
1936
  async get(key) {
1931
1937
  try {
@@ -2826,9 +2832,16 @@ function inside(root, relative) {
2826
2832
  }
2827
2833
  async function inBatches(items, size, fn) {
2828
2834
  let cursor = 0;
2829
- await Promise.all(Array.from({ length: Math.min(size, items.length) }, async () => {
2830
- while (cursor < items.length) await fn(items[cursor++]);
2831
- }));
2835
+ let failed = false;
2836
+ const rejected = (await Promise.allSettled(Array.from({ length: Math.min(size, items.length) }, async () => {
2837
+ while (!failed && cursor < items.length) try {
2838
+ await fn(items[cursor++]);
2839
+ } catch (err) {
2840
+ failed = true;
2841
+ throw err;
2842
+ }
2843
+ }))).find((lane) => lane.status === "rejected");
2844
+ if (rejected) throw rejected.reason;
2832
2845
  }
2833
2846
  async function packDir(dir) {
2834
2847
  const files = listFiles(dir);
@@ -4761,7 +4774,10 @@ function buildVerdaccioConfig(config) {
4761
4774
  primary_color: "#38C780",
4762
4775
  darkMode: true
4763
4776
  },
4764
- server: { keepAliveTimeout: 60 },
4777
+ server: {
4778
+ keepAliveTimeout: 60,
4779
+ ...config.trustProxy !== void 0 && { trustProxy: config.trustProxy }
4780
+ },
4765
4781
  log: {
4766
4782
  type: "stdout",
4767
4783
  format: "pretty",
@@ -4799,8 +4815,10 @@ async function enqueueSweep(db) {
4799
4815
  return names.rows.length;
4800
4816
  }
4801
4817
  async function hasManifestIndex(db) {
4802
- return (await db.query("SELECT to_regclass('verdaccio_manifests') IS NOT NULL AS exists")).rows[0]?.exists === true;
4818
+ return (await db.query(`SELECT to_regclass('verdaccio_manifests') IS NOT NULL
4819
+ AND to_regclass('verdaccio_packages') IS NOT NULL AS exists`)).rows[0]?.exists === true;
4803
4820
  }
4821
+ const PUBLISHED = "EXISTS (SELECT 1 FROM verdaccio_packages l WHERE l.name = m.name)";
4804
4822
  const UNPUBLISHED = `
4805
4823
  SELECT p.name FROM registry_packages p
4806
4824
  WHERE p.local AND p.updated_at < now() - interval '30 seconds'
@@ -4809,7 +4827,7 @@ const QUICK_CHANGED = `
4809
4827
  SELECT m.name
4810
4828
  FROM verdaccio_manifests m
4811
4829
  LEFT JOIN registry_packages p ON p.name = m.name
4812
- WHERE m.updated_at < now() - interval '30 seconds'
4830
+ WHERE m.updated_at < now() - interval '30 seconds' AND ${PUBLISHED}
4813
4831
  AND (p.name IS NULL OR p.manifest_rev IS DISTINCT FROM m.rev)
4814
4832
  UNION ${UNPUBLISHED}
4815
4833
  LIMIT 1000`;
@@ -4826,7 +4844,7 @@ const FULL_MISSING = `
4826
4844
  FROM verdaccio_manifests m
4827
4845
  CROSS JOIN LATERAL jsonb_array_elements_text(m.versions) AS v(version)
4828
4846
  LEFT JOIN registry_versions r ON r.package = m.name AND r.version = v.version
4829
- WHERE m.updated_at < now() - interval '30 seconds'
4847
+ WHERE m.updated_at < now() - interval '30 seconds' AND ${PUBLISHED}
4830
4848
  AND (r.package IS NULL
4831
4849
  OR (r.status = 'pending' AND r.updated_at < now() - interval '5 minutes'
4832
4850
  AND NOT EXISTS (SELECT 1 FROM registry_jobs j
@@ -4837,7 +4855,7 @@ const FULL_CHANGED = `
4837
4855
  SELECT m.name
4838
4856
  FROM verdaccio_manifests m
4839
4857
  LEFT JOIN registry_packages p ON p.name = m.name
4840
- WHERE m.updated_at < now() - interval '30 seconds'
4858
+ WHERE m.updated_at < now() - interval '30 seconds' AND ${PUBLISHED}
4841
4859
  AND (p.name IS NULL OR p.dist_tags <> m.dist_tags
4842
4860
  OR EXISTS (SELECT 1 FROM registry_versions r
4843
4861
  WHERE r.package = m.name AND NOT m.versions ? r.version))
@@ -4853,6 +4871,22 @@ async function reconcile(db, options = {}) {
4853
4871
  if (queued > 0) await wakeWorkers(db);
4854
4872
  return queued;
4855
4873
  }
4874
+ async function pruneCachedPackages(q, published) {
4875
+ if (!(await q.query("SELECT to_regclass('registry_jobs') IS NOT NULL AS exists")).rows[0]?.exists) return [];
4876
+ const pruned = (await q.query(`DELETE FROM verdaccio_packages p
4877
+ WHERE p.name <> ALL($1::text[])
4878
+ AND NOT EXISTS (SELECT 1 FROM registry_package_owners o
4879
+ WHERE o.package_name = p.name)
4880
+ AND NOT EXISTS (SELECT 1 FROM registry_packages r
4881
+ WHERE r.name = p.name AND r.listed_manifest IS NOT NULL)
4882
+ RETURNING p.name`, [published])).rows.map((row) => row.name);
4883
+ if (pruned.length) {
4884
+ await q.query("DELETE FROM registry_jobs WHERE package = ANY($1) AND locked_by IS NULL", [pruned]);
4885
+ await q.query("DELETE FROM registry_versions WHERE package = ANY($1)", [pruned]);
4886
+ await q.query("DELETE FROM registry_packages WHERE name = ANY($1)", [pruned]);
4887
+ }
4888
+ return pruned;
4889
+ }
4856
4890
  function publisherOf(job) {
4857
4891
  const by = job.payload.publishedBy;
4858
4892
  return by?.address ? by : void 0;
@@ -5057,8 +5091,11 @@ async function seedVerdaccioSecret(databaseUrl, secret) {
5057
5091
  const MIB = 1048576;
5058
5092
  const DRAIN_DELAY_MS = 5e3;
5059
5093
  const DRAIN_TIMEOUT_MS = 2e4;
5094
+ function parseTrustProxy(value) {
5095
+ return /^\d+$/.test(value) ? Number(value) : value;
5096
+ }
5060
5097
  async function runRegistry(args) {
5061
- const { port, storageDir, cdnCacheDir, uplink, uplinkMaxage, webEnabled, webhooks, publicUrl, authRenown, renownUrl, verdaccioSecret, localPackages, databaseUrl, pluginsDir, authStore } = args;
5098
+ const { port, storageDir, cdnCacheDir, uplink, uplinkMaxage, trustProxy, webEnabled, webhooks, publicUrl, authRenown, renownUrl, verdaccioSecret, localPackages, databaseUrl, pluginsDir, authStore } = args;
5062
5099
  const storagePath = await resolveDir(storageDir);
5063
5100
  const cdnCachePath = await resolveDir(cdnCacheDir);
5064
5101
  const s3 = s3From(args);
@@ -5084,6 +5121,7 @@ async function runRegistry(args) {
5084
5121
  uplink,
5085
5122
  uplinkMaxage,
5086
5123
  webEnabled,
5124
+ ...trustProxy ? { trustProxy: parseTrustProxy(trustProxy) } : {},
5087
5125
  ...localPackagePatterns?.length ? { localPackagePatterns } : {},
5088
5126
  ...renownEnabled && publicUrl ? { renown: {
5089
5127
  publicUrl,
@@ -5271,27 +5309,6 @@ async function runMigrate(databaseUrl, options) {
5271
5309
  await db.close();
5272
5310
  }
5273
5311
  }
5274
- async function storedPackageNames(s3, config) {
5275
- const prefix = config.keyPrefix ?? "";
5276
- const names = /* @__PURE__ */ new Set();
5277
- let token;
5278
- do {
5279
- const page = await s3.send(new ListObjectsV2Command({
5280
- Bucket: config.bucket,
5281
- Prefix: prefix,
5282
- ContinuationToken: token
5283
- }));
5284
- for (const { Key } of page.Contents ?? []) {
5285
- const rel = Key?.slice(prefix.length) ?? "";
5286
- if (!rel.endsWith("/package.json") || rel.startsWith("artifacts/")) continue;
5287
- const name = rel.slice(0, -13);
5288
- const depth = name.split("/").length;
5289
- if (name.startsWith("@") ? depth === 2 : depth === 1) names.add(name);
5290
- }
5291
- token = page.IsTruncated ? page.NextContinuationToken : void 0;
5292
- } while (token);
5293
- return names;
5294
- }
5295
5312
  async function readStoredManifest(s3, config, name) {
5296
5313
  try {
5297
5314
  const res = await s3.send(new GetObjectCommand({
@@ -5327,9 +5344,9 @@ async function runImportVerdaccioState(args) {
5327
5344
  const name = err.name;
5328
5345
  if (name !== "NoSuchKey" && name !== "NotFound") throw err;
5329
5346
  }
5330
- const names = await storedPackageNames(s3, s3Config);
5331
- for (const name of legacy.list ?? []) names.add(name);
5347
+ const names = new Set(legacy.list ?? []);
5332
5348
  const pool = new pg.Pool({ connectionString: args.databaseUrl });
5349
+ let pruned = [];
5333
5350
  try {
5334
5351
  const store = new S3DatabasePostgres(pool, pluginLogger);
5335
5352
  await store.init();
@@ -5338,6 +5355,7 @@ async function runImportVerdaccioState(args) {
5338
5355
  if (manifest) await store.record(name, manifest);
5339
5356
  }
5340
5357
  for (const name of names) await pool.query("INSERT INTO verdaccio_packages (name) VALUES ($1) ON CONFLICT (name) DO NOTHING", [name]);
5358
+ pruned = await pruneCachedPackages(pool, [...names]);
5341
5359
  for (const token of legacy.tokens ?? []) await pool.query(`INSERT INTO verdaccio_tokens ("user", key, token) VALUES ($1, $2, $3)
5342
5360
  ON CONFLICT ("user", key) DO NOTHING`, [
5343
5361
  token.user,
@@ -5349,7 +5367,7 @@ async function runImportVerdaccioState(args) {
5349
5367
  } finally {
5350
5368
  await pool.end();
5351
5369
  }
5352
- console.log(`[registry] imported ${names.size} package name(s), ${legacy.tokens?.length ?? 0} token(s)${legacy.secret ? " and the secret" : ""}`);
5370
+ console.log(`[registry] imported ${names.size} package name(s), ${legacy.tokens?.length ?? 0} token(s)${legacy.secret ? " and the secret" : ""}; pruned ${pruned.length} cached package(s)`);
5353
5371
  }
5354
5372
  //#endregion
5355
5373
  //#region cli.ts
@@ -5500,6 +5518,13 @@ const registryCommand = command({
5500
5518
  defaultValue: () => process.env.PH_REGISTRY_UPLINK_MAXAGE,
5501
5519
  defaultValueIsSerializable: true
5502
5520
  }),
5521
+ trustProxy: option({
5522
+ long: "trust-proxy",
5523
+ type: optional(string),
5524
+ description: "Express `trust proxy` (e.g. 2 proxy hops), so rate limits key on client IPs; env PH_REGISTRY_TRUST_PROXY",
5525
+ defaultValue: () => process.env.PH_REGISTRY_TRUST_PROXY,
5526
+ defaultValueIsSerializable: true
5527
+ }),
5503
5528
  webEnabled: flag({
5504
5529
  long: "web-enabled",
5505
5530
  defaultValue: () => process.env.REGISTRY_WEB !== "false",
@@ -5546,6 +5571,7 @@ const registryCommand = command({
5546
5571
  databaseUrl: redact(args.databaseUrl),
5547
5572
  listenDatabaseUrl: redact(args.listenDatabaseUrl),
5548
5573
  verdaccioSecret: redact(args.verdaccioSecret),
5574
+ s3AccessKeyId: redact(args.s3AccessKeyId),
5549
5575
  s3SecretAccessKey: redact(args.s3SecretAccessKey)
5550
5576
  });
5551
5577
  try {