Skip to content

Rationalize the root export surface and scope-method vocabulary #24

Description

@lan17

Priority

P2 — Medium — finish the stable public/package boundary before v1; not a production correctness blocker.

Current state at v0.11.0

Most of the original surface-area cleanup has shipped:

The root correctly retains backend-neutral serializer, metrics, and semantic Redis contracts needed for dependency injection. Vendor-specific adapters and the Redis protocol use explicit package subpaths.

Draft #89 is now based on v0.11.0, mergeable, and green. It clarifies the everyday API, advanced integration contracts, and lower-level reference material, but it is documentation-only and does not resolve this issue.

Current evidence:

  • Root exports: https://github.com/lan17/DialCache/blob/e06d833ba245706a499d66056fdad15dc1210b68/src/index.ts
  • Scope aliases:

    DialCache/src/dialcache.ts

    Lines 234 to 248 in e06d833

    enable<T>(fn: () => Awaitable<T>): Promise<T> {
    return this.context.enable(fn);
    }
    disable<T>(fn: () => Awaitable<T>): Promise<T> {
    return this.context.disable(fn);
    }
    withEnabled<T>(fn: () => Awaitable<T>): Promise<T> {
    return this.enable(fn);
    }
    withDisabled<T>(fn: () => Awaitable<T>): Promise<T> {
    return this.disable(fn);
    }
  • Package subpaths:

    DialCache/package.json

    Lines 11 to 71 in e06d833

    "exports": {
    ".": {
    "import": {
    "types": "./dist/index.d.ts",
    "default": "./dist/index.js"
    },
    "require": {
    "types": "./dist/index.d.cts",
    "default": "./dist/index.cjs"
    }
    },
    "./node-redis": {
    "import": {
    "types": "./dist/node-redis.d.ts",
    "default": "./dist/node-redis.js"
    },
    "require": {
    "types": "./dist/node-redis.d.cts",
    "default": "./dist/node-redis.cjs"
    }
    },
    "./datadog": {
    "import": {
    "types": "./dist/datadog.d.ts",
    "default": "./dist/datadog.js"
    },
    "require": {
    "types": "./dist/datadog.d.cts",
    "default": "./dist/datadog.cjs"
    }
    },
    "./prometheus": {
    "import": {
    "types": "./dist/prometheus.d.ts",
    "default": "./dist/prometheus.js"
    },
    "require": {
    "types": "./dist/prometheus.d.cts",
    "default": "./dist/prometheus.cjs"
    }
    },
    "./valkey-glide": {
    "import": {
    "types": "./dist/valkey-glide.d.ts",
    "default": "./dist/valkey-glide.js"
    },
    "require": {
    "types": "./dist/valkey-glide.d.cts",
    "default": "./dist/valkey-glide.cjs"
    }
    },
    "./redis-protocol": {
    "import": {
    "types": "./dist/redis-protocol.d.ts",
    "default": "./dist/redis-protocol.js"
    },
    "require": {
    "types": "./dist/redis-protocol.d.cts",
    "default": "./dist/redis-protocol.cjs"
    }
    }

Remaining audit items

The v0.11 holistic review found these unresolved public-boundary questions:

  1. DialCache exposes duplicate scope-method pairs: enable / disable and withEnabled / withDisabled. Documentation consistently teaches enable / disable.
  2. Prometheus and Datadog subpaths expose both adapter classes and one-line factory functions. Supporting two equivalent construction idioms adds API without adding capability.
  3. The root exports RedisConfig from src/internal/redis-cache.ts. The contract may be intentional, but a public type should live behind a public module boundary rather than making internal part of the source-level API architecture.
  4. redisClusterHashTag("") returns "{}"; Redis does not treat an empty brace pair as an effective cluster hash tag. The helper should either reject an empty value or cease to be a public supported helper.
  5. DialCacheKeyConfig.enabled(ttlSec) enables both local and remote layers at 100%. That shorthand can broaden behavior when Redis is later configured and is less explicit than the normal layer-map API. Decide whether to remove it, rename it to make both layers explicit, or retain it with a strong contract before v1.
  6. The root mixes everyday API with lower-level context, key, metrics, serializer, and semantic Redis contracts. Some are necessary public types—for example, DialCacheKey is the input to CacheConfigProvider—while others still need an explicit supported-consumer justification.

The new cached-definition snapshot, runtime key validation, and CommonJS error-identity findings are tracked separately because they are correctness/package-runtime work rather than export-inventory decisions:

Recommended simplicity-first direction — decision required

Subject to explicit approval before any public removal or rename:

  • Make enable / disable canonical and consider removing withEnabled / withDisabled before v1 with a concise migration note.
  • Pick one construction idiom per metrics integration unless both have a demonstrated consumer need.
  • Move any retained public RedisConfig definition out of internal without introducing another user-facing concept.
  • Make the hash-tag helper incapable of returning a value that does not provide the advertised Redis Cluster behavior.
  • Prefer explicit layer configuration over a broad enabled() shorthand unless real usage justifies it.
  • Classify every root export as everyday consumer API, backend-neutral extension/injection contract, or unsupported internal helper.
  • Remove or relocate only exports that lack a supported consumer contract.
  • Do not create new package subpaths merely to move clutter elsewhere.
  • Preserve the existing Prometheus, Datadog, node-redis, Valkey GLIDE, and Redis-protocol boundaries.

Acceptance criteria

  • Record the approved canonical scope vocabulary and compatibility policy.
  • Decide and document the supported construction idiom for both metrics integrations.
  • No public type is sourced from an internal module after the cleanup.
  • The public hash-tag helper either rejects empty input or is removed/moved; it cannot imply colocation that Redis will not provide.
  • Record the approved future of DialCacheKeyConfig.enabled() with an explicit migration path if it changes.
  • Classify every root export by supported role.
  • Keep normal consumer API and intentional backend-neutral injection contracts accessible without vendor dependencies.
  • Add packed ESM/CommonJS declaration coverage for every retained or moved contract.
  • Include a migration table for every public removal, rename, or move.
  • Reconcile the resulting API reference with docs: focus README on safe cache rollouts #89 if that documentation PR merges first.
  • Do not remove an export solely because repository tests do not consume it.
  • Obtain explicit approval before public removals, renames, or subpath changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions