@smartive/datocms-utils 3.0.0-next.4 → 3.0.0-next.5
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/README.md +134 -56
- package/dist/cache/provider/neon.d.ts +27 -4
- package/dist/cache/provider/neon.js +8 -2
- package/dist/cache/provider/neon.js.map +1 -1
- package/dist/cache/provider/redis.d.ts +20 -4
- package/dist/cache/provider/redis.js +8 -2
- package/dist/cache/provider/redis.js.map +1 -1
- package/dist/cache/types.d.ts +3 -0
- package/dist/classnames.d.ts +1 -1
- package/dist/classnames.js +3 -1
- package/dist/classnames.js.map +1 -1
- package/package.json +1 -1
- package/src/cache/provider/neon.ts +28 -8
- package/src/cache/provider/redis.ts +21 -2
- package/src/cache/types.ts +3 -0
- package/src/classnames.ts +7 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# smartive DatoCMS Utilities
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A collection of utilities and helpers for working with DatoCMS in Next.js projects.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,23 +8,60 @@ A set of utilities and helpers to work with DatoCMS in a Next.js project.
|
|
|
8
8
|
npm install @smartive/datocms-utils
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Utilities
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
### General Utilities
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
#### `classNames`
|
|
16
|
+
|
|
17
|
+
Cleans and joins an array of class names, filtering out undefined and boolean values.
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { classNames } from '@smartive/datocms-utils';
|
|
21
|
+
|
|
22
|
+
const className = classNames('btn', isActive && 'btn-active', undefined, 'btn-primary');
|
|
23
|
+
// Result: "btn btn-active btn-primary"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
#### `getTelLink`
|
|
27
|
+
|
|
28
|
+
Converts a phone number into a `tel:` link by removing non-digit characters (except `+` for international numbers).
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { getTelLink } from '@smartive/datocms-utils';
|
|
32
|
+
|
|
33
|
+
const link = getTelLink('+1 (555) 123-4567');
|
|
34
|
+
// Result: "tel:+15551234567"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### DatoCMS Cache Tags
|
|
38
|
+
|
|
39
|
+
Utilities for managing [DatoCMS cache tags](https://www.datocms.com/docs/content-delivery-api/cache-tags) with different storage backends. Cache tags enable efficient cache invalidation by tracking which queries reference which content.
|
|
40
|
+
|
|
41
|
+
#### Core Utilities
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { generateQueryId, parseXCacheTagsResponseHeader } from '@smartive/datocms-utils/cache';
|
|
16
45
|
|
|
17
|
-
|
|
46
|
+
// Generate a unique ID for a GraphQL query
|
|
47
|
+
const queryId = generateQueryId(document, variables);
|
|
18
48
|
|
|
19
|
-
|
|
49
|
+
// Parse DatoCMS's X-Cache-Tags header
|
|
50
|
+
const tags = parseXCacheTagsResponseHeader('tag-a tag-2 other-tag');
|
|
51
|
+
// Result: ['tag-a', 'tag-2', 'other-tag']
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### Storage Providers
|
|
55
|
+
|
|
56
|
+
The package provides two storage backends for cache tags: **Neon (Postgres)** and **Redis**. Both implement the same `CacheTagsStore` interface.
|
|
20
57
|
|
|
21
|
-
|
|
22
|
-
- `queriesReferencingCacheTags`: Retrieves the queries that reference cache tags.
|
|
23
|
-
- `deleteQueries`: Deletes the cache tags of a query from the database.
|
|
58
|
+
##### Neon (Postgres) Provider
|
|
24
59
|
|
|
25
|
-
|
|
60
|
+
Use Neon serverless Postgres to store cache tag mappings.
|
|
26
61
|
|
|
27
|
-
|
|
62
|
+
**Setup:**
|
|
63
|
+
|
|
64
|
+
1. Create the cache tags table:
|
|
28
65
|
|
|
29
66
|
```sql
|
|
30
67
|
CREATE TABLE IF NOT EXISTS query_cache_tags (
|
|
@@ -34,75 +71,116 @@ CREATE TABLE IF NOT EXISTS query_cache_tags (
|
|
|
34
71
|
);
|
|
35
72
|
```
|
|
36
73
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
The following utilities provide Redis-based alternatives to the Postgres cache tags implementation above. They work with [DatoCMS cache tags](https://www.datocms.com/docs/content-delivery-api/cache-tags) and any Redis instance.
|
|
74
|
+
2. Install [@neondatabase/serverless](https://github.com/neondatabase/serverless)
|
|
40
75
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
- `redis.truncateCacheTags`: Wipes out all cache tags from Redis.
|
|
76
|
+
```bash
|
|
77
|
+
npm install @neondatabase/serverless
|
|
78
|
+
```
|
|
45
79
|
|
|
46
|
-
|
|
80
|
+
3. Create and use the store:
|
|
47
81
|
|
|
48
|
-
|
|
82
|
+
```typescript
|
|
83
|
+
import { createCacheTagsStore } from '@smartive/datocms-utils/cache/neon';
|
|
49
84
|
|
|
50
|
-
|
|
85
|
+
const store = createCacheTagsStore({
|
|
86
|
+
connectionString: process.env.DATABASE_URL!,
|
|
87
|
+
table: 'query_cache_tags',
|
|
88
|
+
});
|
|
51
89
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
# For Upstash Redis
|
|
55
|
-
REDIS_URL=rediss://default:your-token@your-endpoint.upstash.io:6379
|
|
90
|
+
// Store cache tags for a query
|
|
91
|
+
await store.storeQueryCacheTags(queryId, ['item:42', 'product']);
|
|
56
92
|
|
|
57
|
-
|
|
58
|
-
|
|
93
|
+
// Find queries that reference specific tags
|
|
94
|
+
const queries = await store.queriesReferencingCacheTags(['item:42']);
|
|
59
95
|
|
|
60
|
-
|
|
61
|
-
|
|
96
|
+
// Delete specific cache tags
|
|
97
|
+
await store.deleteCacheTags(['item:42']);
|
|
62
98
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
REDIS_KEY_PREFIX=prod # For production
|
|
66
|
-
REDIS_KEY_PREFIX=preview # For preview/staging
|
|
67
|
-
# Leave empty for development (no prefix)
|
|
99
|
+
// Clear all cache tags
|
|
100
|
+
await store.truncateCacheTags();
|
|
68
101
|
```
|
|
69
102
|
|
|
70
|
-
|
|
103
|
+
##### Redis Provider
|
|
104
|
+
|
|
105
|
+
Use Redis to store cache tag mappings with better performance for high-traffic applications.
|
|
71
106
|
|
|
72
|
-
|
|
107
|
+
**Setup:**
|
|
108
|
+
|
|
109
|
+
1. Install [ioredis](https://github.com/redis/ioredis)
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm install ioredis
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
2. Create and use the store:
|
|
73
116
|
|
|
74
117
|
```typescript
|
|
75
|
-
|
|
76
|
-
|
|
118
|
+
import { createCacheTagsStore } from '@smartive/datocms-utils/cache/redis';
|
|
119
|
+
|
|
120
|
+
const store = createCacheTagsStore({
|
|
121
|
+
url: process.env.REDIS_URL!,
|
|
122
|
+
keyPrefix: 'prod:', // Optional: namespace for multi-environment setups
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Same API as Neon provider
|
|
126
|
+
await store.storeQueryCacheTags(queryId, ['item:42', 'product']);
|
|
127
|
+
const queries = await store.queriesReferencingCacheTags(['item:42']);
|
|
128
|
+
await store.deleteCacheTags(['item:42']);
|
|
129
|
+
await store.truncateCacheTags();
|
|
130
|
+
```
|
|
77
131
|
|
|
78
|
-
|
|
132
|
+
**Redis connection string examples:**
|
|
79
133
|
|
|
80
|
-
|
|
81
|
-
|
|
134
|
+
```bash
|
|
135
|
+
# Upstash Redis
|
|
136
|
+
REDIS_URL=rediss://default:token@endpoint.upstash.io:6379
|
|
82
137
|
|
|
83
|
-
|
|
84
|
-
|
|
138
|
+
# Redis Cloud
|
|
139
|
+
REDIS_URL=redis://username:password@redis-host:6379
|
|
85
140
|
|
|
86
|
-
|
|
87
|
-
|
|
141
|
+
# Local development
|
|
142
|
+
REDIS_URL=redis://localhost:6379
|
|
88
143
|
```
|
|
89
144
|
|
|
90
|
-
####
|
|
145
|
+
#### `CacheTagsStore` Interface
|
|
146
|
+
|
|
147
|
+
Both providers implement:
|
|
91
148
|
|
|
92
|
-
|
|
149
|
+
- `storeQueryCacheTags(queryId: string, cacheTags: CacheTag[])`: Store cache tags for a query
|
|
150
|
+
- `queriesReferencingCacheTags(cacheTags: CacheTag[])`: Get query IDs that reference any of the specified tags
|
|
151
|
+
- `deleteCacheTags(cacheTags: CacheTag[])`: Delete specific cache tags
|
|
152
|
+
- `truncateCacheTags()`: Wipe all cache tags (use with caution)
|
|
93
153
|
|
|
94
|
-
|
|
154
|
+
### Complete Example
|
|
95
155
|
|
|
96
|
-
|
|
156
|
+
```typescript
|
|
157
|
+
import { generateQueryId, parseXCacheTagsResponseHeader } from '@smartive/datocms-utils/cache';
|
|
158
|
+
import { createCacheTagsStore } from '@smartive/datocms-utils/cache/redis';
|
|
159
|
+
|
|
160
|
+
const store = createCacheTagsStore({
|
|
161
|
+
url: process.env.REDIS_URL!,
|
|
162
|
+
keyPrefix: 'myapp:',
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// After making a DatoCMS query
|
|
166
|
+
const queryId = generateQueryId(query, variables);
|
|
167
|
+
const cacheTags = parseXCacheTagsResponseHeader(response.headers['x-cache-tags']);
|
|
168
|
+
await store.storeQueryCacheTags(queryId, cacheTags);
|
|
169
|
+
|
|
170
|
+
// When handling DatoCMS webhook for cache invalidation
|
|
171
|
+
const affectedQueries = await store.queriesReferencingCacheTags(webhook.entity.attributes.tags);
|
|
172
|
+
// Revalidate affected queries...
|
|
173
|
+
await store.deleteCacheTags(webhook.entity.attributes.tags);
|
|
174
|
+
```
|
|
97
175
|
|
|
98
|
-
|
|
176
|
+
## TypeScript Types
|
|
99
177
|
|
|
100
|
-
|
|
178
|
+
The package includes TypeScript types for DatoCMS webhooks and cache tags:
|
|
101
179
|
|
|
102
|
-
- `
|
|
103
|
-
- `
|
|
180
|
+
- `CacheTag`: A branded type for cache tags, ensuring type safety
|
|
181
|
+
- `CacheTagsInvalidateWebhook`: Type definition for DatoCMS cache tag invalidation webhook payloads
|
|
182
|
+
- `CacheTagsStore`: Interface for cache tag storage implementations
|
|
104
183
|
|
|
105
|
-
|
|
184
|
+
## License
|
|
106
185
|
|
|
107
|
-
|
|
108
|
-
- `CacheTagsInvalidateWebhook`: The payload of the DatoCMS cache tags invalidate webhook.
|
|
186
|
+
MIT © [smartive AG](https://github.com/smartive)
|
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
import { type CacheTagsStore } from '../types.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
type NeonCacheTagsStoreConfig = {
|
|
3
|
+
/**
|
|
4
|
+
* Neon connection string. You can find it in the "Connection" tab of your Neon project dashboard.
|
|
5
|
+
* Has the format `postgresql://user:pass@host/db`
|
|
6
|
+
*/
|
|
7
|
+
readonly connectionUrl: string;
|
|
8
|
+
/**
|
|
9
|
+
* Name of the table where cache tags will be stored. The table must have the following schema:
|
|
10
|
+
*
|
|
11
|
+
* ```sql
|
|
12
|
+
* CREATE TABLE your_table_name (
|
|
13
|
+
* query_id TEXT NOT NULL,
|
|
14
|
+
* cache_tag TEXT NOT NULL,
|
|
15
|
+
* PRIMARY KEY (query_id, cache_tag)
|
|
16
|
+
* );
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
readonly table: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Creates a `CacheTagsStore` implementation using Neon as the storage backend. Neon is a serverless Postgres database service.
|
|
23
|
+
*
|
|
24
|
+
* @param {NeonCacheTagsStoreConfig} config Configuration object containing the Neon connection string and table name.
|
|
25
|
+
* @returns An object implementing the `CacheTagsStore` interface, allowing you to store and manage cache tags in a Neon database.
|
|
26
|
+
*/
|
|
27
|
+
export declare const createCacheTagsStore: ({ connectionUrl, table }: NeonCacheTagsStoreConfig) => CacheTagsStore;
|
|
28
|
+
export {};
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { neon } from '@neondatabase/serverless';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Creates a `CacheTagsStore` implementation using Neon as the storage backend. Neon is a serverless Postgres database service.
|
|
4
|
+
*
|
|
5
|
+
* @param {NeonCacheTagsStoreConfig} config Configuration object containing the Neon connection string and table name.
|
|
6
|
+
* @returns An object implementing the `CacheTagsStore` interface, allowing you to store and manage cache tags in a Neon database.
|
|
7
|
+
*/
|
|
8
|
+
export const createCacheTagsStore = ({ connectionUrl, table }) => {
|
|
9
|
+
const sql = neon(connectionUrl, { fullResults: true });
|
|
4
10
|
const storeQueryCacheTags = async (queryId, cacheTags) => {
|
|
5
11
|
if (!cacheTags?.length) {
|
|
6
12
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neon.js","sourceRoot":"","sources":["../../../src/cache/provider/neon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"neon.js","sourceRoot":"","sources":["../../../src/cache/provider/neon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAuBhD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,EAAE,aAAa,EAAE,KAAK,EAA4B,EAAkB,EAAE;IACzG,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;IAEvD,MAAM,mBAAmB,GAAG,KAAK,EAAE,OAAe,EAAE,SAAqB,EAAE,EAAE;QAC3E,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClE,MAAM,YAAY,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEzF,MAAM,GAAG,CAAC,KAAK,CAAC,eAAe,KAAK,WAAW,YAAY,yBAAyB,EAAE,IAAI,CAAC,CAAC;IAC9F,CAAC,CAAC;IAEF,MAAM,2BAA2B,GAAG,KAAK,EAAE,SAAqB,EAAqB,EAAE;QACrF,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;YACvB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,YAAY,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEpE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,KAAK,CAC9B,iCAAiC,KAAK,wBAAwB,YAAY,GAAG,EAC7E,SAAS,CACV,CAAC;QAEF,OAAO,IAAI,CAAC,MAAM,CAAW,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE;YAC7C,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACrC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC;YAED,OAAO,QAAQ,CAAC;QAClB,CAAC,EAAE,EAAE,CAAC,CAAC;IACT,CAAC,CAAC;IAEF,MAAM,eAAe,GAAG,KAAK,EAAE,SAAqB,EAAE,EAAE;QACtD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,CAAC;QACX,CAAC;QACD,MAAM,YAAY,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEpE,OAAO,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,eAAe,KAAK,wBAAwB,YAAY,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC;IACjH,CAAC,CAAC;IAEF,MAAM,iBAAiB,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,eAAe,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC;IAE9F,OAAO;QACL,mBAAmB;QACnB,2BAA2B;QAC3B,eAAe;QACf,iBAAiB;KAClB,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { type CacheTagsStore } from '../types.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
type RedisCacheTagsStoreConfig = {
|
|
3
|
+
/**
|
|
4
|
+
* Redis connection string. For example, `redis://user:pass@host:port/db`.
|
|
5
|
+
*/
|
|
6
|
+
readonly connectionUrl: string;
|
|
7
|
+
/**
|
|
8
|
+
* Optional prefix for Redis keys. If provided, all keys used to store cache tags will be prefixed with this value.
|
|
9
|
+
* This can be useful to avoid key collisions if the same Redis instance is used for multiple purposes.
|
|
10
|
+
* For example, if you set `keyPrefix` to `'myapp:'`, a cache tag like `'tag1'` will be stored under the key `'myapp:tag1'`.
|
|
11
|
+
*/
|
|
12
|
+
readonly keyPrefix?: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Creates a `CacheTagsStore` implementation using Redis as the storage backend.
|
|
16
|
+
*
|
|
17
|
+
* @param {RedisCacheTagsStoreConfig} config Configuration object containing the Redis connection string and optional key prefix.
|
|
18
|
+
* @returns An object implementing the `CacheTagsStore` interface, allowing you to store and manage cache tags in a Redis database.
|
|
19
|
+
*/
|
|
20
|
+
export declare const createCacheTagsStore: ({ connectionUrl, keyPrefix }: RedisCacheTagsStoreConfig) => CacheTagsStore;
|
|
21
|
+
export {};
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { Redis } from 'ioredis';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Creates a `CacheTagsStore` implementation using Redis as the storage backend.
|
|
4
|
+
*
|
|
5
|
+
* @param {RedisCacheTagsStoreConfig} config Configuration object containing the Redis connection string and optional key prefix.
|
|
6
|
+
* @returns An object implementing the `CacheTagsStore` interface, allowing you to store and manage cache tags in a Redis database.
|
|
7
|
+
*/
|
|
8
|
+
export const createCacheTagsStore = ({ connectionUrl, keyPrefix = '' }) => {
|
|
9
|
+
const redis = new Redis(connectionUrl, {
|
|
4
10
|
maxRetriesPerRequest: 3,
|
|
5
11
|
lazyConnect: true,
|
|
6
12
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../../src/cache/provider/redis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../../src/cache/provider/redis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAgBhC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,EAAE,aAAa,EAAE,SAAS,GAAG,EAAE,EAA6B,EAAkB,EAAE;IACnH,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,aAAa,EAAE;QACrC,oBAAoB,EAAE,CAAC;QACvB,WAAW,EAAE,IAAI;KAClB,CAAC,CAAC;IAEH,MAAM,mBAAmB,GAAG,KAAK,EAAE,OAAe,EAAE,SAAqB,EAAE,EAAE;QAC3E,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAElC,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;YAC5B,QAAQ,CAAC,IAAI,CAAC,GAAG,SAAS,GAAG,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC,CAAC;IAEF,MAAM,2BAA2B,GAAG,KAAK,EAAE,SAAqB,EAAE,EAAE;QAClE,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;YACvB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,SAAS,GAAG,GAAG,EAAE,CAAC,CAAC;QAE1D,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IAC/B,CAAC,CAAC;IAEF,MAAM,eAAe,GAAG,KAAK,EAAE,SAAqB,EAAE,EAAE;QACtD,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;YACvB,OAAO,CAAC,CAAC;QACX,CAAC;QAED,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,SAAS,GAAG,GAAG,EAAE,CAAC,CAAC;QAE1D,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5B,CAAC,CAAC;IAEF,MAAM,iBAAiB,GAAG,KAAK,IAAI,EAAE;QACnC,MAAM,OAAO,GAAG,GAAG,SAAS,GAAG,CAAC;QAChC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEvC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,CAAC;QACX,CAAC;QAED,OAAO,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IAClC,CAAC,CAAC;IAEF,OAAO;QACL,mBAAmB;QACnB,2BAA2B;QAC3B,eAAe;QACf,iBAAiB;KAClB,CAAC;AACJ,CAAC,CAAC"}
|
package/dist/cache/types.d.ts
CHANGED
package/dist/classnames.d.ts
CHANGED
package/dist/classnames.js
CHANGED
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
* @param classNames Array of class names
|
|
5
5
|
* @returns Clean string to be used for class name
|
|
6
6
|
*/
|
|
7
|
-
export const classNames = (...classNames) => classNames
|
|
7
|
+
export const classNames = (...classNames) => classNames
|
|
8
|
+
.filter((value) => (typeof value === 'string' && value.length > 0) || (typeof value === 'number' && Number.isFinite(value)))
|
|
9
|
+
.join(' ');
|
|
8
10
|
//# sourceMappingURL=classnames.js.map
|
package/dist/classnames.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classnames.js","sourceRoot":"","sources":["../src/classnames.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAG,
|
|
1
|
+
{"version":3,"file":"classnames.js","sourceRoot":"","sources":["../src/classnames.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAG,UAAqB,EAAE,EAAE,CACrD,UAAU;KACP,MAAM,CACL,CAAC,KAAK,EAA4B,EAAE,CAClC,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAC3G;KACA,IAAI,CAAC,GAAG,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,34 @@
|
|
|
1
1
|
import { neon } from '@neondatabase/serverless';
|
|
2
2
|
import { type CacheTag, type CacheTagsStore } from '../types.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
type NeonCacheTagsStoreConfig = {
|
|
5
|
+
/**
|
|
6
|
+
* Neon connection string. You can find it in the "Connection" tab of your Neon project dashboard.
|
|
7
|
+
* Has the format `postgresql://user:pass@host/db`
|
|
8
|
+
*/
|
|
9
|
+
readonly connectionUrl: string;
|
|
10
|
+
/**
|
|
11
|
+
* Name of the table where cache tags will be stored. The table must have the following schema:
|
|
12
|
+
*
|
|
13
|
+
* ```sql
|
|
14
|
+
* CREATE TABLE your_table_name (
|
|
15
|
+
* query_id TEXT NOT NULL,
|
|
16
|
+
* cache_tag TEXT NOT NULL,
|
|
17
|
+
* PRIMARY KEY (query_id, cache_tag)
|
|
18
|
+
* );
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
readonly table: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Creates a `CacheTagsStore` implementation using Neon as the storage backend. Neon is a serverless Postgres database service.
|
|
26
|
+
*
|
|
27
|
+
* @param {NeonCacheTagsStoreConfig} config Configuration object containing the Neon connection string and table name.
|
|
28
|
+
* @returns An object implementing the `CacheTagsStore` interface, allowing you to store and manage cache tags in a Neon database.
|
|
29
|
+
*/
|
|
30
|
+
export const createCacheTagsStore = ({ connectionUrl, table }: NeonCacheTagsStoreConfig): CacheTagsStore => {
|
|
31
|
+
const sql = neon(connectionUrl, { fullResults: true });
|
|
12
32
|
|
|
13
33
|
const storeQueryCacheTags = async (queryId: string, cacheTags: CacheTag[]) => {
|
|
14
34
|
if (!cacheTags?.length) {
|
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
import { Redis } from 'ioredis';
|
|
2
2
|
import { type CacheTag, type CacheTagsStore } from '../types.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
type RedisCacheTagsStoreConfig = {
|
|
5
|
+
/**
|
|
6
|
+
* Redis connection string. For example, `redis://user:pass@host:port/db`.
|
|
7
|
+
*/
|
|
8
|
+
readonly connectionUrl: string;
|
|
9
|
+
/**
|
|
10
|
+
* Optional prefix for Redis keys. If provided, all keys used to store cache tags will be prefixed with this value.
|
|
11
|
+
* This can be useful to avoid key collisions if the same Redis instance is used for multiple purposes.
|
|
12
|
+
* For example, if you set `keyPrefix` to `'myapp:'`, a cache tag like `'tag1'` will be stored under the key `'myapp:tag1'`.
|
|
13
|
+
*/
|
|
14
|
+
readonly keyPrefix?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates a `CacheTagsStore` implementation using Redis as the storage backend.
|
|
19
|
+
*
|
|
20
|
+
* @param {RedisCacheTagsStoreConfig} config Configuration object containing the Redis connection string and optional key prefix.
|
|
21
|
+
* @returns An object implementing the `CacheTagsStore` interface, allowing you to store and manage cache tags in a Redis database.
|
|
22
|
+
*/
|
|
23
|
+
export const createCacheTagsStore = ({ connectionUrl, keyPrefix = '' }: RedisCacheTagsStoreConfig): CacheTagsStore => {
|
|
24
|
+
const redis = new Redis(connectionUrl, {
|
|
6
25
|
maxRetriesPerRequest: 3,
|
|
7
26
|
lazyConnect: true,
|
|
8
27
|
});
|
package/src/cache/types.ts
CHANGED
package/src/classnames.ts
CHANGED
|
@@ -4,4 +4,10 @@
|
|
|
4
4
|
* @param classNames Array of class names
|
|
5
5
|
* @returns Clean string to be used for class name
|
|
6
6
|
*/
|
|
7
|
-
export const classNames = (...classNames:
|
|
7
|
+
export const classNames = (...classNames: unknown[]) =>
|
|
8
|
+
classNames
|
|
9
|
+
.filter(
|
|
10
|
+
(value): value is string | number =>
|
|
11
|
+
(typeof value === 'string' && value.length > 0) || (typeof value === 'number' && Number.isFinite(value)),
|
|
12
|
+
)
|
|
13
|
+
.join(' ');
|