gm-pantry 0.3.0 → 0.3.1

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.
Files changed (2) hide show
  1. package/dist/s3.js +31 -3
  2. package/package.json +1 -1
package/dist/s3.js CHANGED
@@ -287,6 +287,36 @@ export class S3Store {
287
287
  }
288
288
  const GENERATED_SHARD = /^shards\/[0-9a-f]{16}\.json$/;
289
289
  const keyOf = (url) => decodeURIComponent(url);
290
+ /**
291
+ * repo.json with its guard. A 412 is a real conflict only if the object's ETag is no longer the expected one:
292
+ * some services answer 412 for an ETag they handed out themselves (quoted versus bare), so when the ETag still
293
+ * matches, retry once with the bare form, then without a guard (the ETag was checked just now).
294
+ */
295
+ async function putGuarded(store, body, put, guard, expected, result) {
296
+ const conflict = () => new ConflictError("repo.json changed while publishing; reload and apply the edit again");
297
+ try {
298
+ await store.putObject("repo.json", body, { ...put, ...guard });
299
+ return;
300
+ }
301
+ catch (e) {
302
+ if (!(e instanceof S3Error) || e.status !== 412)
303
+ throw e;
304
+ if (!expected || (await store.headObject("repo.json"))?.etag !== expected)
305
+ throw conflict();
306
+ }
307
+ const bare = expected.replace(/^W\//, "").replace(/^"(.*)"$/, "$1");
308
+ try {
309
+ await store.putObject("repo.json", body, { ...put, ifMatch: bare });
310
+ }
311
+ catch (e) {
312
+ if (!(e instanceof S3Error) || e.status !== 412)
313
+ throw e;
314
+ if ((await store.headObject("repo.json"))?.etag !== expected)
315
+ throw conflict();
316
+ result.conditional = false;
317
+ await store.putObject("repo.json", body, put);
318
+ }
319
+ }
290
320
  /**
291
321
  * Uploads archives and new shards first and repo.json last, so a reader never sees a repo.json that points at missing files;
292
322
  * then deletes the shards nothing lists any more. repo.json is written with `If-Match` (or `If-None-Match: *`) where the service supports it.
@@ -327,11 +357,9 @@ export async function publishRepo(store, input, o = {}) {
327
357
  const guard = expected ? { ifMatch: expected } : { ifNoneMatch: "*" };
328
358
  const put = { contentType: "application/json", cacheControl: "no-cache" };
329
359
  try {
330
- await store.putObject("repo.json", input.repoJson, { ...put, ...guard });
360
+ await putGuarded(store, input.repoJson, put, guard, expected, result);
331
361
  }
332
362
  catch (e) {
333
- if (e instanceof S3Error && e.status === 412)
334
- throw new ConflictError("repo.json changed while publishing; reload and apply the edit again");
335
363
  if (!(e instanceof S3Error) || (e.status !== 501 && e.code !== "NotImplemented"))
336
364
  throw e;
337
365
  result.conditional = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-pantry",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Game repositories: static directories of game archives with a repo.json, plus a parser, validator and CLI.",
5
5
  "keywords": [
6
6
  "gamemaker",