@trieb.work/nextjs-turbo-redis-cache 1.12.0 → 1.14.0

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 (82) hide show
  1. package/.github/workflows/ci.yml +11 -13
  2. package/.github/workflows/release.yml +68 -7
  3. package/CHANGELOG.md +28 -0
  4. package/README.md +29 -21
  5. package/dist/index.d.mts +8 -3
  6. package/dist/index.d.ts +8 -3
  7. package/dist/index.js +64 -7
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +54 -7
  10. package/dist/index.mjs.map +1 -1
  11. package/package.json +6 -3
  12. package/playwright.config.ts +1 -1
  13. package/src/CacheComponentsHandler.ts +11 -5
  14. package/src/CachedHandler.ts +16 -2
  15. package/src/RedisStringsHandler.ts +4 -2
  16. package/src/utils/prefix.test.ts +115 -0
  17. package/src/utils/prefix.ts +44 -0
  18. package/test/cache-components/cache-components.integration.spec.ts +1 -1
  19. package/test/integration/build-id-prefix.integration.test.ts +102 -0
  20. package/test/integration/next-app-15-0-3/pnpm-lock.yaml +32 -1
  21. package/test/integration/next-app-15-3-2/pnpm-lock.yaml +33 -1
  22. package/test/integration/next-app-15-4-7/pnpm-lock.yaml +36 -1
  23. package/test/integration/next-app-16-0-3/pnpm-lock.yaml +37 -1
  24. package/test/integration/next-app-16-0-3/postcss.config.mjs +7 -7
  25. package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/package.json +3 -3
  26. package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/pnpm-lock.yaml +98 -55
  27. package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/postcss.config.mjs +7 -7
  28. package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/package.json +3 -3
  29. package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/pnpm-lock.yaml +98 -55
  30. package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/postcss.config.mjs +7 -7
  31. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/README.md +0 -0
  32. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/eslint.config.mjs +0 -0
  33. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/next.config.ts +0 -0
  34. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/api/cached-static-fetch/route.ts +0 -0
  35. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/api/nested-fetch-in-api-route/revalidated-fetch/route.ts +0 -0
  36. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/api/revalidatePath/route.ts +0 -0
  37. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/api/revalidateTag/route.ts +0 -0
  38. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/api/revalidated-fetch/route.ts +0 -0
  39. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/api/uncached-fetch/route.ts +0 -0
  40. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/favicon.ico +0 -0
  41. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/globals.css +0 -0
  42. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/layout.tsx +0 -0
  43. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/page.tsx +0 -0
  44. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/pages/cached-static-fetch/default--force-dynamic-page/page.tsx +0 -0
  45. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/pages/cached-static-fetch/revalidate15--default-page/page.tsx +0 -0
  46. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/pages/cached-static-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  47. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/pages/no-fetch/default-page/page.tsx +0 -0
  48. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/pages/revalidated-fetch/default--force-dynamic-page/page.tsx +0 -0
  49. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/pages/revalidated-fetch/revalidate15--default-page/page.tsx +0 -0
  50. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/pages/revalidated-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  51. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/pages/uncached-fetch/default--force-dynamic-page/page.tsx +0 -0
  52. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/pages/uncached-fetch/revalidate15--default-page/page.tsx +0 -0
  53. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/pages/uncached-fetch/revalidate15--force-dynamic-page/page.tsx +0 -0
  54. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/revalidation-interface.tsx +0 -0
  55. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/src/app/update-tag-test/page.tsx +0 -0
  56. /package/test/integration/{next-app-16-1-1 → next-app-16-2-3}/tsconfig.json +0 -0
  57. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/README.md +0 -0
  58. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/cache-handler.js +0 -0
  59. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/eslint.config.mjs +0 -0
  60. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/next.config.ts +0 -0
  61. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/public/file.svg +0 -0
  62. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/public/globe.svg +0 -0
  63. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/public/next.svg +0 -0
  64. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/public/vercel.svg +0 -0
  65. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/public/window.svg +0 -0
  66. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/api/cached-static-fetch/route.ts +0 -0
  67. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/api/cached-with-tag/route.ts +0 -0
  68. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/api/revalidate-tag/route.ts +0 -0
  69. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/api/revalidated-fetch/route.ts +0 -0
  70. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/cache-lab/cachelife-short/page.tsx +0 -0
  71. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/cache-lab/page.tsx +0 -0
  72. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/cache-lab/runtime-data-suspense/page.tsx +0 -0
  73. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/cache-lab/stale-while-revalidate/page.tsx +0 -0
  74. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/cache-lab/tag-invalidation/page.tsx +0 -0
  75. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/cache-lab/use-cache-nondeterministic/page.tsx +0 -0
  76. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/favicon.ico +0 -0
  77. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/globals.css +0 -0
  78. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/layout.tsx +0 -0
  79. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/page.tsx +0 -0
  80. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/revalidation-interface.tsx +0 -0
  81. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/src/app/update-tag-test/page.tsx +0 -0
  82. /package/test/integration/{next-app-16-1-1-cache-components → next-app-16-2-3-cache-components}/tsconfig.json +0 -0
@@ -0,0 +1,115 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import os from 'node:os';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { resolveKeyPrefix } from './prefix';
6
+
7
+ function withEnv<T>(env: Partial<NodeJS.ProcessEnv>, fn: () => T): T {
8
+ const original: NodeJS.ProcessEnv = { ...process.env };
9
+ Object.entries(env).forEach(([k, v]) => {
10
+ const envRec = process.env as Record<string, string | undefined>;
11
+ if (v === undefined) delete envRec[k];
12
+ else envRec[k] = v as string;
13
+ });
14
+ try {
15
+ return fn();
16
+ } finally {
17
+ process.env = original;
18
+ }
19
+ }
20
+
21
+ function makeBuildIdTree(buildId: string) {
22
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'prefix-test-'));
23
+ const nextDir = path.join(tmp, '.next');
24
+ const serverDir = path.join(nextDir, 'server');
25
+ fs.mkdirSync(serverDir, { recursive: true });
26
+ fs.writeFileSync(path.join(nextDir, 'BUILD_ID'), buildId, 'utf8');
27
+ return {
28
+ serverDistDir: serverDir,
29
+ cleanup: () => fs.rmSync(tmp, { recursive: true, force: true }),
30
+ };
31
+ }
32
+
33
+ describe('resolveKeyPrefix', () => {
34
+ beforeEach(() => {
35
+ const envRec = process.env as Record<string, string | undefined>;
36
+ delete envRec.KEY_PREFIX;
37
+ delete envRec.VERCEL_URL;
38
+ });
39
+
40
+ it('returns option keyPrefix when provided (including empty string)', () => {
41
+ const p1 = resolveKeyPrefix({ optionKeyPrefix: 'opt_', env: process.env });
42
+ expect(p1).toBe('opt_');
43
+
44
+ const p2 = resolveKeyPrefix({ optionKeyPrefix: '', env: process.env });
45
+ expect(p2).toBe('');
46
+ });
47
+
48
+ it('uses KEY_PREFIX when option is undefined', () => {
49
+ const res = withEnv({ KEY_PREFIX: 'envkp_' }, () =>
50
+ resolveKeyPrefix({ optionKeyPrefix: undefined, env: process.env }),
51
+ );
52
+ expect(res).toBe('envkp_');
53
+ });
54
+
55
+ it('uses BUILD_ID when KEY_PREFIX and VERCEL_URL are absent and BUILD_ID readable', () => {
56
+ const { serverDistDir, cleanup } = makeBuildIdTree('BID123');
57
+ try {
58
+ const res = resolveKeyPrefix({
59
+ optionKeyPrefix: undefined,
60
+ env: process.env,
61
+ serverDistDir,
62
+ });
63
+ expect(res).toBe('BID123');
64
+ } finally {
65
+ cleanup();
66
+ }
67
+ });
68
+
69
+ it('uses VERCEL_URL before BUILD_ID when both are available', () => {
70
+ const { serverDistDir, cleanup } = makeBuildIdTree('BIDXYZ');
71
+ try {
72
+ const res = withEnv({ VERCEL_URL: 'vercel.example' }, () =>
73
+ resolveKeyPrefix({
74
+ optionKeyPrefix: undefined,
75
+ env: process.env,
76
+ serverDistDir,
77
+ }),
78
+ );
79
+ expect(res).toBe('vercel.example');
80
+ } finally {
81
+ cleanup();
82
+ }
83
+ });
84
+
85
+ it('uses VERCEL_URL when KEY_PREFIX and BUILD_ID not available', () => {
86
+ const res = withEnv({ VERCEL_URL: 'vercel.example' }, () =>
87
+ resolveKeyPrefix({ optionKeyPrefix: undefined, env: process.env }),
88
+ );
89
+ expect(res).toBe('vercel.example');
90
+ });
91
+
92
+ it('falls back to UNDEFINED_URL_ when nothing else available', () => {
93
+ const res = resolveKeyPrefix({
94
+ optionKeyPrefix: undefined,
95
+ env: process.env,
96
+ });
97
+ expect(res).toBe('UNDEFINED_URL_');
98
+ });
99
+
100
+ it('treats empty env values as absent', () => {
101
+ const { serverDistDir, cleanup } = makeBuildIdTree('BIDX');
102
+ try {
103
+ const res = withEnv({ KEY_PREFIX: '', VERCEL_URL: '' }, () =>
104
+ resolveKeyPrefix({
105
+ optionKeyPrefix: undefined,
106
+ env: process.env,
107
+ serverDistDir,
108
+ }),
109
+ );
110
+ expect(res).toBe('BIDX');
111
+ } finally {
112
+ cleanup();
113
+ }
114
+ });
115
+ });
@@ -0,0 +1,44 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ export function readBuildId(serverDistDir?: string): string | undefined {
5
+ try {
6
+ if (serverDistDir) {
7
+ const buildIdPath = path.join(serverDistDir, '..', 'BUILD_ID');
8
+ const buildId = fs.readFileSync(buildIdPath, 'utf8').trim();
9
+ return buildId || undefined;
10
+ }
11
+ } catch {
12
+ // fall through to cwd-based read
13
+ }
14
+ try {
15
+ const fromCwd = path.join(process.cwd(), '.next', 'BUILD_ID');
16
+ const buildId = fs.readFileSync(fromCwd, 'utf8').trim();
17
+ return buildId || undefined;
18
+ } catch {
19
+ return undefined;
20
+ }
21
+ }
22
+
23
+ export function resolveKeyPrefix({
24
+ optionKeyPrefix,
25
+ serverDistDir,
26
+ env,
27
+ }: {
28
+ optionKeyPrefix?: string;
29
+ serverDistDir?: string;
30
+ env: NodeJS.ProcessEnv;
31
+ }): string {
32
+ // If the option is explicitly provided, honor it even if it's an empty string
33
+ if (optionKeyPrefix !== undefined) {
34
+ return optionKeyPrefix;
35
+ }
36
+
37
+ const keyPrefixEnv =
38
+ env.KEY_PREFIX && env.KEY_PREFIX.length > 0 ? env.KEY_PREFIX : undefined;
39
+ const vercelUrl =
40
+ env.VERCEL_URL && env.VERCEL_URL.length > 0 ? env.VERCEL_URL : undefined;
41
+ const buildId = readBuildId(serverDistDir);
42
+
43
+ return keyPrefixEnv ?? vercelUrl ?? buildId ?? 'UNDEFINED_URL_';
44
+ }
@@ -28,7 +28,7 @@ describe('Next.js 16 Cache Components Integration', () => {
28
28
  __dirname,
29
29
  '..',
30
30
  'integration',
31
- 'next-app-16-1-1-cache-components',
31
+ 'next-app-16-2-3-cache-components',
32
32
  );
33
33
 
34
34
  console.log('Installing Next.js app dependencies...');
@@ -0,0 +1,102 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import { spawn, ChildProcessWithoutNullStreams } from 'child_process';
3
+ import { createClient, RedisClientType } from 'redis';
4
+ import path from 'path';
5
+ import fs from 'fs';
6
+ import fetch from 'node-fetch';
7
+
8
+ const NEXT_APP = 'next-app-15-4-7';
9
+ const PORT = 3075;
10
+ const BASE_URL = `http://localhost:${PORT}`;
11
+
12
+ async function waitForServer(url: string, timeout = 20000) {
13
+ const start = Date.now();
14
+ while (Date.now() - start < timeout) {
15
+ try {
16
+ const res = await fetch(url + '/api/cached-static-fetch');
17
+ if (res.ok) return;
18
+ } catch {}
19
+ await new Promise((r) => setTimeout(r, 300));
20
+ }
21
+ throw new Error('Next.js server did not start in time');
22
+ }
23
+
24
+ describe('BUILD_ID-based key prefix (Next handlers)', () => {
25
+ let nextProcess: ChildProcessWithoutNullStreams | undefined;
26
+ let redis: RedisClientType;
27
+ let appDir: string;
28
+ let buildId: string;
29
+
30
+ beforeAll(async () => {
31
+ appDir = path.join(__dirname, NEXT_APP);
32
+
33
+ // Ensure clean env so BUILD_ID precedence kicks in
34
+ delete (process.env as Record<string, string | undefined>).KEY_PREFIX;
35
+ delete (process.env as Record<string, string | undefined>).VERCEL_URL;
36
+ process.env.VERCEL_ENV = 'production';
37
+ process.env.REDISHOST = process.env.REDISHOST || 'localhost';
38
+ process.env.REDISPORT = process.env.REDISPORT || '6379';
39
+
40
+ // Install + build
41
+ await new Promise<void>((resolve, reject) => {
42
+ const p = spawn('pnpm', ['install'], { cwd: appDir, stdio: 'inherit' });
43
+ p.on('close', (code) =>
44
+ code === 0 ? resolve() : reject(new Error('pnpm i failed')),
45
+ );
46
+ });
47
+ await new Promise<void>((resolve, reject) => {
48
+ const p = spawn('pnpm', ['build'], { cwd: appDir, stdio: 'inherit' });
49
+ p.on('close', (code) =>
50
+ code === 0 ? resolve() : reject(new Error('pnpm build failed')),
51
+ );
52
+ });
53
+
54
+ // Read BUILD_ID from the built app
55
+ buildId = fs
56
+ .readFileSync(path.join(appDir, '.next', 'BUILD_ID'), 'utf8')
57
+ .trim();
58
+
59
+ // Start server without KEY_PREFIX/VERCEL_URL
60
+ nextProcess = spawn('npx', ['next', 'start', '-p', String(PORT)], {
61
+ cwd: appDir,
62
+ env: { ...process.env, SKIP_KEYSPACE_CONFIG_CHECK: 'true' },
63
+ stdio: 'pipe',
64
+ });
65
+
66
+ nextProcess.stderr?.on(
67
+ 'data',
68
+ (d) => process.env.DEBUG_INTEGRATION && console.error(String(d)),
69
+ );
70
+
71
+ await waitForServer(BASE_URL);
72
+
73
+ // Connect Redis
74
+ redis = createClient({
75
+ url: `redis://${process.env.REDISHOST}:${process.env.REDISPORT}`,
76
+ });
77
+ await redis.connect();
78
+ }, 180000);
79
+
80
+ afterAll(async () => {
81
+ try {
82
+ if (redis) await redis.quit();
83
+ } finally {
84
+ if (nextProcess) nextProcess.kill();
85
+ }
86
+ });
87
+
88
+ it('uses BUILD_ID as the Redis key prefix when no KEY_PREFIX/VERCEL_URL set', async () => {
89
+ // Warm cache twice to ensure a set occurs even if first was during startup
90
+ const res1 = await fetch(BASE_URL + '/api/cached-static-fetch');
91
+ expect(res1.ok).toBe(true);
92
+ await new Promise((r) => setTimeout(r, 500));
93
+ const res2 = await fetch(BASE_URL + '/api/cached-static-fetch');
94
+ expect(res2.ok).toBe(true);
95
+
96
+ // Allow background syncs
97
+ await new Promise((r) => setTimeout(r, 1500));
98
+
99
+ const keys = await redis.keys(`${buildId}*`);
100
+ expect(keys.length).toBeGreaterThan(0);
101
+ });
102
+ });
@@ -155,67 +155,79 @@ packages:
155
155
  resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
156
156
  cpu: [arm64]
157
157
  os: [linux]
158
+ libc: [glibc]
158
159
 
159
160
  '@img/sharp-libvips-linux-arm@1.0.5':
160
161
  resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
161
162
  cpu: [arm]
162
163
  os: [linux]
164
+ libc: [glibc]
163
165
 
164
166
  '@img/sharp-libvips-linux-s390x@1.0.4':
165
167
  resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
166
168
  cpu: [s390x]
167
169
  os: [linux]
170
+ libc: [glibc]
168
171
 
169
172
  '@img/sharp-libvips-linux-x64@1.0.4':
170
173
  resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
171
174
  cpu: [x64]
172
175
  os: [linux]
176
+ libc: [glibc]
173
177
 
174
178
  '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
175
179
  resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
176
180
  cpu: [arm64]
177
181
  os: [linux]
182
+ libc: [musl]
178
183
 
179
184
  '@img/sharp-libvips-linuxmusl-x64@1.0.4':
180
185
  resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
181
186
  cpu: [x64]
182
187
  os: [linux]
188
+ libc: [musl]
183
189
 
184
190
  '@img/sharp-linux-arm64@0.33.5':
185
191
  resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
186
192
  engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
187
193
  cpu: [arm64]
188
194
  os: [linux]
195
+ libc: [glibc]
189
196
 
190
197
  '@img/sharp-linux-arm@0.33.5':
191
198
  resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
192
199
  engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
193
200
  cpu: [arm]
194
201
  os: [linux]
202
+ libc: [glibc]
195
203
 
196
204
  '@img/sharp-linux-s390x@0.33.5':
197
205
  resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
198
206
  engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
199
207
  cpu: [s390x]
200
208
  os: [linux]
209
+ libc: [glibc]
201
210
 
202
211
  '@img/sharp-linux-x64@0.33.5':
203
212
  resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
204
213
  engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
205
214
  cpu: [x64]
206
215
  os: [linux]
216
+ libc: [glibc]
207
217
 
208
218
  '@img/sharp-linuxmusl-arm64@0.33.5':
209
219
  resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
210
220
  engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
211
221
  cpu: [arm64]
212
222
  os: [linux]
223
+ libc: [musl]
213
224
 
214
225
  '@img/sharp-linuxmusl-x64@0.33.5':
215
226
  resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
216
227
  engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
217
228
  cpu: [x64]
218
229
  os: [linux]
230
+ libc: [musl]
219
231
 
220
232
  '@img/sharp-wasm32@0.33.5':
221
233
  resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
@@ -260,24 +272,28 @@ packages:
260
272
  engines: {node: '>= 10'}
261
273
  cpu: [arm64]
262
274
  os: [linux]
275
+ libc: [glibc]
263
276
 
264
277
  '@next/swc-linux-arm64-musl@15.0.3':
265
278
  resolution: {integrity: sha512-WkAk6R60mwDjH4lG/JBpb2xHl2/0Vj0ZRu1TIzWuOYfQ9tt9NFsIinI1Epma77JVgy81F32X/AeD+B2cBu/YQA==}
266
279
  engines: {node: '>= 10'}
267
280
  cpu: [arm64]
268
281
  os: [linux]
282
+ libc: [musl]
269
283
 
270
284
  '@next/swc-linux-x64-gnu@15.0.3':
271
285
  resolution: {integrity: sha512-gWL/Cta1aPVqIGgDb6nxkqy06DkwJ9gAnKORdHWX1QBbSZZB+biFYPFti8aKIQL7otCE1pjyPaXpFzGeG2OS2w==}
272
286
  engines: {node: '>= 10'}
273
287
  cpu: [x64]
274
288
  os: [linux]
289
+ libc: [glibc]
275
290
 
276
291
  '@next/swc-linux-x64-musl@15.0.3':
277
292
  resolution: {integrity: sha512-QQEMwFd8r7C0GxQS62Zcdy6GKx999I/rTO2ubdXEe+MlZk9ZiinsrjwoiBL5/57tfyjikgh6GOU2WRQVUej3UA==}
278
293
  engines: {node: '>= 10'}
279
294
  cpu: [x64]
280
295
  os: [linux]
296
+ libc: [musl]
281
297
 
282
298
  '@next/swc-win32-arm64-msvc@15.0.3':
283
299
  resolution: {integrity: sha512-9TEp47AAd/ms9fPNgtgnT7F3M1Hf7koIYYWCMQ9neOwjbVWJsHZxrFbI3iEDJ8rf1TDGpmHbKxXf2IFpAvheIQ==}
@@ -386,24 +402,28 @@ packages:
386
402
  engines: {node: '>= 10'}
387
403
  cpu: [arm64]
388
404
  os: [linux]
405
+ libc: [glibc]
389
406
 
390
407
  '@tailwindcss/oxide-linux-arm64-musl@4.1.4':
391
408
  resolution: {integrity: sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw==}
392
409
  engines: {node: '>= 10'}
393
410
  cpu: [arm64]
394
411
  os: [linux]
412
+ libc: [musl]
395
413
 
396
414
  '@tailwindcss/oxide-linux-x64-gnu@4.1.4':
397
415
  resolution: {integrity: sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ==}
398
416
  engines: {node: '>= 10'}
399
417
  cpu: [x64]
400
418
  os: [linux]
419
+ libc: [glibc]
401
420
 
402
421
  '@tailwindcss/oxide-linux-x64-musl@4.1.4':
403
422
  resolution: {integrity: sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ==}
404
423
  engines: {node: '>= 10'}
405
424
  cpu: [x64]
406
425
  os: [linux]
426
+ libc: [musl]
407
427
 
408
428
  '@tailwindcss/oxide-wasm32-wasi@4.1.4':
409
429
  resolution: {integrity: sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q==}
@@ -439,7 +459,7 @@ packages:
439
459
  '@trieb.work/nextjs-turbo-redis-cache@file:../../..':
440
460
  resolution: {directory: ../../.., type: directory}
441
461
  peerDependencies:
442
- next: '>=15.0.3 <= 15.4.7'
462
+ next: '>=15.0.3 <16.3.0'
443
463
  redis: 4.7.0
444
464
 
445
465
  '@tybys/wasm-util@0.9.0':
@@ -541,36 +561,43 @@ packages:
541
561
  resolution: {integrity: sha512-FX2FV7vpLE/+Z0NZX9/1pwWud5Wocm/2PgpUXbT5aSV3QEB10kBPJAzssOQylvdj8mOHoKl5pVkXpbCwww/T2g==}
542
562
  cpu: [arm64]
543
563
  os: [linux]
564
+ libc: [glibc]
544
565
 
545
566
  '@unrs/resolver-binding-linux-arm64-musl@1.5.0':
546
567
  resolution: {integrity: sha512-+gF97xst1BZb28T3nwwzEtq2ewCoMDGKsenYsZuvpmNrW0019G1iUAunZN+FG55L21y+uP7zsGX06OXDQ/viKw==}
547
568
  cpu: [arm64]
548
569
  os: [linux]
570
+ libc: [musl]
549
571
 
550
572
  '@unrs/resolver-binding-linux-ppc64-gnu@1.5.0':
551
573
  resolution: {integrity: sha512-5bEmVcQw9js8JYM2LkUBw5SeELSIxX+qKf9bFrfFINKAp4noZ//hUxLpbF7u/3gTBN1GsER6xOzIZlw/VTdXtA==}
552
574
  cpu: [ppc64]
553
575
  os: [linux]
576
+ libc: [glibc]
554
577
 
555
578
  '@unrs/resolver-binding-linux-riscv64-gnu@1.5.0':
556
579
  resolution: {integrity: sha512-GGk/8TPUsf1Q99F+lzMdjE6sGL26uJCwQ9TlvBs8zR3cLQNw/MIumPN7zrs3GFGySjnwXc8gA6J3HKbejywmqA==}
557
580
  cpu: [riscv64]
558
581
  os: [linux]
582
+ libc: [glibc]
559
583
 
560
584
  '@unrs/resolver-binding-linux-s390x-gnu@1.5.0':
561
585
  resolution: {integrity: sha512-5uRkFYYVNAeVaA4W/CwugjFN3iDOHCPqsBLCCOoJiMfFMMz4evBRsg+498OFa9w6VcTn2bD5aI+RRayaIgk2Sw==}
562
586
  cpu: [s390x]
563
587
  os: [linux]
588
+ libc: [glibc]
564
589
 
565
590
  '@unrs/resolver-binding-linux-x64-gnu@1.5.0':
566
591
  resolution: {integrity: sha512-j905CZH3nehYy6NimNqC2B14pxn4Ltd7guKMyPTzKehbFXTUgihQS/ZfHQTdojkMzbSwBOSgq1dOrY+IpgxDsA==}
567
592
  cpu: [x64]
568
593
  os: [linux]
594
+ libc: [glibc]
569
595
 
570
596
  '@unrs/resolver-binding-linux-x64-musl@1.5.0':
571
597
  resolution: {integrity: sha512-dmLevQTuzQRwu5A+mvj54R5aye5I4PVKiWqGxg8tTaYP2k2oTs/3Mo8mgnhPk28VoYCi0fdFYpgzCd4AJndQvQ==}
572
598
  cpu: [x64]
573
599
  os: [linux]
600
+ libc: [musl]
574
601
 
575
602
  '@unrs/resolver-binding-wasm32-wasi@1.5.0':
576
603
  resolution: {integrity: sha512-LtJMhwu7avhoi+kKfAZOKN773RtzLBVVF90YJbB0wyMpUj9yQPeA+mteVUI9P70OG/opH47FeV5AWeaNWWgqJg==}
@@ -1284,24 +1311,28 @@ packages:
1284
1311
  engines: {node: '>= 12.0.0'}
1285
1312
  cpu: [arm64]
1286
1313
  os: [linux]
1314
+ libc: [glibc]
1287
1315
 
1288
1316
  lightningcss-linux-arm64-musl@1.29.2:
1289
1317
  resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==}
1290
1318
  engines: {node: '>= 12.0.0'}
1291
1319
  cpu: [arm64]
1292
1320
  os: [linux]
1321
+ libc: [musl]
1293
1322
 
1294
1323
  lightningcss-linux-x64-gnu@1.29.2:
1295
1324
  resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==}
1296
1325
  engines: {node: '>= 12.0.0'}
1297
1326
  cpu: [x64]
1298
1327
  os: [linux]
1328
+ libc: [glibc]
1299
1329
 
1300
1330
  lightningcss-linux-x64-musl@1.29.2:
1301
1331
  resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==}
1302
1332
  engines: {node: '>= 12.0.0'}
1303
1333
  cpu: [x64]
1304
1334
  os: [linux]
1335
+ libc: [musl]
1305
1336
 
1306
1337
  lightningcss-win32-arm64-msvc@1.29.2:
1307
1338
  resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==}