@leejpsd/nextjs-cache-handler 0.2.0 → 0.3.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.
- package/CHANGELOG.md +59 -2
- package/README.md +47 -25
- package/dist/cache-components/index.cjs +235 -46
- package/dist/cache-components/index.cjs.map +1 -1
- package/dist/cache-components/index.d.cts +1 -1
- package/dist/cache-components/index.d.ts +1 -1
- package/dist/cache-components/index.js +227 -46
- package/dist/cache-components/index.js.map +1 -1
- package/dist/incremental/index.cjs +231 -41
- package/dist/incremental/index.cjs.map +1 -1
- package/dist/incremental/index.d.cts +42 -3
- package/dist/incremental/index.d.ts +42 -3
- package/dist/incremental/index.js +223 -41
- package/dist/incremental/index.js.map +1 -1
- package/dist/index.cjs +313 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +305 -70
- package/dist/index.js.map +1 -1
- package/dist/ops/index.d.cts +1 -1
- package/dist/ops/index.d.ts +1 -1
- package/dist/otel/index.cjs +72 -0
- package/dist/otel/index.cjs.map +1 -0
- package/dist/otel/index.d.cts +72 -0
- package/dist/otel/index.d.ts +72 -0
- package/dist/otel/index.js +64 -0
- package/dist/otel/index.js.map +1 -0
- package/dist/shared/client/adapter-ioredis.cjs +49 -10
- package/dist/shared/client/adapter-ioredis.cjs.map +1 -1
- package/dist/shared/client/adapter-ioredis.d.cts +5 -2
- package/dist/shared/client/adapter-ioredis.d.ts +5 -2
- package/dist/shared/client/adapter-ioredis.js +43 -11
- package/dist/shared/client/adapter-ioredis.js.map +1 -1
- package/dist/shared/client/adapter-redis.cjs +22 -7
- package/dist/shared/client/adapter-redis.cjs.map +1 -1
- package/dist/shared/client/adapter-redis.d.cts +1 -1
- package/dist/shared/client/adapter-redis.d.ts +1 -1
- package/dist/shared/client/adapter-redis.js +16 -7
- package/dist/shared/client/adapter-redis.js.map +1 -1
- package/dist/{types-Dq1q6qXd.d.cts → types-Bl-rkLKX.d.cts} +37 -1
- package/dist/{types-Dq1q6qXd.d.ts → types-Bl-rkLKX.d.ts} +37 -1
- package/package.json +20 -3
package/dist/ops/index.d.cts
CHANGED
package/dist/ops/index.d.ts
CHANGED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var module$1 = require('module');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
|
|
6
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
9
|
+
|
|
10
|
+
// node_modules/tsup/assets/cjs_shims.js
|
|
11
|
+
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
12
|
+
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
13
|
+
var packageRequire = module$1.createRequire(importMetaUrl);
|
|
14
|
+
var cwdRequire = module$1.createRequire(
|
|
15
|
+
path__default.default.join(process.cwd(), "__resolve-anchor__.js")
|
|
16
|
+
);
|
|
17
|
+
function nodeRequire(id) {
|
|
18
|
+
try {
|
|
19
|
+
return packageRequire(id);
|
|
20
|
+
} catch {
|
|
21
|
+
return cwdRequire(id);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/otel/index.ts
|
|
26
|
+
var ATTR_ALLOWLIST = ["freshness", "backend", "reason", "op"];
|
|
27
|
+
function lowCardinalityAttrs(event) {
|
|
28
|
+
const out = { type: event.type };
|
|
29
|
+
const meta = event.meta ?? {};
|
|
30
|
+
for (const key of ATTR_ALLOWLIST) {
|
|
31
|
+
const v = meta[key];
|
|
32
|
+
if (typeof v === "string") out[key] = v;
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
function resolveApi() {
|
|
37
|
+
try {
|
|
38
|
+
return nodeRequire("@opentelemetry/api");
|
|
39
|
+
} catch {
|
|
40
|
+
throw new Error(
|
|
41
|
+
"[next-cache] createOtelMetricEmitter needs @opentelemetry/api. Install it in your app (npm i @opentelemetry/api) and configure a MeterProvider, or pass the module via the `api` option."
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function createOtelMetricEmitter(options = {}) {
|
|
46
|
+
const api = options.api ?? resolveApi();
|
|
47
|
+
const meter = api.metrics.getMeter(
|
|
48
|
+
options.meterName ?? "nextjs-cache-handler"
|
|
49
|
+
);
|
|
50
|
+
const counter = meter.createCounter(
|
|
51
|
+
options.counterName ?? "nextjs_cache.events_total",
|
|
52
|
+
{ description: "Cache handler events by type" }
|
|
53
|
+
);
|
|
54
|
+
const histogram = meter.createHistogram(
|
|
55
|
+
options.histogramName ?? "nextjs_cache.op_latency_ms",
|
|
56
|
+
{
|
|
57
|
+
description: "Per-op latency for events that carry an `ms` field",
|
|
58
|
+
unit: "ms"
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
return (event) => {
|
|
62
|
+
const attrs = lowCardinalityAttrs(event);
|
|
63
|
+
counter.add(1, attrs);
|
|
64
|
+
if (typeof event.ms === "number") {
|
|
65
|
+
histogram.record(event.ms, attrs);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
exports.createOtelMetricEmitter = createOtelMetricEmitter;
|
|
71
|
+
//# sourceMappingURL=index.cjs.map
|
|
72
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../node_modules/tsup/assets/cjs_shims.js","../../src/shared/node-require.ts","../../src/otel/index.ts"],"names":["createRequire","path"],"mappings":";;;;;;;;;;AAKA,IAAM,gBAAA,GAAmB,MACvB,OAAO,QAAA,KAAa,WAAA,GAChB,IAAI,GAAA,CAAI,CAAA,KAAA,EAAQ,UAAU,CAAA,CAAE,CAAA,CAAE,IAAA,GAC7B,QAAA,CAAS,aAAA,IAAiB,QAAA,CAAS,aAAA,CAAc,OAAA,CAAQ,WAAA,EAAY,KAAM,QAAA,GAC1E,QAAA,CAAS,aAAA,CAAc,GAAA,GACvB,IAAI,GAAA,CAAI,SAAA,EAAW,QAAA,CAAS,OAAO,CAAA,CAAE,IAAA;AAEtC,IAAM,gCAAgC,gBAAA,EAAiB;ACC9D,IAAM,cAAA,GAAiBA,uBAAc,aAAe,CAAA;AACpD,IAAM,UAAA,GAAaA,sBAAA;AAAA,EACjBC,qBAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,GAAA,IAAO,uBAAuB;AAClD,CAAA;AAEO,SAAS,YAAY,EAAA,EAAqB;AAC/C,EAAA,IAAI;AACF,IAAA,OAAO,eAAe,EAAE,CAAA;AAAA,EAC1B,CAAA,CAAA,MAAQ;AAKN,IAAA,OAAO,WAAW,EAAE,CAAA;AAAA,EACtB;AACF;;;AC2CA,IAAM,cAAA,GAAiB,CAAC,WAAA,EAAa,SAAA,EAAW,UAAU,IAAI,CAAA;AAE9D,SAAS,oBAAoB,KAAA,EAA4C;AACvE,EAAA,MAAM,GAAA,GAA8B,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAK;AACvD,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,IAAA,IAAQ,EAAC;AAC5B,EAAA,KAAA,MAAW,OAAO,cAAA,EAAgB;AAChC,IAAA,MAAM,CAAA,GAAI,KAAK,GAAG,CAAA;AAClB,IAAA,IAAI,OAAO,CAAA,KAAM,QAAA,EAAU,GAAA,CAAI,GAAG,CAAA,GAAI,CAAA;AAAA,EACxC;AACA,EAAA,OAAO,GAAA;AACT;AAEA,SAAS,UAAA,GAA0B;AACjC,EAAA,IAAI;AAGF,IAAA,OAAO,YAAY,oBAAoB,CAAA;AAAA,EACzC,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAGF;AAAA,EACF;AACF;AAOO,SAAS,uBAAA,CACd,OAAA,GAA8B,EAAC,EAChB;AACf,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,IAAO,UAAA,EAAW;AACtC,EAAA,MAAM,KAAA,GAAQ,IAAI,OAAA,CAAQ,QAAA;AAAA,IACxB,QAAQ,SAAA,IAAa;AAAA,GACvB;AACA,EAAA,MAAM,UAAU,KAAA,CAAM,aAAA;AAAA,IACpB,QAAQ,WAAA,IAAe,2BAAA;AAAA,IACvB,EAAE,aAAa,8BAAA;AAA+B,GAChD;AACA,EAAA,MAAM,YAAY,KAAA,CAAM,eAAA;AAAA,IACtB,QAAQ,aAAA,IAAiB,4BAAA;AAAA,IACzB;AAAA,MACE,WAAA,EAAa,oDAAA;AAAA,MACb,IAAA,EAAM;AAAA;AACR,GACF;AAEA,EAAA,OAAO,CAAC,KAAA,KAAuB;AAC7B,IAAA,MAAM,KAAA,GAAQ,oBAAoB,KAAK,CAAA;AACvC,IAAA,OAAA,CAAQ,GAAA,CAAI,GAAG,KAAK,CAAA;AACpB,IAAA,IAAI,OAAO,KAAA,CAAM,EAAA,KAAO,QAAA,EAAU;AAChC,MAAA,SAAA,CAAU,MAAA,CAAO,KAAA,CAAM,EAAA,EAAI,KAAK,CAAA;AAAA,IAClC;AAAA,EACF,CAAA;AACF","file":"index.cjs","sourcesContent":["// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","/**\n * A require() that works from BOTH build outputs.\n *\n * The client/OTel factories load optional peers (`redis`, `ioredis`,\n * `@opentelemetry/api`) lazily at runtime. A bare `require()` compiles fine\n * for CJS but becomes esbuild's `__require` fallback in the ESM build, which\n * throws \"Dynamic require of X is not supported\" in pure-ESM consumers.\n * `createRequire(import.meta.url)` is real in ESM, and tsup's `shims: true`\n * provides an `import.meta.url` polyfill for the CJS build.\n */\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\n\nconst packageRequire = createRequire(import.meta.url);\nconst cwdRequire = createRequire(\n path.join(process.cwd(), \"__resolve-anchor__.js\")\n);\n\nexport function nodeRequire(id: string): unknown {\n try {\n return packageRequire(id);\n } catch {\n // Symlinked installs (npm i <local-path>, pnpm's virtual store) resolve\n // import.meta.url to the package's REAL location, outside the app tree —\n // so peers installed by the app aren't reachable from that anchor. Retry\n // from the process CWD, which under a Next.js server is the app root.\n return cwdRequire(id);\n }\n}\n","/**\n * Built-in OpenTelemetry metric emitter.\n *\n * `@opentelemetry/api` stays out of this package's dependency tree: the\n * module talks to it through structural types and resolves it at runtime\n * (injectable for tests / custom setups). Install `@opentelemetry/api` and\n * configure a MeterProvider (e.g. `@opentelemetry/sdk-node`) in your app,\n * then:\n *\n * ```ts\n * import { createOtelMetricEmitter } from \"@leejpsd/nextjs-cache-handler/otel\";\n *\n * createCacheComponentsHandler({\n * client: { type: \"redis\", url: process.env.REDIS_URL },\n * onMetric: createOtelMetricEmitter(),\n * });\n * ```\n *\n * Instruments:\n * - counter `nextjs_cache.events_total` — one increment per event\n * - histogram `nextjs_cache.op_latency_ms` — for events carrying `ms`\n *\n * Cardinality discipline: only an allowlist of meta fields (freshness,\n * backend, reason, op) is promoted to attributes. Cache keys and tag names\n * never enter the metric pipeline.\n */\n\nimport type { MetricEmitter, MetricEvent } from \"../types.js\";\nimport { nodeRequire } from \"../shared/node-require.js\";\n\nexport interface OtelCounterLike {\n add(value: number, attributes?: Record<string, string>): void;\n}\n\nexport interface OtelHistogramLike {\n record(value: number, attributes?: Record<string, string>): void;\n}\n\nexport interface OtelMeterLike {\n createCounter(\n name: string,\n options?: { description?: string }\n ): OtelCounterLike;\n createHistogram(\n name: string,\n options?: { description?: string; unit?: string }\n ): OtelHistogramLike;\n}\n\n/** The subset of `@opentelemetry/api` this adapter touches. */\nexport interface OtelApiLike {\n metrics: {\n getMeter(name: string, version?: string): OtelMeterLike;\n };\n}\n\nexport interface OtelEmitterOptions {\n /**\n * The `@opentelemetry/api` module (or a compatible object). Defaults to\n * `require(\"@opentelemetry/api\")` (via createRequire, so it works from\n * both the CJS and ESM builds) — pass explicitly in tests or custom setups.\n */\n api?: OtelApiLike;\n /** Meter name. Default: \"nextjs-cache-handler\". */\n meterName?: string;\n /** Counter instrument name. Default: \"nextjs_cache.events_total\". */\n counterName?: string;\n /** Histogram instrument name. Default: \"nextjs_cache.op_latency_ms\". */\n histogramName?: string;\n}\n\nconst ATTR_ALLOWLIST = [\"freshness\", \"backend\", \"reason\", \"op\"] as const;\n\nfunction lowCardinalityAttrs(event: MetricEvent): Record<string, string> {\n const out: Record<string, string> = { type: event.type };\n const meta = event.meta ?? {};\n for (const key of ATTR_ALLOWLIST) {\n const v = meta[key];\n if (typeof v === \"string\") out[key] = v;\n }\n return out;\n}\n\nfunction resolveApi(): OtelApiLike {\n try {\n // Runtime-only lookup — @opentelemetry/api is NOT a dependency of this\n // package. It must come from the application.\n return nodeRequire(\"@opentelemetry/api\") as OtelApiLike;\n } catch {\n throw new Error(\n \"[next-cache] createOtelMetricEmitter needs @opentelemetry/api. \" +\n \"Install it in your app (npm i @opentelemetry/api) and configure a \" +\n \"MeterProvider, or pass the module via the `api` option.\"\n );\n }\n}\n\n/**\n * Build a `MetricEmitter` that forwards every handler event to OpenTelemetry.\n * Wire it to the `onMetric` option — emitter errors are already swallowed by\n * the handlers, so telemetry can never break the cache path.\n */\nexport function createOtelMetricEmitter(\n options: OtelEmitterOptions = {}\n): MetricEmitter {\n const api = options.api ?? resolveApi();\n const meter = api.metrics.getMeter(\n options.meterName ?? \"nextjs-cache-handler\"\n );\n const counter = meter.createCounter(\n options.counterName ?? \"nextjs_cache.events_total\",\n { description: \"Cache handler events by type\" }\n );\n const histogram = meter.createHistogram(\n options.histogramName ?? \"nextjs_cache.op_latency_ms\",\n {\n description: \"Per-op latency for events that carry an `ms` field\",\n unit: \"ms\",\n }\n );\n\n return (event: MetricEvent) => {\n const attrs = lowCardinalityAttrs(event);\n counter.add(1, attrs);\n if (typeof event.ms === \"number\") {\n histogram.record(event.ms, attrs);\n }\n };\n}\n"]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { M as MetricEmitter } from '../types-Bl-rkLKX.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Built-in OpenTelemetry metric emitter.
|
|
5
|
+
*
|
|
6
|
+
* `@opentelemetry/api` stays out of this package's dependency tree: the
|
|
7
|
+
* module talks to it through structural types and resolves it at runtime
|
|
8
|
+
* (injectable for tests / custom setups). Install `@opentelemetry/api` and
|
|
9
|
+
* configure a MeterProvider (e.g. `@opentelemetry/sdk-node`) in your app,
|
|
10
|
+
* then:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { createOtelMetricEmitter } from "@leejpsd/nextjs-cache-handler/otel";
|
|
14
|
+
*
|
|
15
|
+
* createCacheComponentsHandler({
|
|
16
|
+
* client: { type: "redis", url: process.env.REDIS_URL },
|
|
17
|
+
* onMetric: createOtelMetricEmitter(),
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Instruments:
|
|
22
|
+
* - counter `nextjs_cache.events_total` — one increment per event
|
|
23
|
+
* - histogram `nextjs_cache.op_latency_ms` — for events carrying `ms`
|
|
24
|
+
*
|
|
25
|
+
* Cardinality discipline: only an allowlist of meta fields (freshness,
|
|
26
|
+
* backend, reason, op) is promoted to attributes. Cache keys and tag names
|
|
27
|
+
* never enter the metric pipeline.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
interface OtelCounterLike {
|
|
31
|
+
add(value: number, attributes?: Record<string, string>): void;
|
|
32
|
+
}
|
|
33
|
+
interface OtelHistogramLike {
|
|
34
|
+
record(value: number, attributes?: Record<string, string>): void;
|
|
35
|
+
}
|
|
36
|
+
interface OtelMeterLike {
|
|
37
|
+
createCounter(name: string, options?: {
|
|
38
|
+
description?: string;
|
|
39
|
+
}): OtelCounterLike;
|
|
40
|
+
createHistogram(name: string, options?: {
|
|
41
|
+
description?: string;
|
|
42
|
+
unit?: string;
|
|
43
|
+
}): OtelHistogramLike;
|
|
44
|
+
}
|
|
45
|
+
/** The subset of `@opentelemetry/api` this adapter touches. */
|
|
46
|
+
interface OtelApiLike {
|
|
47
|
+
metrics: {
|
|
48
|
+
getMeter(name: string, version?: string): OtelMeterLike;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
interface OtelEmitterOptions {
|
|
52
|
+
/**
|
|
53
|
+
* The `@opentelemetry/api` module (or a compatible object). Defaults to
|
|
54
|
+
* `require("@opentelemetry/api")` (via createRequire, so it works from
|
|
55
|
+
* both the CJS and ESM builds) — pass explicitly in tests or custom setups.
|
|
56
|
+
*/
|
|
57
|
+
api?: OtelApiLike;
|
|
58
|
+
/** Meter name. Default: "nextjs-cache-handler". */
|
|
59
|
+
meterName?: string;
|
|
60
|
+
/** Counter instrument name. Default: "nextjs_cache.events_total". */
|
|
61
|
+
counterName?: string;
|
|
62
|
+
/** Histogram instrument name. Default: "nextjs_cache.op_latency_ms". */
|
|
63
|
+
histogramName?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Build a `MetricEmitter` that forwards every handler event to OpenTelemetry.
|
|
67
|
+
* Wire it to the `onMetric` option — emitter errors are already swallowed by
|
|
68
|
+
* the handlers, so telemetry can never break the cache path.
|
|
69
|
+
*/
|
|
70
|
+
declare function createOtelMetricEmitter(options?: OtelEmitterOptions): MetricEmitter;
|
|
71
|
+
|
|
72
|
+
export { type OtelApiLike, type OtelCounterLike, type OtelEmitterOptions, type OtelHistogramLike, type OtelMeterLike, createOtelMetricEmitter };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { M as MetricEmitter } from '../types-Bl-rkLKX.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Built-in OpenTelemetry metric emitter.
|
|
5
|
+
*
|
|
6
|
+
* `@opentelemetry/api` stays out of this package's dependency tree: the
|
|
7
|
+
* module talks to it through structural types and resolves it at runtime
|
|
8
|
+
* (injectable for tests / custom setups). Install `@opentelemetry/api` and
|
|
9
|
+
* configure a MeterProvider (e.g. `@opentelemetry/sdk-node`) in your app,
|
|
10
|
+
* then:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { createOtelMetricEmitter } from "@leejpsd/nextjs-cache-handler/otel";
|
|
14
|
+
*
|
|
15
|
+
* createCacheComponentsHandler({
|
|
16
|
+
* client: { type: "redis", url: process.env.REDIS_URL },
|
|
17
|
+
* onMetric: createOtelMetricEmitter(),
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Instruments:
|
|
22
|
+
* - counter `nextjs_cache.events_total` — one increment per event
|
|
23
|
+
* - histogram `nextjs_cache.op_latency_ms` — for events carrying `ms`
|
|
24
|
+
*
|
|
25
|
+
* Cardinality discipline: only an allowlist of meta fields (freshness,
|
|
26
|
+
* backend, reason, op) is promoted to attributes. Cache keys and tag names
|
|
27
|
+
* never enter the metric pipeline.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
interface OtelCounterLike {
|
|
31
|
+
add(value: number, attributes?: Record<string, string>): void;
|
|
32
|
+
}
|
|
33
|
+
interface OtelHistogramLike {
|
|
34
|
+
record(value: number, attributes?: Record<string, string>): void;
|
|
35
|
+
}
|
|
36
|
+
interface OtelMeterLike {
|
|
37
|
+
createCounter(name: string, options?: {
|
|
38
|
+
description?: string;
|
|
39
|
+
}): OtelCounterLike;
|
|
40
|
+
createHistogram(name: string, options?: {
|
|
41
|
+
description?: string;
|
|
42
|
+
unit?: string;
|
|
43
|
+
}): OtelHistogramLike;
|
|
44
|
+
}
|
|
45
|
+
/** The subset of `@opentelemetry/api` this adapter touches. */
|
|
46
|
+
interface OtelApiLike {
|
|
47
|
+
metrics: {
|
|
48
|
+
getMeter(name: string, version?: string): OtelMeterLike;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
interface OtelEmitterOptions {
|
|
52
|
+
/**
|
|
53
|
+
* The `@opentelemetry/api` module (or a compatible object). Defaults to
|
|
54
|
+
* `require("@opentelemetry/api")` (via createRequire, so it works from
|
|
55
|
+
* both the CJS and ESM builds) — pass explicitly in tests or custom setups.
|
|
56
|
+
*/
|
|
57
|
+
api?: OtelApiLike;
|
|
58
|
+
/** Meter name. Default: "nextjs-cache-handler". */
|
|
59
|
+
meterName?: string;
|
|
60
|
+
/** Counter instrument name. Default: "nextjs_cache.events_total". */
|
|
61
|
+
counterName?: string;
|
|
62
|
+
/** Histogram instrument name. Default: "nextjs_cache.op_latency_ms". */
|
|
63
|
+
histogramName?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Build a `MetricEmitter` that forwards every handler event to OpenTelemetry.
|
|
67
|
+
* Wire it to the `onMetric` option — emitter errors are already swallowed by
|
|
68
|
+
* the handlers, so telemetry can never break the cache path.
|
|
69
|
+
*/
|
|
70
|
+
declare function createOtelMetricEmitter(options?: OtelEmitterOptions): MetricEmitter;
|
|
71
|
+
|
|
72
|
+
export { type OtelApiLike, type OtelCounterLike, type OtelEmitterOptions, type OtelHistogramLike, type OtelMeterLike, createOtelMetricEmitter };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
// src/shared/node-require.ts
|
|
5
|
+
var packageRequire = createRequire(import.meta.url);
|
|
6
|
+
var cwdRequire = createRequire(
|
|
7
|
+
path.join(process.cwd(), "__resolve-anchor__.js")
|
|
8
|
+
);
|
|
9
|
+
function nodeRequire(id) {
|
|
10
|
+
try {
|
|
11
|
+
return packageRequire(id);
|
|
12
|
+
} catch {
|
|
13
|
+
return cwdRequire(id);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/otel/index.ts
|
|
18
|
+
var ATTR_ALLOWLIST = ["freshness", "backend", "reason", "op"];
|
|
19
|
+
function lowCardinalityAttrs(event) {
|
|
20
|
+
const out = { type: event.type };
|
|
21
|
+
const meta = event.meta ?? {};
|
|
22
|
+
for (const key of ATTR_ALLOWLIST) {
|
|
23
|
+
const v = meta[key];
|
|
24
|
+
if (typeof v === "string") out[key] = v;
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
function resolveApi() {
|
|
29
|
+
try {
|
|
30
|
+
return nodeRequire("@opentelemetry/api");
|
|
31
|
+
} catch {
|
|
32
|
+
throw new Error(
|
|
33
|
+
"[next-cache] createOtelMetricEmitter needs @opentelemetry/api. Install it in your app (npm i @opentelemetry/api) and configure a MeterProvider, or pass the module via the `api` option."
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function createOtelMetricEmitter(options = {}) {
|
|
38
|
+
const api = options.api ?? resolveApi();
|
|
39
|
+
const meter = api.metrics.getMeter(
|
|
40
|
+
options.meterName ?? "nextjs-cache-handler"
|
|
41
|
+
);
|
|
42
|
+
const counter = meter.createCounter(
|
|
43
|
+
options.counterName ?? "nextjs_cache.events_total",
|
|
44
|
+
{ description: "Cache handler events by type" }
|
|
45
|
+
);
|
|
46
|
+
const histogram = meter.createHistogram(
|
|
47
|
+
options.histogramName ?? "nextjs_cache.op_latency_ms",
|
|
48
|
+
{
|
|
49
|
+
description: "Per-op latency for events that carry an `ms` field",
|
|
50
|
+
unit: "ms"
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
return (event) => {
|
|
54
|
+
const attrs = lowCardinalityAttrs(event);
|
|
55
|
+
counter.add(1, attrs);
|
|
56
|
+
if (typeof event.ms === "number") {
|
|
57
|
+
histogram.record(event.ms, attrs);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { createOtelMetricEmitter };
|
|
63
|
+
//# sourceMappingURL=index.js.map
|
|
64
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/shared/node-require.ts","../../src/otel/index.ts"],"names":[],"mappings":";;;;AAaA,IAAM,cAAA,GAAiB,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAA;AACpD,IAAM,UAAA,GAAa,aAAA;AAAA,EACjB,IAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,GAAA,IAAO,uBAAuB;AAClD,CAAA;AAEO,SAAS,YAAY,EAAA,EAAqB;AAC/C,EAAA,IAAI;AACF,IAAA,OAAO,eAAe,EAAE,CAAA;AAAA,EAC1B,CAAA,CAAA,MAAQ;AAKN,IAAA,OAAO,WAAW,EAAE,CAAA;AAAA,EACtB;AACF;;;AC2CA,IAAM,cAAA,GAAiB,CAAC,WAAA,EAAa,SAAA,EAAW,UAAU,IAAI,CAAA;AAE9D,SAAS,oBAAoB,KAAA,EAA4C;AACvE,EAAA,MAAM,GAAA,GAA8B,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAK;AACvD,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,IAAA,IAAQ,EAAC;AAC5B,EAAA,KAAA,MAAW,OAAO,cAAA,EAAgB;AAChC,IAAA,MAAM,CAAA,GAAI,KAAK,GAAG,CAAA;AAClB,IAAA,IAAI,OAAO,CAAA,KAAM,QAAA,EAAU,GAAA,CAAI,GAAG,CAAA,GAAI,CAAA;AAAA,EACxC;AACA,EAAA,OAAO,GAAA;AACT;AAEA,SAAS,UAAA,GAA0B;AACjC,EAAA,IAAI;AAGF,IAAA,OAAO,YAAY,oBAAoB,CAAA;AAAA,EACzC,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAGF;AAAA,EACF;AACF;AAOO,SAAS,uBAAA,CACd,OAAA,GAA8B,EAAC,EAChB;AACf,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,IAAO,UAAA,EAAW;AACtC,EAAA,MAAM,KAAA,GAAQ,IAAI,OAAA,CAAQ,QAAA;AAAA,IACxB,QAAQ,SAAA,IAAa;AAAA,GACvB;AACA,EAAA,MAAM,UAAU,KAAA,CAAM,aAAA;AAAA,IACpB,QAAQ,WAAA,IAAe,2BAAA;AAAA,IACvB,EAAE,aAAa,8BAAA;AAA+B,GAChD;AACA,EAAA,MAAM,YAAY,KAAA,CAAM,eAAA;AAAA,IACtB,QAAQ,aAAA,IAAiB,4BAAA;AAAA,IACzB;AAAA,MACE,WAAA,EAAa,oDAAA;AAAA,MACb,IAAA,EAAM;AAAA;AACR,GACF;AAEA,EAAA,OAAO,CAAC,KAAA,KAAuB;AAC7B,IAAA,MAAM,KAAA,GAAQ,oBAAoB,KAAK,CAAA;AACvC,IAAA,OAAA,CAAQ,GAAA,CAAI,GAAG,KAAK,CAAA;AACpB,IAAA,IAAI,OAAO,KAAA,CAAM,EAAA,KAAO,QAAA,EAAU;AAChC,MAAA,SAAA,CAAU,MAAA,CAAO,KAAA,CAAM,EAAA,EAAI,KAAK,CAAA;AAAA,IAClC;AAAA,EACF,CAAA;AACF","file":"index.js","sourcesContent":["/**\n * A require() that works from BOTH build outputs.\n *\n * The client/OTel factories load optional peers (`redis`, `ioredis`,\n * `@opentelemetry/api`) lazily at runtime. A bare `require()` compiles fine\n * for CJS but becomes esbuild's `__require` fallback in the ESM build, which\n * throws \"Dynamic require of X is not supported\" in pure-ESM consumers.\n * `createRequire(import.meta.url)` is real in ESM, and tsup's `shims: true`\n * provides an `import.meta.url` polyfill for the CJS build.\n */\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\n\nconst packageRequire = createRequire(import.meta.url);\nconst cwdRequire = createRequire(\n path.join(process.cwd(), \"__resolve-anchor__.js\")\n);\n\nexport function nodeRequire(id: string): unknown {\n try {\n return packageRequire(id);\n } catch {\n // Symlinked installs (npm i <local-path>, pnpm's virtual store) resolve\n // import.meta.url to the package's REAL location, outside the app tree —\n // so peers installed by the app aren't reachable from that anchor. Retry\n // from the process CWD, which under a Next.js server is the app root.\n return cwdRequire(id);\n }\n}\n","/**\n * Built-in OpenTelemetry metric emitter.\n *\n * `@opentelemetry/api` stays out of this package's dependency tree: the\n * module talks to it through structural types and resolves it at runtime\n * (injectable for tests / custom setups). Install `@opentelemetry/api` and\n * configure a MeterProvider (e.g. `@opentelemetry/sdk-node`) in your app,\n * then:\n *\n * ```ts\n * import { createOtelMetricEmitter } from \"@leejpsd/nextjs-cache-handler/otel\";\n *\n * createCacheComponentsHandler({\n * client: { type: \"redis\", url: process.env.REDIS_URL },\n * onMetric: createOtelMetricEmitter(),\n * });\n * ```\n *\n * Instruments:\n * - counter `nextjs_cache.events_total` — one increment per event\n * - histogram `nextjs_cache.op_latency_ms` — for events carrying `ms`\n *\n * Cardinality discipline: only an allowlist of meta fields (freshness,\n * backend, reason, op) is promoted to attributes. Cache keys and tag names\n * never enter the metric pipeline.\n */\n\nimport type { MetricEmitter, MetricEvent } from \"../types.js\";\nimport { nodeRequire } from \"../shared/node-require.js\";\n\nexport interface OtelCounterLike {\n add(value: number, attributes?: Record<string, string>): void;\n}\n\nexport interface OtelHistogramLike {\n record(value: number, attributes?: Record<string, string>): void;\n}\n\nexport interface OtelMeterLike {\n createCounter(\n name: string,\n options?: { description?: string }\n ): OtelCounterLike;\n createHistogram(\n name: string,\n options?: { description?: string; unit?: string }\n ): OtelHistogramLike;\n}\n\n/** The subset of `@opentelemetry/api` this adapter touches. */\nexport interface OtelApiLike {\n metrics: {\n getMeter(name: string, version?: string): OtelMeterLike;\n };\n}\n\nexport interface OtelEmitterOptions {\n /**\n * The `@opentelemetry/api` module (or a compatible object). Defaults to\n * `require(\"@opentelemetry/api\")` (via createRequire, so it works from\n * both the CJS and ESM builds) — pass explicitly in tests or custom setups.\n */\n api?: OtelApiLike;\n /** Meter name. Default: \"nextjs-cache-handler\". */\n meterName?: string;\n /** Counter instrument name. Default: \"nextjs_cache.events_total\". */\n counterName?: string;\n /** Histogram instrument name. Default: \"nextjs_cache.op_latency_ms\". */\n histogramName?: string;\n}\n\nconst ATTR_ALLOWLIST = [\"freshness\", \"backend\", \"reason\", \"op\"] as const;\n\nfunction lowCardinalityAttrs(event: MetricEvent): Record<string, string> {\n const out: Record<string, string> = { type: event.type };\n const meta = event.meta ?? {};\n for (const key of ATTR_ALLOWLIST) {\n const v = meta[key];\n if (typeof v === \"string\") out[key] = v;\n }\n return out;\n}\n\nfunction resolveApi(): OtelApiLike {\n try {\n // Runtime-only lookup — @opentelemetry/api is NOT a dependency of this\n // package. It must come from the application.\n return nodeRequire(\"@opentelemetry/api\") as OtelApiLike;\n } catch {\n throw new Error(\n \"[next-cache] createOtelMetricEmitter needs @opentelemetry/api. \" +\n \"Install it in your app (npm i @opentelemetry/api) and configure a \" +\n \"MeterProvider, or pass the module via the `api` option.\"\n );\n }\n}\n\n/**\n * Build a `MetricEmitter` that forwards every handler event to OpenTelemetry.\n * Wire it to the `onMetric` option — emitter errors are already swallowed by\n * the handlers, so telemetry can never break the cache path.\n */\nexport function createOtelMetricEmitter(\n options: OtelEmitterOptions = {}\n): MetricEmitter {\n const api = options.api ?? resolveApi();\n const meter = api.metrics.getMeter(\n options.meterName ?? \"nextjs-cache-handler\"\n );\n const counter = meter.createCounter(\n options.counterName ?? \"nextjs_cache.events_total\",\n { description: \"Cache handler events by type\" }\n );\n const histogram = meter.createHistogram(\n options.histogramName ?? \"nextjs_cache.op_latency_ms\",\n {\n description: \"Per-op latency for events that carry an `ms` field\",\n unit: \"ms\",\n }\n );\n\n return (event: MetricEvent) => {\n const attrs = lowCardinalityAttrs(event);\n counter.add(1, attrs);\n if (typeof event.ms === \"number\") {\n histogram.record(event.ms, attrs);\n }\n };\n}\n"]}
|
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
var module$1 = require('module');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
|
|
6
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
9
|
+
|
|
10
|
+
// node_modules/tsup/assets/cjs_shims.js
|
|
11
|
+
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
12
|
+
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
13
|
+
var packageRequire = module$1.createRequire(importMetaUrl);
|
|
14
|
+
var cwdRequire = module$1.createRequire(
|
|
15
|
+
path__default.default.join(process.cwd(), "__resolve-anchor__.js")
|
|
16
|
+
);
|
|
17
|
+
function nodeRequire(id) {
|
|
18
|
+
try {
|
|
19
|
+
return packageRequire(id);
|
|
20
|
+
} catch {
|
|
21
|
+
return cwdRequire(id);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
9
24
|
|
|
10
25
|
// src/shared/client/adapter-ioredis.ts
|
|
11
26
|
function adapt(client, isClusterClient = false) {
|
|
@@ -89,6 +104,9 @@ function adapt(client, isClusterClient = false) {
|
|
|
89
104
|
on: (event, listener) => {
|
|
90
105
|
client.on(event, listener);
|
|
91
106
|
return client;
|
|
107
|
+
},
|
|
108
|
+
dispose: () => {
|
|
109
|
+
client.disconnect();
|
|
92
110
|
}
|
|
93
111
|
};
|
|
94
112
|
}
|
|
@@ -99,18 +117,36 @@ function adaptCluster(cluster) {
|
|
|
99
117
|
return adapt(cluster, true);
|
|
100
118
|
}
|
|
101
119
|
function createIoredisClient(config) {
|
|
102
|
-
const IORedis =
|
|
120
|
+
const IORedis = nodeRequire("ioredis");
|
|
103
121
|
const Ctor = IORedis.default ?? IORedis;
|
|
104
122
|
return new Ctor(config.url, {
|
|
105
123
|
password: config.password,
|
|
106
124
|
connectTimeout: config.connectTimeout ?? 1500,
|
|
107
125
|
tls: config.tls ? {} : void 0,
|
|
108
126
|
lazyConnect: true,
|
|
109
|
-
maxRetriesPerRequest: 1
|
|
127
|
+
maxRetriesPerRequest: 1,
|
|
128
|
+
// Batch same-tick commands into one RTT (matches redis@5's default
|
|
129
|
+
// behavior) — makes Promise.all fan-outs a single pipeline.
|
|
130
|
+
enableAutoPipelining: true
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function createIoredisSentinel(config) {
|
|
134
|
+
const IORedis = nodeRequire("ioredis");
|
|
135
|
+
const Ctor = IORedis.default ?? IORedis;
|
|
136
|
+
return new Ctor({
|
|
137
|
+
sentinels: config.sentinels,
|
|
138
|
+
name: config.name,
|
|
139
|
+
password: config.password,
|
|
140
|
+
sentinelPassword: config.sentinelPassword,
|
|
141
|
+
connectTimeout: config.connectTimeout ?? 1500,
|
|
142
|
+
tls: config.tls ? {} : void 0,
|
|
143
|
+
lazyConnect: true,
|
|
144
|
+
maxRetriesPerRequest: 1,
|
|
145
|
+
enableAutoPipelining: true
|
|
110
146
|
});
|
|
111
147
|
}
|
|
112
148
|
function createIoredisCluster(config) {
|
|
113
|
-
const IORedis =
|
|
149
|
+
const IORedis = nodeRequire("ioredis");
|
|
114
150
|
const ClusterCtor = IORedis.Cluster ?? IORedis.default.Cluster;
|
|
115
151
|
return new ClusterCtor(config.nodes, {
|
|
116
152
|
redisOptions: {
|
|
@@ -118,7 +154,9 @@ function createIoredisCluster(config) {
|
|
|
118
154
|
tls: config.tls ? {} : void 0
|
|
119
155
|
},
|
|
120
156
|
enableReadyCheck: true,
|
|
121
|
-
lazyConnect: true
|
|
157
|
+
lazyConnect: true,
|
|
158
|
+
// Same-slot commands issued in the same tick share one pipeline.
|
|
159
|
+
enableAutoPipelining: true
|
|
122
160
|
});
|
|
123
161
|
}
|
|
124
162
|
|
|
@@ -126,5 +164,6 @@ exports.adaptCluster = adaptCluster;
|
|
|
126
164
|
exports.adaptIoredis = adaptIoredis;
|
|
127
165
|
exports.createIoredisClient = createIoredisClient;
|
|
128
166
|
exports.createIoredisCluster = createIoredisCluster;
|
|
167
|
+
exports.createIoredisSentinel = createIoredisSentinel;
|
|
129
168
|
//# sourceMappingURL=adapter-ioredis.cjs.map
|
|
130
169
|
//# sourceMappingURL=adapter-ioredis.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/shared/client/adapter-ioredis.ts"],"names":[],"mappings":";;;;;;;;;;AAeA,SAAS,KAAA,CAAM,MAAA,EAAkB,eAAA,GAAkB,KAAA,EAAwB;AACzE,EAAA,OAAO;AAAA,IACL,IAAI,MAAA,GAAS;AAGX,MAAA,OAAO,OAAO,MAAA,KAAW,OAAA;AAAA,IAC3B,CAAA;AAAA,IACA,SAAS,YAAY;AACnB,MAAA,IAAI,MAAA,CAAO,MAAA,KAAW,OAAA,IAAW,MAAA,CAAO,WAAW,YAAA,EAAc;AAC/D,QAAA,MAAM,OAAO,OAAA,EAAQ;AAAA,MACvB;AAAA,IACF,CAAA;AAAA,IACA,GAAA,EAAK,CAAC,CAAA,KAAM,MAAA,CAAO,IAAI,CAAC,CAAA;AAAA,IACxB,KAAK,CAAC,CAAA,EAAG,GAAG,IAAA,KACV,IAAA,EAAM,OAAO,MAAA,GACT,MAAA,CAAO,IAAI,CAAA,EAAG,CAAA,EAAG,MAAM,IAAA,CAAK,EAAE,IAC9B,MAAA,CAAO,GAAA,CAAI,GAAG,CAAC,CAAA;AAAA,IACrB,GAAA,EAAK,OAAO,IAAA,KAAS;AACnB,MAAA,MAAM,MAAM,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,GAAI,IAAA,GAAO,CAAC,IAAI,CAAA;AAC9C,MAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,EAAG,OAAO,CAAA;AAC7B,MAAA,OAAO,MAAA,CAAO,GAAA,CAAI,GAAG,GAAG,CAAA;AAAA,IAC1B,CAAA;AAAA,IACA,IAAA,EAAM,OAAO,CAAA,EAAG,CAAA,KAAM;AACpB,MAAA,MAAM,MAAM,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,GAAI,CAAA,GAAI,CAAC,CAAC,CAAA;AACrC,MAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,EAAG,OAAO,CAAA;AAC7B,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,CAAA,EAAG,GAAG,GAAG,CAAA;AAAA,IAC9B,CAAA;AAAA,IACA,QAAA,EAAU,CAAC,CAAA,KAAM,MAAA,CAAO,SAAS,CAAC,CAAA;AAAA,IAClC,MAAA,EAAQ,OAAO,CAAA,EAAG,CAAA,KAAM;AACtB,MAAA,MAAM,CAAA,GAAI,MAAM,MAAA,CAAO,MAAA,CAAO,GAAG,CAAC,CAAA;AAClC,MAAA,OAAO,CAAA;AAAA,IACT,CAAA;AAAA,IACA,IAAA,EAAM,OAAO,IAAA,KAAS;AACpB,MAAA,IAAI,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAC/B,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,GAAG,IAAI,CAAA;AAAA,IAC5B,CAAA;AAAA,IACA,IAAA,EAAM,CAAC,MAAA,EAAQ,IAAA,KACb,MAAA,CAAO,IAAA;AAAA,MACL,MAAA;AAAA,MACA,KAAK,IAAA,CAAK,MAAA;AAAA,MACV,GAAG,IAAA,CAAK,IAAA;AAAA,MACR,GAAG,IAAA,CAAK;AAAA,KACV;AAAA,IACF,OAAA,EAAS,CAAC,GAAA,EAAK,IAAA,KACb,MAAA,CAAO,OAAA;AAAA,MACL,GAAA;AAAA,MACA,KAAK,IAAA,CAAK,MAAA;AAAA,MACV,GAAG,IAAA,CAAK,IAAA;AAAA,MACR,GAAG,IAAA,CAAK;AAAA,KACV;AAAA,IACF,YAAY,OAAO,MAAA,KAChB,MAAM,MAAA,CAAO,MAAA,CAAO,QAAQ,MAAM,CAAA;AAAA,IACrC,YAAA,EAAc,iBAAiB,EAAE,KAAA,EAAO,OAAM,EAAG;AAG/C,MAAA,IAAI,eAAA,EAAiB;AACnB,QAAA,MAAM,OAAA,GAAU,MAAA;AAChB,QAAA,KAAA,MAAW,IAAA,IAAQ,OAAA,CAAQ,KAAA,CAAM,QAAQ,CAAA,EAAG;AAC1C,UAAA,IAAI,MAAA,GAAS,GAAA;AACb,UAAA,GAAG;AACD,YAAA,MAAM,CAAC,IAAA,EAAM,KAAK,CAAA,GAAwB,MAAM,IAAA,CAAK,IAAA;AAAA,cACnD,MAAA;AAAA,cACA,OAAA;AAAA,cACA,KAAA;AAAA,cACA,OAAA;AAAA,cACA,KAAA,IAAS;AAAA,aACX;AACA,YAAA,MAAA,GAAS,IAAA;AACT,YAAA,IAAI,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG,MAAM,KAAA;AAAA,UAC9B,SAAS,MAAA,KAAW,GAAA;AAAA,QACtB;AAAA,MACF,CAAA,MAAO;AACL,QAAA,MAAM,KAAA,GAAQ,MAAA;AACd,QAAA,IAAI,MAAA,GAAS,GAAA;AACb,QAAA,GAAG;AACD,UAAA,MAAM,CAAC,IAAA,EAAM,KAAK,CAAA,GAAwB,MAAM,KAAA,CAAM,IAAA;AAAA,YACpD,MAAA;AAAA,YACA,OAAA;AAAA,YACA,KAAA;AAAA,YACA,OAAA;AAAA,YACA,KAAA,IAAS;AAAA,WACX;AACA,UAAA,MAAA,GAAS,IAAA;AACT,UAAA,IAAI,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG,MAAM,KAAA;AAAA,QAC9B,SAAS,MAAA,KAAW,GAAA;AAAA,MACtB;AAAA,IACF,CAAA;AAAA,IACA,IAAA,EAAM,MAAM,MAAA,CAAO,IAAA,EAAK;AAAA,IACxB,EAAA,EAAI,CAAC,KAAA,EAAO,QAAA,KAAa;AACvB,MAAA,MAAA,CAAO,EAAA,CAAG,OAAO,QAAQ,CAAA;AACzB,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,GACF;AACF;AAEO,SAAS,aAAa,MAAA,EAAgC;AAC3D,EAAA,OAAO,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5B;AAEO,SAAS,aAAa,OAAA,EAAmC;AAC9D,EAAA,OAAO,KAAA,CAAM,SAAS,IAAI,CAAA;AAC5B;AAEO,SAAS,oBACd,MAAA,EACO;AAEP,EAAA,MAAM,OAAA,GAAU,UAAQ,SAAS,CAAA;AAGjC,EAAA,MAAM,IAAA,GAAO,QAAQ,OAAA,IAAY,OAAA;AACjC,EAAA,OAAO,IAAI,IAAA,CAAK,MAAA,CAAO,GAAA,EAAK;AAAA,IAC1B,UAAU,MAAA,CAAO,QAAA;AAAA,IACjB,cAAA,EAAgB,OAAO,cAAA,IAAkB,IAAA;AAAA,IACzC,GAAA,EAAK,MAAA,CAAO,GAAA,GAAM,EAAC,GAAI,MAAA;AAAA,IACvB,WAAA,EAAa,IAAA;AAAA,IACb,oBAAA,EAAsB;AAAA,GACvB,CAAA;AACH;AAEO,SAAS,qBACd,MAAA,EACS;AAET,EAAA,MAAM,OAAA,GAAU,UAAQ,SAAS,CAAA;AACjC,EAAA,MAAM,WAAA,GACJ,OAAA,CAAQ,OAAA,IACP,OAAA,CAAQ,OAAA,CACN,OAAA;AACL,EAAA,OAAO,IAAI,WAAA,CAAY,MAAA,CAAO,KAAA,EAAO;AAAA,IACnC,YAAA,EAAc;AAAA,MACZ,UAAU,MAAA,CAAO,QAAA;AAAA,MACjB,GAAA,EAAK,MAAA,CAAO,GAAA,GAAM,EAAC,GAAI;AAAA,KACzB;AAAA,IACA,gBAAA,EAAkB,IAAA;AAAA,IAClB,WAAA,EAAa;AAAA,GACd,CAAA;AACH","file":"adapter-ioredis.cjs","sourcesContent":["/**\n * Adapter for `ioredis` (and `ioredis.Cluster`).\n *\n * ioredis uses snake_case method names (`sadd`, `smembers`, `mget`) and a\n * stream-based scan. We normalize both into the camelCase / async-iterable\n * `RedisClientLike` contract so the handlers don't need to know which client\n * is underneath.\n */\n\nimport type { Cluster, Redis } from \"ioredis\";\n\nimport type { RedisClientConfig, RedisClientLike } from \"../../types.js\";\n\ntype AnyRedis = Redis | Cluster;\n\nfunction adapt(client: AnyRedis, isClusterClient = false): RedisClientLike {\n return {\n get isOpen() {\n // ioredis exposes a `status` string. Both single-node and cluster\n // report \"ready\" once connected.\n return client.status === \"ready\";\n },\n connect: async () => {\n if (client.status !== \"ready\" && client.status !== \"connecting\") {\n await client.connect();\n }\n },\n get: (k) => client.get(k),\n set: (k, v, opts) =>\n opts?.EX !== undefined\n ? client.set(k, v, \"EX\", opts.EX)\n : client.set(k, v),\n del: async (keys) => {\n const arr = Array.isArray(keys) ? keys : [keys];\n if (arr.length === 0) return 0;\n return client.del(...arr);\n },\n sAdd: async (k, m) => {\n const arr = Array.isArray(m) ? m : [m];\n if (arr.length === 0) return 0;\n return client.sadd(k, ...arr);\n },\n sMembers: (k) => client.smembers(k),\n expire: async (k, s) => {\n const r = await client.expire(k, s);\n return r;\n },\n mGet: async (keys) => {\n if (keys.length === 0) return [];\n return client.mget(...keys);\n },\n eval: (script, opts) =>\n client.eval(\n script,\n opts.keys.length,\n ...opts.keys,\n ...opts.arguments\n ) as Promise<unknown>,\n evalSha: (sha, opts) =>\n client.evalsha(\n sha,\n opts.keys.length,\n ...opts.keys,\n ...opts.arguments\n ) as Promise<unknown>,\n scriptLoad: async (script) =>\n (await client.script(\"LOAD\", script)) as string,\n scanIterator: async function* ({ MATCH, COUNT }) {\n // Cluster does not support a single SCAN cursor across slots; use\n // ioredis's per-node iterator there. Single-node: standard SCAN cursor.\n if (isClusterClient) {\n const cluster = client as Cluster;\n for (const node of cluster.nodes(\"master\")) {\n let cursor = \"0\";\n do {\n const [next, batch]: [string, string[]] = await node.scan(\n cursor,\n \"MATCH\",\n MATCH,\n \"COUNT\",\n COUNT ?? 100\n );\n cursor = next;\n if (batch.length > 0) yield batch;\n } while (cursor !== \"0\");\n }\n } else {\n const redis = client as Redis;\n let cursor = \"0\";\n do {\n const [next, batch]: [string, string[]] = await redis.scan(\n cursor,\n \"MATCH\",\n MATCH,\n \"COUNT\",\n COUNT ?? 100\n );\n cursor = next;\n if (batch.length > 0) yield batch;\n } while (cursor !== \"0\");\n }\n },\n ping: () => client.ping(),\n on: (event, listener) => {\n client.on(event, listener);\n return client;\n },\n };\n}\n\nexport function adaptIoredis(client: Redis): RedisClientLike {\n return adapt(client, false);\n}\n\nexport function adaptCluster(cluster: Cluster): RedisClientLike {\n return adapt(cluster, true);\n}\n\nexport function createIoredisClient(\n config: Extract<RedisClientConfig, { type: \"ioredis\" }>\n): Redis {\n \n const IORedis = require(\"ioredis\") as typeof import(\"ioredis\");\n // ioredis exports the class as both `default` and a named property.\n // Hitting the default works for both ESM and CJS consumers.\n const Ctor = IORedis.default ?? (IORedis as unknown as typeof import(\"ioredis\").default);\n return new Ctor(config.url, {\n password: config.password,\n connectTimeout: config.connectTimeout ?? 1500,\n tls: config.tls ? {} : undefined,\n lazyConnect: true,\n maxRetriesPerRequest: 1,\n });\n}\n\nexport function createIoredisCluster(\n config: Extract<RedisClientConfig, { type: \"cluster\" }>\n): Cluster {\n \n const IORedis = require(\"ioredis\") as typeof import(\"ioredis\");\n const ClusterCtor =\n IORedis.Cluster ??\n (IORedis.default as unknown as { Cluster: typeof import(\"ioredis\").Cluster })\n .Cluster;\n return new ClusterCtor(config.nodes, {\n redisOptions: {\n password: config.password,\n tls: config.tls ? {} : undefined,\n },\n enableReadyCheck: true,\n lazyConnect: true,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../node_modules/tsup/assets/cjs_shims.js","../../../src/shared/node-require.ts","../../../src/shared/client/adapter-ioredis.ts"],"names":["createRequire","path"],"mappings":"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","file":"adapter-ioredis.cjs","sourcesContent":["// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","/**\n * A require() that works from BOTH build outputs.\n *\n * The client/OTel factories load optional peers (`redis`, `ioredis`,\n * `@opentelemetry/api`) lazily at runtime. A bare `require()` compiles fine\n * for CJS but becomes esbuild's `__require` fallback in the ESM build, which\n * throws \"Dynamic require of X is not supported\" in pure-ESM consumers.\n * `createRequire(import.meta.url)` is real in ESM, and tsup's `shims: true`\n * provides an `import.meta.url` polyfill for the CJS build.\n */\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\n\nconst packageRequire = createRequire(import.meta.url);\nconst cwdRequire = createRequire(\n path.join(process.cwd(), \"__resolve-anchor__.js\")\n);\n\nexport function nodeRequire(id: string): unknown {\n try {\n return packageRequire(id);\n } catch {\n // Symlinked installs (npm i <local-path>, pnpm's virtual store) resolve\n // import.meta.url to the package's REAL location, outside the app tree —\n // so peers installed by the app aren't reachable from that anchor. Retry\n // from the process CWD, which under a Next.js server is the app root.\n return cwdRequire(id);\n }\n}\n","/**\n * Adapter for `ioredis` (and `ioredis.Cluster`).\n *\n * ioredis uses snake_case method names (`sadd`, `smembers`, `mget`) and a\n * stream-based scan. We normalize both into the camelCase / async-iterable\n * `RedisClientLike` contract so the handlers don't need to know which client\n * is underneath.\n */\n\nimport type { Cluster, Redis } from \"ioredis\";\n\nimport type { RedisClientConfig, RedisClientLike } from \"../../types.js\";\nimport { nodeRequire } from \"../node-require.js\";\n\ntype AnyRedis = Redis | Cluster;\n\nfunction adapt(client: AnyRedis, isClusterClient = false): RedisClientLike {\n return {\n get isOpen() {\n // ioredis exposes a `status` string. Both single-node and cluster\n // report \"ready\" once connected.\n return client.status === \"ready\";\n },\n connect: async () => {\n if (client.status !== \"ready\" && client.status !== \"connecting\") {\n await client.connect();\n }\n },\n get: (k) => client.get(k),\n set: (k, v, opts) =>\n opts?.EX !== undefined\n ? client.set(k, v, \"EX\", opts.EX)\n : client.set(k, v),\n del: async (keys) => {\n const arr = Array.isArray(keys) ? keys : [keys];\n if (arr.length === 0) return 0;\n return client.del(...arr);\n },\n sAdd: async (k, m) => {\n const arr = Array.isArray(m) ? m : [m];\n if (arr.length === 0) return 0;\n return client.sadd(k, ...arr);\n },\n sMembers: (k) => client.smembers(k),\n expire: async (k, s) => {\n const r = await client.expire(k, s);\n return r;\n },\n mGet: async (keys) => {\n if (keys.length === 0) return [];\n return client.mget(...keys);\n },\n eval: (script, opts) =>\n client.eval(\n script,\n opts.keys.length,\n ...opts.keys,\n ...opts.arguments\n ) as Promise<unknown>,\n evalSha: (sha, opts) =>\n client.evalsha(\n sha,\n opts.keys.length,\n ...opts.keys,\n ...opts.arguments\n ) as Promise<unknown>,\n scriptLoad: async (script) =>\n (await client.script(\"LOAD\", script)) as string,\n scanIterator: async function* ({ MATCH, COUNT }) {\n // Cluster does not support a single SCAN cursor across slots; use\n // ioredis's per-node iterator there. Single-node: standard SCAN cursor.\n if (isClusterClient) {\n const cluster = client as Cluster;\n for (const node of cluster.nodes(\"master\")) {\n let cursor = \"0\";\n do {\n const [next, batch]: [string, string[]] = await node.scan(\n cursor,\n \"MATCH\",\n MATCH,\n \"COUNT\",\n COUNT ?? 100\n );\n cursor = next;\n if (batch.length > 0) yield batch;\n } while (cursor !== \"0\");\n }\n } else {\n const redis = client as Redis;\n let cursor = \"0\";\n do {\n const [next, batch]: [string, string[]] = await redis.scan(\n cursor,\n \"MATCH\",\n MATCH,\n \"COUNT\",\n COUNT ?? 100\n );\n cursor = next;\n if (batch.length > 0) yield batch;\n } while (cursor !== \"0\");\n }\n },\n ping: () => client.ping(),\n on: (event, listener) => {\n client.on(event, listener);\n return client;\n },\n dispose: () => {\n client.disconnect();\n },\n };\n}\n\nexport function adaptIoredis(client: Redis): RedisClientLike {\n return adapt(client, false);\n}\n\nexport function adaptCluster(cluster: Cluster): RedisClientLike {\n return adapt(cluster, true);\n}\n\nexport function createIoredisClient(\n config: Extract<RedisClientConfig, { type: \"ioredis\" }>\n): Redis {\n \n const IORedis = nodeRequire(\"ioredis\") as typeof import(\"ioredis\");\n // ioredis exports the class as both `default` and a named property.\n // Hitting the default works for both ESM and CJS consumers.\n const Ctor = IORedis.default ?? (IORedis as unknown as typeof import(\"ioredis\").default);\n return new Ctor(config.url, {\n password: config.password,\n connectTimeout: config.connectTimeout ?? 1500,\n tls: config.tls ? {} : undefined,\n lazyConnect: true,\n maxRetriesPerRequest: 1,\n // Batch same-tick commands into one RTT (matches redis@5's default\n // behavior) — makes Promise.all fan-outs a single pipeline.\n enableAutoPipelining: true,\n });\n}\n\nexport function createIoredisSentinel(\n config: Extract<RedisClientConfig, { type: \"sentinel\" }>\n): Redis {\n const IORedis = nodeRequire(\"ioredis\") as typeof import(\"ioredis\");\n const Ctor = IORedis.default ?? (IORedis as unknown as typeof import(\"ioredis\").default);\n return new Ctor({\n sentinels: config.sentinels,\n name: config.name,\n password: config.password,\n sentinelPassword: config.sentinelPassword,\n connectTimeout: config.connectTimeout ?? 1500,\n tls: config.tls ? {} : undefined,\n lazyConnect: true,\n maxRetriesPerRequest: 1,\n enableAutoPipelining: true,\n });\n}\n\nexport function createIoredisCluster(\n config: Extract<RedisClientConfig, { type: \"cluster\" }>\n): Cluster {\n \n const IORedis = nodeRequire(\"ioredis\") as typeof import(\"ioredis\");\n const ClusterCtor =\n IORedis.Cluster ??\n (IORedis.default as unknown as { Cluster: typeof import(\"ioredis\").Cluster })\n .Cluster;\n return new ClusterCtor(config.nodes, {\n redisOptions: {\n password: config.password,\n tls: config.tls ? {} : undefined,\n },\n enableReadyCheck: true,\n lazyConnect: true,\n // Same-slot commands issued in the same tick share one pipeline.\n enableAutoPipelining: true,\n });\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Cluster, Redis } from 'ioredis';
|
|
2
|
-
import { R as RedisClientLike, a as RedisClientConfig } from '../../types-
|
|
2
|
+
import { R as RedisClientLike, a as RedisClientConfig } from '../../types-Bl-rkLKX.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Adapter for `ioredis` (and `ioredis.Cluster`).
|
|
@@ -15,8 +15,11 @@ declare function adaptCluster(cluster: Cluster): RedisClientLike;
|
|
|
15
15
|
declare function createIoredisClient(config: Extract<RedisClientConfig, {
|
|
16
16
|
type: "ioredis";
|
|
17
17
|
}>): Redis;
|
|
18
|
+
declare function createIoredisSentinel(config: Extract<RedisClientConfig, {
|
|
19
|
+
type: "sentinel";
|
|
20
|
+
}>): Redis;
|
|
18
21
|
declare function createIoredisCluster(config: Extract<RedisClientConfig, {
|
|
19
22
|
type: "cluster";
|
|
20
23
|
}>): Cluster;
|
|
21
24
|
|
|
22
|
-
export { adaptCluster, adaptIoredis, createIoredisClient, createIoredisCluster };
|
|
25
|
+
export { adaptCluster, adaptIoredis, createIoredisClient, createIoredisCluster, createIoredisSentinel };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Cluster, Redis } from 'ioredis';
|
|
2
|
-
import { R as RedisClientLike, a as RedisClientConfig } from '../../types-
|
|
2
|
+
import { R as RedisClientLike, a as RedisClientConfig } from '../../types-Bl-rkLKX.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Adapter for `ioredis` (and `ioredis.Cluster`).
|
|
@@ -15,8 +15,11 @@ declare function adaptCluster(cluster: Cluster): RedisClientLike;
|
|
|
15
15
|
declare function createIoredisClient(config: Extract<RedisClientConfig, {
|
|
16
16
|
type: "ioredis";
|
|
17
17
|
}>): Redis;
|
|
18
|
+
declare function createIoredisSentinel(config: Extract<RedisClientConfig, {
|
|
19
|
+
type: "sentinel";
|
|
20
|
+
}>): Redis;
|
|
18
21
|
declare function createIoredisCluster(config: Extract<RedisClientConfig, {
|
|
19
22
|
type: "cluster";
|
|
20
23
|
}>): Cluster;
|
|
21
24
|
|
|
22
|
-
export { adaptCluster, adaptIoredis, createIoredisClient, createIoredisCluster };
|
|
25
|
+
export { adaptCluster, adaptIoredis, createIoredisClient, createIoredisCluster, createIoredisSentinel };
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
// src/shared/node-require.ts
|
|
5
|
+
var packageRequire = createRequire(import.meta.url);
|
|
6
|
+
var cwdRequire = createRequire(
|
|
7
|
+
path.join(process.cwd(), "__resolve-anchor__.js")
|
|
8
|
+
);
|
|
9
|
+
function nodeRequire(id) {
|
|
10
|
+
try {
|
|
11
|
+
return packageRequire(id);
|
|
12
|
+
} catch {
|
|
13
|
+
return cwdRequire(id);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
7
16
|
|
|
8
17
|
// src/shared/client/adapter-ioredis.ts
|
|
9
18
|
function adapt(client, isClusterClient = false) {
|
|
@@ -87,6 +96,9 @@ function adapt(client, isClusterClient = false) {
|
|
|
87
96
|
on: (event, listener) => {
|
|
88
97
|
client.on(event, listener);
|
|
89
98
|
return client;
|
|
99
|
+
},
|
|
100
|
+
dispose: () => {
|
|
101
|
+
client.disconnect();
|
|
90
102
|
}
|
|
91
103
|
};
|
|
92
104
|
}
|
|
@@ -97,18 +109,36 @@ function adaptCluster(cluster) {
|
|
|
97
109
|
return adapt(cluster, true);
|
|
98
110
|
}
|
|
99
111
|
function createIoredisClient(config) {
|
|
100
|
-
const IORedis =
|
|
112
|
+
const IORedis = nodeRequire("ioredis");
|
|
101
113
|
const Ctor = IORedis.default ?? IORedis;
|
|
102
114
|
return new Ctor(config.url, {
|
|
103
115
|
password: config.password,
|
|
104
116
|
connectTimeout: config.connectTimeout ?? 1500,
|
|
105
117
|
tls: config.tls ? {} : void 0,
|
|
106
118
|
lazyConnect: true,
|
|
107
|
-
maxRetriesPerRequest: 1
|
|
119
|
+
maxRetriesPerRequest: 1,
|
|
120
|
+
// Batch same-tick commands into one RTT (matches redis@5's default
|
|
121
|
+
// behavior) — makes Promise.all fan-outs a single pipeline.
|
|
122
|
+
enableAutoPipelining: true
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function createIoredisSentinel(config) {
|
|
126
|
+
const IORedis = nodeRequire("ioredis");
|
|
127
|
+
const Ctor = IORedis.default ?? IORedis;
|
|
128
|
+
return new Ctor({
|
|
129
|
+
sentinels: config.sentinels,
|
|
130
|
+
name: config.name,
|
|
131
|
+
password: config.password,
|
|
132
|
+
sentinelPassword: config.sentinelPassword,
|
|
133
|
+
connectTimeout: config.connectTimeout ?? 1500,
|
|
134
|
+
tls: config.tls ? {} : void 0,
|
|
135
|
+
lazyConnect: true,
|
|
136
|
+
maxRetriesPerRequest: 1,
|
|
137
|
+
enableAutoPipelining: true
|
|
108
138
|
});
|
|
109
139
|
}
|
|
110
140
|
function createIoredisCluster(config) {
|
|
111
|
-
const IORedis =
|
|
141
|
+
const IORedis = nodeRequire("ioredis");
|
|
112
142
|
const ClusterCtor = IORedis.Cluster ?? IORedis.default.Cluster;
|
|
113
143
|
return new ClusterCtor(config.nodes, {
|
|
114
144
|
redisOptions: {
|
|
@@ -116,10 +146,12 @@ function createIoredisCluster(config) {
|
|
|
116
146
|
tls: config.tls ? {} : void 0
|
|
117
147
|
},
|
|
118
148
|
enableReadyCheck: true,
|
|
119
|
-
lazyConnect: true
|
|
149
|
+
lazyConnect: true,
|
|
150
|
+
// Same-slot commands issued in the same tick share one pipeline.
|
|
151
|
+
enableAutoPipelining: true
|
|
120
152
|
});
|
|
121
153
|
}
|
|
122
154
|
|
|
123
|
-
export { adaptCluster, adaptIoredis, createIoredisClient, createIoredisCluster };
|
|
155
|
+
export { adaptCluster, adaptIoredis, createIoredisClient, createIoredisCluster, createIoredisSentinel };
|
|
124
156
|
//# sourceMappingURL=adapter-ioredis.js.map
|
|
125
157
|
//# sourceMappingURL=adapter-ioredis.js.map
|