@summerscar/dsh-mnemon-sync 0.1.9 → 0.1.10

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 (3) hide show
  1. package/index.js +0 -1
  2. package/package.json +1 -1
  3. package/targets.js +11 -2
package/index.js CHANGED
@@ -123,7 +123,6 @@ export function archiveKey(at = new Date(), device = 'device') {
123
123
  const stamp = at.toISOString().replace(/:/gu, '-')
124
124
  return `${ARCHIVE_PREFIX}${device}-${stamp}.zip`
125
125
  }
126
-
127
126
  export function apply(ctx, config = {}) {
128
127
  const dataRoot = resolveDataRoot(config)
129
128
  // `<home>/data` → `<home>`: settings and work files sit beside the store.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@summerscar/dsh-mnemon-sync",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Back up and restore dsh-mnemon memory components (memory spaces, project documents, runtime memory) to S3-compatible storage or WebDAV.",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/targets.js CHANGED
@@ -467,7 +467,10 @@ function encodeRfc3986(value) {
467
467
  * misconfigured, and paging the whole bucket would cost more than it explains.
468
468
  */
469
469
  async function listS3(target, signal) {
470
- const url = new URL(s3Url(target, ''))
470
+ // ListObjectsV2 operates on the bucket: the path is the bucket alone and
471
+ // the prefix carries the folder. s3Url(target, '') would put remotePath in
472
+ // the path, and the store then answers 404 NoSuchKey for the missing object.
473
+ const url = new URL(s3Endpoint(target))
471
474
  url.searchParams.set('list-type', '2')
472
475
  url.searchParams.set('prefix', `${target.remotePath}/`)
473
476
  const { response } = await signedS3UrlRequest(target, 'GET', url, signal)
@@ -519,7 +522,13 @@ async function signedS3UrlRequest(target, method, url, signal) {
519
522
  const stringToSign = ['AWS4-HMAC-SHA256', amzDate, scope, sha256Hex(canonicalRequest)].join('\n')
520
523
  const signingKey = signatureKey(target.secretAccessKey, dateStamp, target.region)
521
524
  const signature = createHmac('sha256', signingKey).update(stringToSign).digest('hex')
522
- const response = await fetch(url, {
525
+ // The signature covers the canonical (RFC 3986) query, but URLSearchParams
526
+ // re-encodes spaces as `+` when the URL stringifies, which would not match
527
+ // what was signed. Serialize the query for the wire with the same encoding.
528
+ const wireQuery = [...url.searchParams.entries()]
529
+ .map(([name, value]) => `${encodeRfc3986(name)}=${encodeRfc3986(value)}`)
530
+ .join('&')
531
+ const response = await fetch(`${url.origin}${url.pathname}?${wireQuery}`, {
523
532
  method,
524
533
  headers: {
525
534
  ...headers,