@acodeninja/persist 3.3.0 → 3.3.1-next.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.
@@ -23,40 +23,35 @@ export class Tag extends Persist.Model {
23
23
  await connection.put(new Tag({tag: 'documentation'}));
24
24
  ```
25
25
 
26
- ### Versioning with S3 Buckets
26
+ ### Caching and stale reads
27
27
 
28
- When you use versioning with an S3 Bucket, you may have to set `pragma` header and `ResponseCacheControl` metadata on all requests. This can be done by adding middleware for both the `build` and `serialize` steps for each request:
28
+ The engine stamps a `Cache-Control` header on every object it writes (models,
29
+ find indexes, and search indexes). By default this is `no-cache`, which lets a
30
+ browser store the object body but requires it to revalidate against S3 (a
31
+ conditional GET on the ETag) before reuse — S3 answers `304 Not Modified` when
32
+ nothing changed, or `200` with fresh data when it did.
29
33
 
30
- ```javascript
31
- import Persist from "@acodeninja/persist";
32
- import {S3Client} from "@aws-sdk/client-s3";
33
- import S3StorageEngine from "@acodeninja/persist/storage/s3";
34
+ This matters in the browser: without a stored cache directive, browsers apply
35
+ *heuristic caching* to S3 objects and can serve a stale `_index.json` shortly
36
+ after a write — showing missing new records or lingering deleted ones until a
37
+ hard reload. Stamping `no-cache` on writes avoids this, and because the directive
38
+ travels on the object itself, no read-side or client configuration is needed.
34
39
 
35
- const client = new S3Client();
36
-
37
- client.middlewareStack.add(
38
- (next, context) => (args) => {
39
- args.request.headers['pragma'] = 'no-cache';
40
- return next(args);
41
- },
42
- {step: 'build'},
43
- );
44
-
45
- client.middlewareStack.add(
46
- (next, context) => (args) => {
47
- args.input.ResponseCacheControl = 'no-cache';
48
- return next(args);
49
- },
50
- {step: 'serialize'},
51
- );
40
+ If you knowingly serve immutable data and want to opt out (or use a different
41
+ directive), set `cacheControl` on the engine configuration:
52
42
 
43
+ ```javascript
53
44
  const connection = Persist.registerConnection('remote', new S3StorageEngine({
54
45
  bucket: 'test-bucket',
55
- client,
46
+ client: new S3Client(),
47
+ cacheControl: 'max-age=31536000, immutable',
56
48
  }));
57
49
  ```
58
50
 
59
- These changes will ensure that all requests are made with a `no-cache` header set for getting and putting objects to the S3 bucket.
51
+ > Objects written before this behaviour existed keep serving without the header
52
+ > until they are rewritten. Indexes fix themselves on the next mutation; to update
53
+ > an entire bucket at once you can run:
54
+ > `aws s3 cp s3://bucket s3://bucket --recursive --metadata-directive REPLACE --cache-control no-cache`
60
55
 
61
56
  ## HTTP Storage StorageEngine
62
57
 
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@acodeninja/persist",
3
- "version": "3.3.0",
3
+ "version": "3.3.1-next.1",
4
4
  "description": "A JSON based data modelling and persistence module with alternate storage mechanisms.",
5
5
  "type": "module",
6
6
  "scripts": {
7
- "test": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" npx jest",
8
- "test:watch": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" npx jest --watch",
9
- "test:coverage": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" npx jest --collect-coverage",
7
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
8
+ "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch",
9
+ "test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --collect-coverage",
10
10
  "lint": "eslint ./",
11
11
  "prepare": "husky"
12
12
  },
@@ -22,27 +22,27 @@
22
22
  "provenance": true
23
23
  },
24
24
  "dependencies": {
25
- "ajv": "^8.17.1",
25
+ "ajv": "^8.20.0",
26
26
  "ajv-errors": "^3.0.0",
27
27
  "ajv-formats": "^3.0.1",
28
- "fuse.js": "^7.1.0",
29
- "lodash": "^4.17.21",
30
- "slugify": "^1.6.6",
31
- "ulid": "^2.3.0"
28
+ "fuse.js": "^7.5.0",
29
+ "lodash": "^4.18.1",
30
+ "slugify": "^1.6.9",
31
+ "ulid": "^3.0.2"
32
32
  },
33
33
  "optionalDependencies": {
34
- "@aws-sdk/client-s3": "^3.735.0"
34
+ "@aws-sdk/client-s3": "^3.1118.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@commitlint/cli": "^19.6.1",
38
- "@commitlint/config-conventional": "^19.6.0",
39
- "@eslint/js": "^9.19.0",
40
- "@jest/globals": "^29.7.0",
37
+ "@commitlint/cli": "^21.2.2",
38
+ "@commitlint/config-conventional": "^21.2.2",
39
+ "@eslint/js": "^10.0.1",
40
+ "@jest/globals": "^30.4.1",
41
41
  "@semantic-release/commit-analyzer": "^13.0.1",
42
- "eslint": "^9.19.0",
43
- "globals": "^15.14.0",
42
+ "eslint": "^10.9.1",
43
+ "globals": "^17.11.0",
44
44
  "husky": "^9.1.7",
45
- "jest": "^29.7.0",
46
- "semantic-release": "^24.2.1"
45
+ "jest": "^30.4.2",
46
+ "semantic-release": "^25.0.9"
47
47
  }
48
48
  }
@@ -15,6 +15,7 @@ class S3StorageEngine extends StorageEngine {
15
15
  * @param {string} [configuration.bucket] - Hostname and protocol of the HTTP service to use (ie: https://example.com).
16
16
  * @param {string?} [configuration.prefix] - The prefix on the host to perform operations against.
17
17
  * @param {S3Client} [configuration.client] - The http client that implements fetch.
18
+ * @param {string?} [configuration.cacheControl] - Cache-Control header stamped on written objects. Defaults to 'no-cache'.
18
19
  */
19
20
  constructor(configuration) {
20
21
  super(configuration);
@@ -59,6 +60,7 @@ class S3StorageEngine extends StorageEngine {
59
60
  Body: JSON.stringify(model),
60
61
  Bucket: this.configuration.bucket,
61
62
  ContentType: 'application/json',
63
+ CacheControl: this.configuration.cacheControl ?? 'no-cache',
62
64
  }));
63
65
  }
64
66
 
@@ -120,6 +122,7 @@ class S3StorageEngine extends StorageEngine {
120
122
  Bucket: this.configuration.bucket,
121
123
  Body: JSON.stringify(index),
122
124
  ContentType: 'application/json',
125
+ CacheControl: this.configuration.cacheControl ?? 'no-cache',
123
126
  }));
124
127
  }
125
128
 
@@ -160,6 +163,7 @@ class S3StorageEngine extends StorageEngine {
160
163
  Bucket: this.configuration.bucket,
161
164
  Body: JSON.stringify(index),
162
165
  ContentType: 'application/json',
166
+ CacheControl: this.configuration.cacheControl ?? 'no-cache',
163
167
  }));
164
168
  }
165
169