@to11ai/sdk 2.0.0-rc.3 → 2.0.0-rc.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/src/transport.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import type { BehaviorStore } from "./behaviors/store";
2
2
  import {
3
- AmbiguousEnvLabelsError,
4
3
  EnvNotEnabledForProviderError,
5
- EnvNotFoundError,
4
+ LabelNotFoundError,
6
5
  NoActiveReleaseError,
7
6
  NoApplicableRouteError,
8
7
  PromptLabelNotRegisteredError,
@@ -108,14 +107,11 @@ function mapPromptResolveError(
108
107
  );
109
108
  }
110
109
  if (detailsCode === "ENV_NOT_FOUND") {
111
- // Server-side unknown-env. (The client-eager "missing" variant is
112
- // raised in resource.ts before any request — never here.)
113
- return new EnvNotFoundError(
114
- message,
115
- { env, source: "unknown" },
116
- details,
117
- status,
118
- );
110
+ // Server-side unknown-label. (The client-side MissingLabelError — no
111
+ // label supplied at all — is raised in resource.ts before any request.)
112
+ // Prefer `details.label`, falling back to `details.env`.
113
+ const label = typeof d.label === "string" ? d.label : env;
114
+ return new LabelNotFoundError(message, label, details, status);
119
115
  }
120
116
  if (detailsCode === "NO_ACTIVE_RELEASE") {
121
117
  return new NoActiveReleaseError(
@@ -125,9 +121,6 @@ function mapPromptResolveError(
125
121
  status,
126
122
  );
127
123
  }
128
- if (detailsCode === "AMBIGUOUS_ENV_LABELS") {
129
- return new AmbiguousEnvLabelsError(message, { env, slug }, details, status);
130
- }
131
124
  if (PROMPT_POLICY_VIOLATION_DETAILS_CODES.has(detailsCode)) {
132
125
  const policy = typeof d.policy === "string" ? d.policy : undefined;
133
126
  return new PromptPolicyViolationError(
@@ -140,9 +133,9 @@ function mapPromptResolveError(
140
133
  return undefined;
141
134
  }
142
135
 
143
- export type AuthConfig =
144
- | { readonly apiKey: string; readonly getAccessToken?: never }
145
- | { readonly getAccessToken: () => Promise<string>; readonly apiKey?: never };
136
+ export interface AuthConfig {
137
+ readonly apiKey: string;
138
+ }
146
139
 
147
140
  export interface TransportConfig {
148
141
  readonly baseUrl: string;
@@ -248,12 +241,25 @@ function mapHttpError(
248
241
  }
249
242
  if (status === 429) {
250
243
  const retryAfterHeader = headers?.get("retry-after");
251
- const retryAfterSeconds = retryAfterHeader
252
- ? Number(retryAfterHeader)
253
- : undefined;
244
+ let retryAfterSeconds: number | undefined;
245
+ if (retryAfterHeader) {
246
+ const numeric = Number(retryAfterHeader);
247
+ if (Number.isFinite(numeric)) {
248
+ retryAfterSeconds = Math.max(0, numeric);
249
+ } else {
250
+ // RFC 9110 HTTP-date form → seconds until that instant.
251
+ const when = Date.parse(retryAfterHeader);
252
+ if (!Number.isNaN(when)) {
253
+ retryAfterSeconds = Math.max(
254
+ 0,
255
+ Math.round((when - Date.now()) / 1000),
256
+ );
257
+ }
258
+ }
259
+ }
254
260
  return new To11aiRateLimitError(
255
261
  message,
256
- Number.isFinite(retryAfterSeconds) ? retryAfterSeconds : undefined,
262
+ retryAfterSeconds,
257
263
  errorCode,
258
264
  details,
259
265
  );
@@ -395,25 +401,20 @@ export class Transport {
395
401
  private readonly behaviorStore: BehaviorStore | undefined;
396
402
 
397
403
  constructor(config: TransportConfig) {
398
- this.baseUrl = config.baseUrl.replace(/\/$/, "");
404
+ this.baseUrl = config.baseUrl.replace(/\/+$/, "");
399
405
  this.auth = config.auth;
400
406
  this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
401
407
  this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
402
408
  this.behaviorStore = config.behaviorStore;
403
409
  }
404
410
 
405
- private async buildHeaders(): Promise<Record<string, string>> {
411
+ private buildHeaders(): Record<string, string> {
406
412
  const headers: Record<string, string> = {
407
413
  "content-type": "application/json",
408
414
  accept: "application/json",
409
415
  };
410
416
 
411
- if (this.auth.apiKey !== undefined) {
412
- headers.authorization = `Bearer ${this.auth.apiKey}`;
413
- } else if (this.auth.getAccessToken) {
414
- const token = await this.auth.getAccessToken();
415
- headers.authorization = `Bearer ${token}`;
416
- }
417
+ headers.authorization = `Bearer ${this.auth.apiKey}`;
417
418
 
418
419
  const tp = activeTraceContext();
419
420
  if (tp) {
@@ -452,7 +453,7 @@ export class Transport {
452
453
  let attempt = 0;
453
454
 
454
455
  while (true) {
455
- const headers = await this.buildHeaders();
456
+ const headers = this.buildHeaders();
456
457
  const controller = new AbortController();
457
458
  const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
458
459
 
@@ -495,16 +496,28 @@ export class Transport {
495
496
  let json: ApiEnvelope<T> | undefined;
496
497
  const text = await response.text();
497
498
 
498
- try {
499
- json = JSON.parse(text) as ApiEnvelope<T>;
500
- } catch {
501
- // Not a JSON body — treat as generic API error
502
- throw mapHttpError(
503
- response.status,
504
- null,
505
- text || response.statusText,
506
- response.headers,
507
- );
499
+ if (text === "" && response.ok) {
500
+ // A successful empty body (e.g. 204): synthesize an empty-data
501
+ // envelope rather than mis-mapping it to an API error.
502
+ json = {
503
+ success: true,
504
+ code: response.status,
505
+ data: null,
506
+ error: null,
507
+ pagination: null,
508
+ } as ApiEnvelope<T>;
509
+ } else {
510
+ try {
511
+ json = JSON.parse(text) as ApiEnvelope<T>;
512
+ } catch {
513
+ // Not a JSON body — treat as generic API error
514
+ throw mapHttpError(
515
+ response.status,
516
+ null,
517
+ text || response.statusText,
518
+ response.headers,
519
+ );
520
+ }
508
521
  }
509
522
 
510
523
  if (!response.ok) {
@@ -554,7 +567,7 @@ export class Transport {
554
567
  const isTimeout =
555
568
  err instanceof DOMException && err.name === "AbortError";
556
569
  // Only retry on genuine network failures (fetch throws TypeError) or timeouts.
557
- // Unexpected errors (e.g. getAccessToken rejections) propagate immediately.
570
+ // Other unexpected errors propagate immediately.
558
571
  const isNetworkError = err instanceof TypeError;
559
572
 
560
573
  if (
@@ -1,58 +0,0 @@
1
- import type { RenderedPrompt } from "./types";
2
- export interface CacheConfig {
3
- /** Time-to-live in seconds. Default: 300 (5 minutes) */
4
- ttlSeconds?: number;
5
- /** Maximum number of entries. Default: 100 */
6
- maxEntries?: number;
7
- }
8
- /**
9
- * The cache stores enriched `render()` results, which are JSON-cloneable.
10
- */
11
- export type CacheableRendered = RenderedPrompt;
12
- /**
13
- * Build a canonical base cache key from project/slug/labels/variables.
14
- *
15
- * - projectId is included to prevent cross-project cache collisions: a single
16
- * PromptResource can serve multiple projects, so the same slug+labels must
17
- * not share a cache entry across different projects.
18
- * - Labels are sorted alphabetically so ["production", "en"] and ["en", "production"]
19
- * map to the same key.
20
- * - Variables are canonicalized with deterministic sorted-keys JSON before hashing.
21
- * - Each segment is JSON-encoded so that separator characters in slug or label
22
- * values cannot cause key collisions (e.g. a slug containing "|" or ",").
23
- */
24
- export declare function buildCacheKey(projectId: string, slug: string, labels: string[], variables?: Record<string, unknown>): string;
25
- /**
26
- * Build a cache key for an env-aware `render()` call.
27
- *
28
- * Folds in `env` and `subject` ON TOP of the base
29
- * project/slug/labels/variables key so two calls that differ only by
30
- * env/subject do not return a stale cross-env / cross-cohort hit
31
- * (spec §3 "Caching key changes"). The env slug is also forwarded as the
32
- * single resolution label (env→label mapping, correction #3), so it appears
33
- * in the `labels` segment too; including it explicitly here keeps the key
34
- * unambiguous and self-documenting.
35
- *
36
- * `subject` participates in the key so a `subject`-keyed weighted assignment
37
- * caches as the same deterministic variant on repeat (D8); the
38
- * weighted-no-subject bypass is handled in the resource, not here.
39
- */
40
- export declare function buildRenderCacheKey(projectId: string, slug: string, env: string, subject?: string, variables?: Record<string, unknown>): string;
41
- /**
42
- * In-memory LRU cache for RenderedPrompt objects.
43
- *
44
- * Uses a Map (insertion-ordered) as the backing store. On every access, the
45
- * entry is deleted and re-inserted to maintain LRU order — oldest entries sit
46
- * at the front of the Map.
47
- */
48
- export declare class PromptCache {
49
- private readonly ttlMs;
50
- private readonly maxEntries;
51
- private readonly store;
52
- constructor(config?: CacheConfig);
53
- get(key: string): CacheableRendered | undefined;
54
- set(key: string, value: CacheableRendered): void;
55
- delete(key: string): void;
56
- clear(): void;
57
- get size(): number;
58
- }
@@ -1,138 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- function __accessProp(key) {
6
- return this[key];
7
- }
8
- var __toCommonJS = (from) => {
9
- var entry = (__moduleCache ??= new WeakMap).get(from), desc;
10
- if (entry)
11
- return entry;
12
- entry = __defProp({}, "__esModule", { value: true });
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (var key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(entry, key))
16
- __defProp(entry, key, {
17
- get: __accessProp.bind(from, key),
18
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
19
- });
20
- }
21
- __moduleCache.set(from, entry);
22
- return entry;
23
- };
24
- var __moduleCache;
25
- var __returnValue = (v) => v;
26
- function __exportSetter(name, newValue) {
27
- this[name] = __returnValue.bind(null, newValue);
28
- }
29
- var __export = (target, all) => {
30
- for (var name in all)
31
- __defProp(target, name, {
32
- get: all[name],
33
- enumerable: true,
34
- configurable: true,
35
- set: __exportSetter.bind(all, name)
36
- });
37
- };
38
-
39
- // src/routingRules/resource.ts
40
- var exports_resource = {};
41
- __export(exports_resource, {
42
- RoutingRulesResource: () => RoutingRulesResource,
43
- RoutingRuleVersionResource: () => RoutingRuleVersionResource,
44
- RoutingRuleReleaseResource: () => RoutingRuleReleaseResource
45
- });
46
- module.exports = __toCommonJS(exports_resource);
47
- var import_zod = require("zod");
48
- var import_zod2 = require("../generated/zod.gen");
49
- function unwrap(envelope) {
50
- if (envelope.data === null || envelope.data === undefined) {
51
- throw new Error("API returned success but data is null");
52
- }
53
- return envelope.data;
54
- }
55
- function rulePath(projectId, ruleId) {
56
- return `/v1/projects/${encodeURIComponent(projectId)}/routing/rules/${encodeURIComponent(ruleId)}`;
57
- }
58
- function versionsPath(projectId, ruleId) {
59
- return `${rulePath(projectId, ruleId)}/versions`;
60
- }
61
- function versionPath(projectId, ruleId, versionId) {
62
- return `${versionsPath(projectId, ruleId)}/${encodeURIComponent(versionId)}`;
63
- }
64
- function releasesPath(projectId, ruleId) {
65
- return `${rulePath(projectId, ruleId)}/releases`;
66
- }
67
- function releasePath(projectId, ruleId, envSlug) {
68
- return `${releasesPath(projectId, ruleId)}/${encodeURIComponent(envSlug)}`;
69
- }
70
- function releaseRollbackPath(projectId, ruleId, envSlug) {
71
- return `${releasePath(projectId, ruleId, envSlug)}/rollback`;
72
- }
73
-
74
- class RoutingRuleVersionResource {
75
- transport;
76
- constructor(transport) {
77
- this.transport = transport;
78
- }
79
- async create(input) {
80
- const { projectId, ruleId, ...body } = input;
81
- const envelope = await this.transport.request("POST", versionsPath(projectId, ruleId), { body });
82
- return unwrap(envelope);
83
- }
84
- async list(input) {
85
- const query = {};
86
- if (input.cursor !== undefined)
87
- query.cursor = input.cursor;
88
- if (input.limit !== undefined)
89
- query.limit = input.limit;
90
- const envelope = await this.transport.request("GET", versionsPath(input.projectId, input.ruleId), { query });
91
- return unwrap(envelope);
92
- }
93
- async get(input) {
94
- const envelope = await this.transport.request("GET", versionPath(input.projectId, input.ruleId, input.versionId), {
95
- decode: (data) => import_zod2.zRuleVersion.parse(data)
96
- });
97
- return unwrap(envelope);
98
- }
99
- }
100
-
101
- class RoutingRuleReleaseResource {
102
- transport;
103
- constructor(transport) {
104
- this.transport = transport;
105
- }
106
- async set(input) {
107
- const { projectId, ruleId, envSlug, ...body } = input;
108
- const envelope = await this.transport.request("PUT", releasePath(projectId, ruleId, envSlug), { body });
109
- return unwrap(envelope);
110
- }
111
- async rollback(input) {
112
- const { projectId, ruleId, envSlug, ...body } = input;
113
- const envelope = await this.transport.request("POST", releaseRollbackPath(projectId, ruleId, envSlug), { body });
114
- return unwrap(envelope);
115
- }
116
- async list(input) {
117
- const query = {};
118
- if (input.cursor !== undefined)
119
- query.cursor = input.cursor;
120
- if (input.limit !== undefined)
121
- query.limit = input.limit;
122
- const path = input.envSlug !== undefined ? releasePath(input.projectId, input.ruleId, input.envSlug) : releasesPath(input.projectId, input.ruleId);
123
- const envelope = await this.transport.request("GET", path, {
124
- query,
125
- decode: (data) => import_zod.z.array(import_zod2.zRuleRelease).parse(data)
126
- });
127
- return unwrap(envelope);
128
- }
129
- }
130
-
131
- class RoutingRulesResource {
132
- versions;
133
- releases;
134
- constructor(transport) {
135
- this.versions = new RoutingRuleVersionResource(transport);
136
- this.releases = new RoutingRuleReleaseResource(transport);
137
- }
138
- }
@@ -1,98 +0,0 @@
1
- // src/routingRules/resource.ts
2
- import { z } from "zod";
3
- import { zRuleRelease, zRuleVersion } from "../generated/zod.gen";
4
- function unwrap(envelope) {
5
- if (envelope.data === null || envelope.data === undefined) {
6
- throw new Error("API returned success but data is null");
7
- }
8
- return envelope.data;
9
- }
10
- function rulePath(projectId, ruleId) {
11
- return `/v1/projects/${encodeURIComponent(projectId)}/routing/rules/${encodeURIComponent(ruleId)}`;
12
- }
13
- function versionsPath(projectId, ruleId) {
14
- return `${rulePath(projectId, ruleId)}/versions`;
15
- }
16
- function versionPath(projectId, ruleId, versionId) {
17
- return `${versionsPath(projectId, ruleId)}/${encodeURIComponent(versionId)}`;
18
- }
19
- function releasesPath(projectId, ruleId) {
20
- return `${rulePath(projectId, ruleId)}/releases`;
21
- }
22
- function releasePath(projectId, ruleId, envSlug) {
23
- return `${releasesPath(projectId, ruleId)}/${encodeURIComponent(envSlug)}`;
24
- }
25
- function releaseRollbackPath(projectId, ruleId, envSlug) {
26
- return `${releasePath(projectId, ruleId, envSlug)}/rollback`;
27
- }
28
-
29
- class RoutingRuleVersionResource {
30
- transport;
31
- constructor(transport) {
32
- this.transport = transport;
33
- }
34
- async create(input) {
35
- const { projectId, ruleId, ...body } = input;
36
- const envelope = await this.transport.request("POST", versionsPath(projectId, ruleId), { body });
37
- return unwrap(envelope);
38
- }
39
- async list(input) {
40
- const query = {};
41
- if (input.cursor !== undefined)
42
- query.cursor = input.cursor;
43
- if (input.limit !== undefined)
44
- query.limit = input.limit;
45
- const envelope = await this.transport.request("GET", versionsPath(input.projectId, input.ruleId), { query });
46
- return unwrap(envelope);
47
- }
48
- async get(input) {
49
- const envelope = await this.transport.request("GET", versionPath(input.projectId, input.ruleId, input.versionId), {
50
- decode: (data) => zRuleVersion.parse(data)
51
- });
52
- return unwrap(envelope);
53
- }
54
- }
55
-
56
- class RoutingRuleReleaseResource {
57
- transport;
58
- constructor(transport) {
59
- this.transport = transport;
60
- }
61
- async set(input) {
62
- const { projectId, ruleId, envSlug, ...body } = input;
63
- const envelope = await this.transport.request("PUT", releasePath(projectId, ruleId, envSlug), { body });
64
- return unwrap(envelope);
65
- }
66
- async rollback(input) {
67
- const { projectId, ruleId, envSlug, ...body } = input;
68
- const envelope = await this.transport.request("POST", releaseRollbackPath(projectId, ruleId, envSlug), { body });
69
- return unwrap(envelope);
70
- }
71
- async list(input) {
72
- const query = {};
73
- if (input.cursor !== undefined)
74
- query.cursor = input.cursor;
75
- if (input.limit !== undefined)
76
- query.limit = input.limit;
77
- const path = input.envSlug !== undefined ? releasePath(input.projectId, input.ruleId, input.envSlug) : releasesPath(input.projectId, input.ruleId);
78
- const envelope = await this.transport.request("GET", path, {
79
- query,
80
- decode: (data) => z.array(zRuleRelease).parse(data)
81
- });
82
- return unwrap(envelope);
83
- }
84
- }
85
-
86
- class RoutingRulesResource {
87
- versions;
88
- releases;
89
- constructor(transport) {
90
- this.versions = new RoutingRuleVersionResource(transport);
91
- this.releases = new RoutingRuleReleaseResource(transport);
92
- }
93
- }
94
- export {
95
- RoutingRulesResource,
96
- RoutingRuleVersionResource,
97
- RoutingRuleReleaseResource
98
- };
@@ -1,153 +0,0 @@
1
- import type { RenderedPrompt } from "./types";
2
-
3
- export interface CacheConfig {
4
- /** Time-to-live in seconds. Default: 300 (5 minutes) */
5
- ttlSeconds?: number;
6
- /** Maximum number of entries. Default: 100 */
7
- maxEntries?: number;
8
- }
9
-
10
- /**
11
- * The cache stores enriched `render()` results, which are JSON-cloneable.
12
- */
13
- export type CacheableRendered = RenderedPrompt;
14
-
15
- interface CacheEntry {
16
- value: CacheableRendered;
17
- expiresAt: number;
18
- }
19
-
20
- /**
21
- * Build a canonical base cache key from project/slug/labels/variables.
22
- *
23
- * - projectId is included to prevent cross-project cache collisions: a single
24
- * PromptResource can serve multiple projects, so the same slug+labels must
25
- * not share a cache entry across different projects.
26
- * - Labels are sorted alphabetically so ["production", "en"] and ["en", "production"]
27
- * map to the same key.
28
- * - Variables are canonicalized with deterministic sorted-keys JSON before hashing.
29
- * - Each segment is JSON-encoded so that separator characters in slug or label
30
- * values cannot cause key collisions (e.g. a slug containing "|" or ",").
31
- */
32
- export function buildCacheKey(
33
- projectId: string,
34
- slug: string,
35
- labels: string[],
36
- variables?: Record<string, unknown>,
37
- ): string {
38
- const sortedLabels = [...labels].sort();
39
- const varsPart =
40
- variables !== undefined ? JSON.stringify(sortObjectKeys(variables)) : null;
41
- // JSON.stringify each segment to prevent separator-character collisions
42
- return `${JSON.stringify(projectId)}|${JSON.stringify(slug)}|${JSON.stringify(sortedLabels)}|${JSON.stringify(varsPart)}`;
43
- }
44
-
45
- /**
46
- * Build a cache key for an env-aware `render()` call.
47
- *
48
- * Folds in `env` and `subject` ON TOP of the base
49
- * project/slug/labels/variables key so two calls that differ only by
50
- * env/subject do not return a stale cross-env / cross-cohort hit
51
- * (spec §3 "Caching key changes"). The env slug is also forwarded as the
52
- * single resolution label (env→label mapping, correction #3), so it appears
53
- * in the `labels` segment too; including it explicitly here keeps the key
54
- * unambiguous and self-documenting.
55
- *
56
- * `subject` participates in the key so a `subject`-keyed weighted assignment
57
- * caches as the same deterministic variant on repeat (D8); the
58
- * weighted-no-subject bypass is handled in the resource, not here.
59
- */
60
- export function buildRenderCacheKey(
61
- projectId: string,
62
- slug: string,
63
- env: string,
64
- subject?: string,
65
- variables?: Record<string, unknown>,
66
- ): string {
67
- const base = buildCacheKey(projectId, slug, [env], variables);
68
- return `render|${base}|${JSON.stringify(env)}|${JSON.stringify(subject ?? null)}`;
69
- }
70
-
71
- function sortObjectKeys(obj: unknown): unknown {
72
- if (obj === null || typeof obj !== "object") return obj;
73
- if (Array.isArray(obj)) return obj.map(sortObjectKeys);
74
- const sorted: Record<string, unknown> = {};
75
- for (const key of Object.keys(obj as Record<string, unknown>).sort()) {
76
- sorted[key] = sortObjectKeys((obj as Record<string, unknown>)[key]);
77
- }
78
- return sorted;
79
- }
80
-
81
- /**
82
- * In-memory LRU cache for RenderedPrompt objects.
83
- *
84
- * Uses a Map (insertion-ordered) as the backing store. On every access, the
85
- * entry is deleted and re-inserted to maintain LRU order — oldest entries sit
86
- * at the front of the Map.
87
- */
88
- export class PromptCache {
89
- private readonly ttlMs: number;
90
- private readonly maxEntries: number;
91
- private readonly store: Map<string, CacheEntry>;
92
-
93
- constructor(config?: CacheConfig) {
94
- this.ttlMs = (config?.ttlSeconds ?? 300) * 1000;
95
- this.maxEntries = config?.maxEntries ?? 100;
96
- this.store = new Map();
97
- }
98
-
99
- get(key: string): CacheableRendered | undefined {
100
- const entry = this.store.get(key);
101
- if (!entry) return undefined;
102
-
103
- if (Date.now() > entry.expiresAt) {
104
- this.store.delete(key);
105
- return undefined;
106
- }
107
-
108
- // Refresh LRU order — move to end
109
- this.store.delete(key);
110
- this.store.set(key, entry);
111
-
112
- return entry.value;
113
- }
114
-
115
- set(key: string, value: CacheableRendered): void {
116
- // maxEntries: 0 (or negative) means caching is disabled — drop the entry.
117
- if (this.maxEntries <= 0) {
118
- return;
119
- }
120
-
121
- // Remove existing entry so re-insertion moves it to the end
122
- if (this.store.has(key)) {
123
- this.store.delete(key);
124
- }
125
-
126
- // Evict least-recently-used entry if at capacity
127
- while (this.store.size >= this.maxEntries) {
128
- const lruKey = this.store.keys().next().value;
129
- if (lruKey !== undefined) {
130
- this.store.delete(lruKey);
131
- } else {
132
- break;
133
- }
134
- }
135
-
136
- this.store.set(key, {
137
- value,
138
- expiresAt: Date.now() + this.ttlMs,
139
- });
140
- }
141
-
142
- delete(key: string): void {
143
- this.store.delete(key);
144
- }
145
-
146
- clear(): void {
147
- this.store.clear();
148
- }
149
-
150
- get size(): number {
151
- return this.store.size;
152
- }
153
- }