slonik-interceptor-query-cache 3.3.1 → 43.0.6

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/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # slonik-interceptor-query-cache
2
+
3
+ ## 43.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [#591](https://github.com/gajus/slonik/pull/591) [`30e89a6`](https://github.com/gajus/slonik/commit/30e89a6f2ab1fc8f9d010bb0157ce41aa4da80e8) Thanks [@gajus](https://github.com/gajus)! - add slonik-interceptor-query-cache to monorepo
8
+
9
+ - Updated dependencies [[`30e89a6`](https://github.com/gajus/slonik/commit/30e89a6f2ab1fc8f9d010bb0157ce41aa4da80e8), [`30e89a6`](https://github.com/gajus/slonik/commit/30e89a6f2ab1fc8f9d010bb0157ce41aa4da80e8)]:
10
+ - slonik@43.0.6
@@ -1 +1 @@
1
- {"version":3,"file":"createQueryCacheInterceptor.d.ts","sourceRoot":"","sources":["../../src/factories/createQueryCacheInterceptor.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,KAAK,EACV,KAAK,WAAW,EAChB,KAAK,cAAc,EACpB,MAAM,QAAQ,CAAC;AAYhB,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,KAAK,OAAO,GAAG;IACb,GAAG,EAAE,CACH,KAAK,EAAE,KAAK,EACZ,eAAe,EAAE,eAAe,KAC7B,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,CAAC;IACjD,GAAG,EAAE,CACH,KAAK,EAAE,KAAK,EACZ,eAAe,EAAE,eAAe,EAChC,WAAW,EAAE,WAAW,CAAC,cAAc,CAAC,KACrC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACxB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAMF,eAAO,MAAM,2BAA2B,uBAClB,kBAAkB,KACrC,WAiFF,CAAC"}
1
+ {"version":3,"file":"createQueryCacheInterceptor.d.ts","sourceRoot":"","sources":["../../src/factories/createQueryCacheInterceptor.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,KAAK,EACV,KAAK,WAAW,EAChB,KAAK,cAAc,EACpB,MAAM,QAAQ,CAAC;AAYhB,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,KAAK,OAAO,GAAG;IACb,GAAG,EAAE,CACH,KAAK,EAAE,KAAK,EACZ,eAAe,EAAE,eAAe,KAC7B,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,CAAC;IACjD,GAAG,EAAE,CACH,KAAK,EAAE,KAAK,EACZ,eAAe,EAAE,eAAe,EAChC,WAAW,EAAE,WAAW,CAAC,cAAc,CAAC,KACrC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACxB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAMF,eAAO,MAAM,2BAA2B,uBAClB,kBAAkB,KACrC,WAiFF,CAAC"}
@@ -2,7 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createQueryCacheInterceptor = void 0;
4
4
  const Logger_1 = require("../Logger");
5
- const utilities_1 = require("../utilities");
5
+ const extractCacheAttributes_1 = require("../utilities/extractCacheAttributes");
6
+ const normalizeCacheAttributes_1 = require("../utilities/normalizeCacheAttributes");
6
7
  const log = Logger_1.Logger.child({
7
8
  namespace: 'createQueryCacheInterceptor',
8
9
  });
@@ -49,11 +50,11 @@ const createQueryCacheInterceptor = (configurationInput) => {
49
50
  if (context.transactionId) {
50
51
  return null;
51
52
  }
52
- const extractedCacheAttributes = (0, utilities_1.extractCacheAttributes)(query.sql, query.values);
53
+ const extractedCacheAttributes = (0, extractCacheAttributes_1.extractCacheAttributes)(query.sql, query.values);
53
54
  if (!extractedCacheAttributes) {
54
55
  return null;
55
56
  }
56
- const cacheAttributes = (0, utilities_1.normalizeCacheAttributes)(extractedCacheAttributes);
57
+ const cacheAttributes = (0, normalizeCacheAttributes_1.normalizeCacheAttributes)(extractedCacheAttributes);
57
58
  context.sandbox.cache = {
58
59
  cacheAttributes,
59
60
  };
@@ -1 +1 @@
1
- {"version":3,"file":"createQueryCacheInterceptor.js","sourceRoot":"","sources":["../../src/factories/createQueryCacheInterceptor.ts"],"names":[],"mappings":";;;AAAA,sCAAmC;AACnC,4CAAgF;AAQhF,MAAM,GAAG,GAAG,eAAM,CAAC,KAAK,CAAC;IACvB,SAAS,EAAE,6BAA6B;CACzC,CAAC,CAAC;AAkCI,MAAM,2BAA2B,GAAG,CACzC,kBAAsC,EACzB,EAAE;IACf,MAAM,aAAa,GAAkB;QACnC,GAAG,kBAAkB;KACtB,CAAC;IAEF,OAAO;QACL,oBAAoB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YAC7C,IAAI,OAAO,CAAC,aAAa,EAAE;gBACzB,OAAO,IAAI,CAAC;aACb;YAED,MAAM,eAAe,GAAI,OAAO,CAAC,OAAmB,CAAC,KAAK;gBACxD,EAAE,eAAe,CAAC;YAEpB,IAAI,CAAC,eAAe,EAAE;gBACpB,OAAO,IAAI,CAAC;aACb;YAED,MAAM,WAAW,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,GAAG,CACjD,KAAK,EACL,eAAe,CAChB,CAAC;YAEF,IAAI,WAAW,EAAE;gBACf,GAAG,CAAC,KAAK,CACP;oBACE,OAAO,EAAE,OAAO,CAAC,OAAO;iBACzB,EACD,4BAA4B,CAC7B,CAAC;gBAEF,OAAO,WAAW,CAAC;aACpB;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QACD,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YAClD,IAAI,OAAO,CAAC,aAAa,EAAE;gBACzB,OAAO,IAAI,CAAC;aACb;YAED,MAAM,eAAe,GAAI,OAAO,CAAC,OAAmB,CAAC,KAAK;gBACxD,EAAE,eAAe,CAAC;YAEpB,IAAI,eAAe,EAAE;gBACnB,IAAI,eAAe,CAAC,YAAY,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE;oBACzD,GAAG,CAAC,KAAK,CACP,wEAAwE,CACzE,CAAC;iBACH;qBAAM;oBACL,MAAM,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;iBACjE;aACF;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QACD,oBAAoB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YAC7C,IAAI,OAAO,CAAC,aAAa,EAAE;gBACzB,OAAO,IAAI,CAAC;aACb;YAED,MAAM,wBAAwB,GAAG,IAAA,kCAAsB,EACrD,KAAK,CAAC,GAAG,EACT,KAAK,CAAC,MAAM,CACb,CAAC;YAEF,IAAI,CAAC,wBAAwB,EAAE;gBAC7B,OAAO,IAAI,CAAC;aACb;YAED,MAAM,eAAe,GAAG,IAAA,oCAAwB,EAC9C,wBAAwB,CACzB,CAAC;YAEF,OAAO,CAAC,OAAO,CAAC,KAAK,GAAG;gBACtB,eAAe;aAChB,CAAC;YAEF,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAnFW,QAAA,2BAA2B,+BAmFtC"}
1
+ {"version":3,"file":"createQueryCacheInterceptor.js","sourceRoot":"","sources":["../../src/factories/createQueryCacheInterceptor.ts"],"names":[],"mappings":";;;AAAA,sCAAmC;AACnC,gFAA6E;AAC7E,oFAAiF;AAQjF,MAAM,GAAG,GAAG,eAAM,CAAC,KAAK,CAAC;IACvB,SAAS,EAAE,6BAA6B;CACzC,CAAC,CAAC;AAkCI,MAAM,2BAA2B,GAAG,CACzC,kBAAsC,EACzB,EAAE;IACf,MAAM,aAAa,GAAkB;QACnC,GAAG,kBAAkB;KACtB,CAAC;IAEF,OAAO;QACL,oBAAoB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YAC7C,IAAI,OAAO,CAAC,aAAa,EAAE;gBACzB,OAAO,IAAI,CAAC;aACb;YAED,MAAM,eAAe,GAAI,OAAO,CAAC,OAAmB,CAAC,KAAK;gBACxD,EAAE,eAAe,CAAC;YAEpB,IAAI,CAAC,eAAe,EAAE;gBACpB,OAAO,IAAI,CAAC;aACb;YAED,MAAM,WAAW,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,GAAG,CACjD,KAAK,EACL,eAAe,CAChB,CAAC;YAEF,IAAI,WAAW,EAAE;gBACf,GAAG,CAAC,KAAK,CACP;oBACE,OAAO,EAAE,OAAO,CAAC,OAAO;iBACzB,EACD,4BAA4B,CAC7B,CAAC;gBAEF,OAAO,WAAW,CAAC;aACpB;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QACD,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YAClD,IAAI,OAAO,CAAC,aAAa,EAAE;gBACzB,OAAO,IAAI,CAAC;aACb;YAED,MAAM,eAAe,GAAI,OAAO,CAAC,OAAmB,CAAC,KAAK;gBACxD,EAAE,eAAe,CAAC;YAEpB,IAAI,eAAe,EAAE;gBACnB,IAAI,eAAe,CAAC,YAAY,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE;oBACzD,GAAG,CAAC,KAAK,CACP,wEAAwE,CACzE,CAAC;iBACH;qBAAM;oBACL,MAAM,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;iBACjE;aACF;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QACD,oBAAoB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YAC7C,IAAI,OAAO,CAAC,aAAa,EAAE;gBACzB,OAAO,IAAI,CAAC;aACb;YAED,MAAM,wBAAwB,GAAG,IAAA,+CAAsB,EACrD,KAAK,CAAC,GAAG,EACT,KAAK,CAAC,MAAM,CACb,CAAC;YAEF,IAAI,CAAC,wBAAwB,EAAE;gBAC7B,OAAO,IAAI,CAAC;aACb;YAED,MAAM,eAAe,GAAG,IAAA,mDAAwB,EAC9C,wBAAwB,CACzB,CAAC;YAEF,OAAO,CAAC,OAAO,CAAC,KAAK,GAAG;gBACtB,eAAe;aAChB,CAAC;YAEF,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAnFW,QAAA,2BAA2B,+BAmFtC"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { createQueryCacheInterceptor } from './factories';
1
+ export { createQueryCacheInterceptor } from './factories/createQueryCacheInterceptor';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,yCAAyC,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createQueryCacheInterceptor = void 0;
4
- var factories_1 = require("./factories");
5
- Object.defineProperty(exports, "createQueryCacheInterceptor", { enumerable: true, get: function () { return factories_1.createQueryCacheInterceptor; } });
4
+ var createQueryCacheInterceptor_1 = require("./factories/createQueryCacheInterceptor");
5
+ Object.defineProperty(exports, "createQueryCacheInterceptor", { enumerable: true, get: function () { return createQueryCacheInterceptor_1.createQueryCacheInterceptor; } });
6
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAA0D;AAAjD,wHAAA,2BAA2B,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uFAAsF;AAA7E,0IAAA,2BAA2B,OAAA"}
package/package.json CHANGED
@@ -16,55 +16,47 @@
16
16
  ]
17
17
  },
18
18
  "dependencies": {
19
- "roarr": "^7.14.0",
19
+ "roarr": "^7.21.1",
20
20
  "strip-comments": "^2.0.1"
21
21
  },
22
- "description": "Cache Slonik query results.",
22
+ "description": "Logs Slonik queries.",
23
23
  "devDependencies": {
24
- "@istanbuljs/nyc-config-typescript": "^1.0.2",
25
- "@semantic-release/commit-analyzer": "^9.0.2",
26
- "@semantic-release/github": "^8.0.7",
27
- "@semantic-release/npm": "^9.0.2",
28
- "ava": "^3.15.0",
29
- "coveralls": "^3.1.1",
30
- "eslint": "^8.39.0",
31
- "eslint-config-canonical": "^41.0.3",
32
- "husky": "^8.0.0",
33
- "inline-loops.macro": "^1.2.2",
34
- "nyc": "^15.1.0",
35
- "semantic-release": "^20.1.3",
36
- "sinon": "^12.0.1",
37
- "ts-node": "^10.9.1",
38
- "typescript": "^5.0.4"
24
+ "ava": "^5.3.1",
25
+ "eslint": "^8.57.0",
26
+ "eslint-config-canonical": "^42.8.1",
27
+ "slonik": "^43.0.6",
28
+ "ts-node": "^10.4.0",
29
+ "typescript": "^4.5.3"
39
30
  },
40
31
  "engines": {
41
- "node": ">=16.0"
32
+ "node": ">=18"
42
33
  },
43
- "files": [
44
- "dist"
45
- ],
46
34
  "keywords": [
47
35
  "postgresql",
48
36
  "interceptor",
49
- "format"
37
+ "logging"
50
38
  ],
51
39
  "license": "BSD-3-Clause",
52
40
  "main": "./dist/index.js",
41
+ "exports": {
42
+ ".": {
43
+ "require": "./dist/index.js",
44
+ "types": "./dist/index.d.ts"
45
+ }
46
+ },
53
47
  "name": "slonik-interceptor-query-cache",
54
48
  "peerDependencies": {
55
- "slonik": ">=27.0.0"
49
+ "slonik": ">=43.0.6"
56
50
  },
57
51
  "repository": {
58
52
  "type": "git",
59
- "url": "https://github.com/gajus/slonik-interceptor-query-cache"
53
+ "url": "https://github.com/gajus/slonik"
60
54
  },
61
55
  "scripts": {
62
- "build": "rm -fr ./dist && tsc --project tsconfig.build.json",
63
- "lint:eslint": "eslint .",
64
- "lint:tsc": "tsc",
65
- "prepare": "husky install",
66
- "test:ava": "nyc ava --verbose --serial"
56
+ "build": "rm -fr ./dist && tsc",
57
+ "lint": "eslint ./src && tsc --noEmit",
58
+ "test": "ava --verbose --serial"
67
59
  },
68
- "types": "./dist/index.d.ts",
69
- "version": "3.3.1"
60
+ "typings": "./dist/index.d.ts",
61
+ "version": "43.0.6"
70
62
  }
package/src/.eslintrc ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "extends": "canonical/auto",
3
+ "ignorePatterns": [
4
+ "dist",
5
+ "package-lock.json"
6
+ ],
7
+ "overrides": [
8
+ {
9
+ "files": "*.ts",
10
+ "rules": {
11
+ "import/no-cycle": 0,
12
+ "no-restricted-imports": ["error", "pg"]
13
+ }
14
+ },
15
+ {
16
+ "files": ["*.test.ts", "**/*.test/*"],
17
+ "extends": "canonical/ava",
18
+ "rules": {
19
+ "@typescript-eslint/no-explicit-any": 0,
20
+ "@typescript-eslint/no-unused-expressions": 0,
21
+ "ava/no-ignored-test-files": 0
22
+ }
23
+ }
24
+ ],
25
+ "root": true
26
+ }
package/src/Logger.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { Roarr } from 'roarr';
2
+
3
+ export const Logger = Roarr.child({
4
+ package: 'slonik-interceptor-query-cache',
5
+ });
@@ -0,0 +1,130 @@
1
+ import { Logger } from '../Logger';
2
+ import { extractCacheAttributes } from '../utilities/extractCacheAttributes';
3
+ import { normalizeCacheAttributes } from '../utilities/normalizeCacheAttributes';
4
+ import {
5
+ type Interceptor,
6
+ type Query,
7
+ type QueryResult,
8
+ type QueryResultRow,
9
+ } from 'slonik';
10
+
11
+ const log = Logger.child({
12
+ namespace: 'createQueryCacheInterceptor',
13
+ });
14
+
15
+ type Sandbox = {
16
+ cache: {
17
+ cacheAttributes: CacheAttributes;
18
+ };
19
+ };
20
+
21
+ export type CacheAttributes = {
22
+ discardEmpty: boolean;
23
+ key: string;
24
+ ttl: number;
25
+ };
26
+
27
+ type Storage = {
28
+ get: (
29
+ query: Query,
30
+ cacheAttributes: CacheAttributes,
31
+ ) => Promise<QueryResult<QueryResultRow> | null>;
32
+ set: (
33
+ query: Query,
34
+ cacheAttributes: CacheAttributes,
35
+ queryResult: QueryResult<QueryResultRow>,
36
+ ) => Promise<void>;
37
+ };
38
+
39
+ type ConfigurationInput = {
40
+ storage: Storage;
41
+ };
42
+
43
+ type Configuration = {
44
+ storage: Storage;
45
+ };
46
+
47
+ export const createQueryCacheInterceptor = (
48
+ configurationInput: ConfigurationInput,
49
+ ): Interceptor => {
50
+ const configuration: Configuration = {
51
+ ...configurationInput,
52
+ };
53
+
54
+ return {
55
+ beforeQueryExecution: async (context, query) => {
56
+ if (context.transactionId) {
57
+ return null;
58
+ }
59
+
60
+ const cacheAttributes = (context.sandbox as Sandbox).cache
61
+ ?.cacheAttributes;
62
+
63
+ if (!cacheAttributes) {
64
+ return null;
65
+ }
66
+
67
+ const maybeResult = await configuration.storage.get(
68
+ query,
69
+ cacheAttributes,
70
+ );
71
+
72
+ if (maybeResult) {
73
+ log.debug(
74
+ {
75
+ queryId: context.queryId,
76
+ },
77
+ 'query is served from cache',
78
+ );
79
+
80
+ return maybeResult;
81
+ }
82
+
83
+ return null;
84
+ },
85
+ beforeQueryResult: async (context, query, result) => {
86
+ if (context.transactionId) {
87
+ return null;
88
+ }
89
+
90
+ const cacheAttributes = (context.sandbox as Sandbox).cache
91
+ ?.cacheAttributes;
92
+
93
+ if (cacheAttributes) {
94
+ if (cacheAttributes.discardEmpty && result.rowCount === 0) {
95
+ log.debug(
96
+ '@cache-discard-empty is set and the query result is empty; not caching',
97
+ );
98
+ } else {
99
+ await configuration.storage.set(query, cacheAttributes, result);
100
+ }
101
+ }
102
+
103
+ return null;
104
+ },
105
+ beforeTransformQuery: async (context, query) => {
106
+ if (context.transactionId) {
107
+ return null;
108
+ }
109
+
110
+ const extractedCacheAttributes = extractCacheAttributes(
111
+ query.sql,
112
+ query.values,
113
+ );
114
+
115
+ if (!extractedCacheAttributes) {
116
+ return null;
117
+ }
118
+
119
+ const cacheAttributes = normalizeCacheAttributes(
120
+ extractedCacheAttributes,
121
+ );
122
+
123
+ context.sandbox.cache = {
124
+ cacheAttributes,
125
+ };
126
+
127
+ return null;
128
+ },
129
+ };
130
+ };
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { createQueryCacheInterceptor } from './factories/createQueryCacheInterceptor';
@@ -0,0 +1,92 @@
1
+ import { extractCacheAttributes } from './extractCacheAttributes';
2
+ import test from 'ava';
3
+
4
+ test('returns null when query does not contain cache attributes', (t) => {
5
+ t.is(extractCacheAttributes('', []), null);
6
+ });
7
+
8
+ test('extracts @cache-ttl', (t) => {
9
+ t.deepEqual(extractCacheAttributes('-- @cache-ttl 60', []), {
10
+ bodyHash: '46b9dd2b0ba88d13233b3feb',
11
+ discardEmpty: false,
12
+ key: 'query:$bodyHash:$valueHash',
13
+ ttl: 60,
14
+ valueHash: 'ec784925b52067bce01fd820',
15
+ });
16
+ });
17
+
18
+ test('extracts @cache-discard-empty', (t) => {
19
+ t.deepEqual(
20
+ extractCacheAttributes(
21
+ '-- @cache-ttl 60\n-- @cache-discard-empty false',
22
+ [],
23
+ ),
24
+ {
25
+ bodyHash: '46b9dd2b0ba88d13233b3feb',
26
+ discardEmpty: false,
27
+ key: 'query:$bodyHash:$valueHash',
28
+ ttl: 60,
29
+ valueHash: 'ec784925b52067bce01fd820',
30
+ },
31
+ );
32
+
33
+ t.deepEqual(
34
+ extractCacheAttributes(
35
+ '-- @cache-ttl 60\n-- @cache-discard-empty true',
36
+ [],
37
+ ),
38
+ {
39
+ bodyHash: '46b9dd2b0ba88d13233b3feb',
40
+ discardEmpty: true,
41
+ key: 'query:$bodyHash:$valueHash',
42
+ ttl: 60,
43
+ valueHash: 'ec784925b52067bce01fd820',
44
+ },
45
+ );
46
+ });
47
+
48
+ test('computes the parameter value hash', (t) => {
49
+ t.deepEqual(extractCacheAttributes('-- @cache-ttl 60', [1]), {
50
+ bodyHash: '46b9dd2b0ba88d13233b3feb',
51
+ discardEmpty: false,
52
+ key: 'query:$bodyHash:$valueHash',
53
+ ttl: 60,
54
+ valueHash: '62eb54746ae2932850f8d6ff',
55
+ });
56
+ });
57
+
58
+ test('computes the body hash; white spaces do not affect the body hash', (t) => {
59
+ t.deepEqual(extractCacheAttributes('-- @cache-ttl 60\nSELECT 1', []), {
60
+ bodyHash: '553ebdb024592064ca4c2c3a',
61
+ discardEmpty: false,
62
+ key: 'query:$bodyHash:$valueHash',
63
+ ttl: 60,
64
+ valueHash: 'ec784925b52067bce01fd820',
65
+ });
66
+
67
+ t.deepEqual(extractCacheAttributes('-- @cache-ttl 60\n\nSELECT 1', []), {
68
+ bodyHash: '553ebdb024592064ca4c2c3a',
69
+ discardEmpty: false,
70
+ key: 'query:$bodyHash:$valueHash',
71
+ ttl: 60,
72
+ valueHash: 'ec784925b52067bce01fd820',
73
+ });
74
+ });
75
+
76
+ test('computes the body hash; comments do not affect the body hash', (t) => {
77
+ t.deepEqual(extractCacheAttributes('-- @cache-ttl 60\nSELECT 1', []), {
78
+ bodyHash: '553ebdb024592064ca4c2c3a',
79
+ discardEmpty: false,
80
+ key: 'query:$bodyHash:$valueHash',
81
+ ttl: 60,
82
+ valueHash: 'ec784925b52067bce01fd820',
83
+ });
84
+
85
+ t.deepEqual(extractCacheAttributes('-- @cache-ttl 120\nSELECT 1', []), {
86
+ bodyHash: '553ebdb024592064ca4c2c3a',
87
+ discardEmpty: false,
88
+ key: 'query:$bodyHash:$valueHash',
89
+ ttl: 120,
90
+ valueHash: 'ec784925b52067bce01fd820',
91
+ });
92
+ });
@@ -0,0 +1,56 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { type PrimitiveValueExpression } from 'slonik';
3
+ import stripComments from 'strip-comments';
4
+
5
+ const hash = (subject: string) => {
6
+ return createHash('shake256', {
7
+ outputLength: 12,
8
+ })
9
+ .update(subject)
10
+ .digest('hex');
11
+ };
12
+
13
+ export type ExtractedCacheAttributes = {
14
+ bodyHash: string;
15
+ discardEmpty: boolean;
16
+ key: string;
17
+ ttl: number;
18
+ valueHash: string;
19
+ };
20
+
21
+ // TODO throw an error if an unknown attribute is used
22
+ export const extractCacheAttributes = (
23
+ subject: string,
24
+ values: readonly PrimitiveValueExpression[],
25
+ ): ExtractedCacheAttributes | null => {
26
+ const ttl = /-- @cache-ttl (\d+)/u.exec(subject)?.[1];
27
+
28
+ // https://github.com/jonschlinkert/strip-comments/issues/71
29
+ const bodyHash = hash(
30
+ stripComments(subject)
31
+ .replaceAll(/^\s*--.*$/gmu, '')
32
+ .replaceAll(/\s/gu, ''),
33
+ );
34
+
35
+ const discardEmpty =
36
+ (/-- @cache-discard-empty (true|false)/u.exec(subject)?.[1] ?? 'false') ===
37
+ 'true';
38
+
39
+ const valueHash = hash(JSON.stringify(values));
40
+
41
+ if (ttl) {
42
+ const key =
43
+ /-- @cache-key ([$\w\-:/]+)/iu.exec(subject)?.[1] ??
44
+ 'query:$bodyHash:$valueHash';
45
+
46
+ return {
47
+ bodyHash,
48
+ discardEmpty,
49
+ key,
50
+ ttl: Number(ttl),
51
+ valueHash,
52
+ };
53
+ }
54
+
55
+ return null;
56
+ };
@@ -0,0 +1,19 @@
1
+ import { normalizeCacheAttributes } from './normalizeCacheAttributes';
2
+ import test from 'ava';
3
+
4
+ test('replaces $bodyHash and $valueHash', (t) => {
5
+ t.deepEqual(
6
+ normalizeCacheAttributes({
7
+ bodyHash: 'foo',
8
+ discardEmpty: false,
9
+ key: '$bodyHash:$valueHash',
10
+ ttl: 60,
11
+ valueHash: 'bar',
12
+ }),
13
+ {
14
+ discardEmpty: false,
15
+ key: 'foo:bar',
16
+ ttl: 60,
17
+ },
18
+ );
19
+ });
@@ -0,0 +1,14 @@
1
+ import { type CacheAttributes } from '../factories/createQueryCacheInterceptor';
2
+ import { type ExtractedCacheAttributes } from './extractCacheAttributes';
3
+
4
+ export const normalizeCacheAttributes = (
5
+ extractedCacheAttributes: ExtractedCacheAttributes,
6
+ ): CacheAttributes => {
7
+ return {
8
+ discardEmpty: extractedCacheAttributes.discardEmpty,
9
+ key: extractedCacheAttributes.key
10
+ .replaceAll('$bodyHash', extractedCacheAttributes.bodyHash)
11
+ .replaceAll('$valueHash', extractedCacheAttributes.valueHash),
12
+ ttl: extractedCacheAttributes.ttl,
13
+ };
14
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowSyntheticDefaultImports": true,
4
+ "declaration": true,
5
+ "declarationMap": true,
6
+ "esModuleInterop": true,
7
+ "lib": [
8
+ "es2021"
9
+ ],
10
+ "module": "commonjs",
11
+ "moduleResolution": "node",
12
+ "noImplicitAny": false,
13
+ "noImplicitReturns": true,
14
+ "outDir": "dist",
15
+ "skipLibCheck": true,
16
+ "sourceMap": true,
17
+ "strict": true,
18
+ "target": "es2020",
19
+ "useUnknownInCatchVariables": false
20
+ },
21
+ "include": [
22
+ "src"
23
+ ]
24
+ }
package/LICENSE DELETED
@@ -1,24 +0,0 @@
1
- Copyright (c) 2023, Gajus Kuizinas (https://gajus.com/)
2
- All rights reserved.
3
-
4
- Redistribution and use in source and binary forms, with or without
5
- modification, are permitted provided that the following conditions are met:
6
- * Redistributions of source code must retain the above copyright
7
- notice, this list of conditions and the following disclaimer.
8
- * Redistributions in binary form must reproduce the above copyright
9
- notice, this list of conditions and the following disclaimer in the
10
- documentation and/or other materials provided with the distribution.
11
- * Neither the name of the Gajus Kuizinas (https://gajus.com/) nor the
12
- names of its contributors may be used to endorse or promote products
13
- derived from this software without specific prior written permission.
14
-
15
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
- DISCLAIMED. IN NO EVENT SHALL GAJUS KUIZINAS BE LIABLE FOR ANY
19
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -1,4 +0,0 @@
1
- import { type QueryContext } from 'slonik';
2
- declare const _default: () => QueryContext;
3
- export default _default;
4
- //# sourceMappingURL=createQueryContext.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createQueryContext.d.ts","sourceRoot":"","sources":["../../src/factories/createQueryContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,QAAQ,CAAC;;AAE3C,wBAaE"}
@@ -1,17 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = () => {
4
- return {
5
- connectionId: '1',
6
- log: {
7
- getContext: () => {
8
- return {
9
- connectionId: '1',
10
- poolId: '1',
11
- };
12
- },
13
- },
14
- poolId: '1',
15
- };
16
- };
17
- //# sourceMappingURL=createQueryContext.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createQueryContext.js","sourceRoot":"","sources":["../../src/factories/createQueryContext.ts"],"names":[],"mappings":";;AAEA,kBAAe,GAAG,EAAE;IAClB,OAAO;QACL,YAAY,EAAE,GAAG;QACjB,GAAG,EAAE;YACH,UAAU,EAAE,GAAG,EAAE;gBACf,OAAO;oBACL,YAAY,EAAE,GAAG;oBACjB,MAAM,EAAE,GAAG;iBACZ,CAAC;YACJ,CAAC;SACF;QACD,MAAM,EAAE,GAAG;KACe,CAAC;AAC/B,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- export { createQueryCacheInterceptor } from './createQueryCacheInterceptor';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/factories/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC"}
@@ -1,6 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createQueryCacheInterceptor = void 0;
4
- var createQueryCacheInterceptor_1 = require("./createQueryCacheInterceptor");
5
- Object.defineProperty(exports, "createQueryCacheInterceptor", { enumerable: true, get: function () { return createQueryCacheInterceptor_1.createQueryCacheInterceptor; } });
6
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/factories/index.ts"],"names":[],"mappings":";;;AAAA,6EAA4E;AAAnE,0IAAA,2BAA2B,OAAA"}
@@ -1,3 +0,0 @@
1
- export { extractCacheAttributes } from './extractCacheAttributes';
2
- export { normalizeCacheAttributes } from './normalizeCacheAttributes';
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utilities/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC"}
@@ -1,8 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.normalizeCacheAttributes = exports.extractCacheAttributes = void 0;
4
- var extractCacheAttributes_1 = require("./extractCacheAttributes");
5
- Object.defineProperty(exports, "extractCacheAttributes", { enumerable: true, get: function () { return extractCacheAttributes_1.extractCacheAttributes; } });
6
- var normalizeCacheAttributes_1 = require("./normalizeCacheAttributes");
7
- Object.defineProperty(exports, "normalizeCacheAttributes", { enumerable: true, get: function () { return normalizeCacheAttributes_1.normalizeCacheAttributes; } });
8
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utilities/index.ts"],"names":[],"mappings":";;;AAAA,mEAAkE;AAAzD,gIAAA,sBAAsB,OAAA;AAC/B,uEAAsE;AAA7D,oIAAA,wBAAwB,OAAA"}