@hypequery/clickhouse 0.0.0-canary-20260306132943

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 (142) hide show
  1. package/LICENSE +201 -0
  2. package/README-CLI.md +123 -0
  3. package/README.md +316 -0
  4. package/dist/cli/bin.js +285 -0
  5. package/dist/cli/generate-types.d.ts +5 -0
  6. package/dist/cli/generate-types.js +218 -0
  7. package/dist/cli/index.d.ts +2 -0
  8. package/dist/cli/index.js +2 -0
  9. package/dist/core/cache/cache-manager.d.ts +4 -0
  10. package/dist/core/cache/cache-manager.d.ts.map +1 -0
  11. package/dist/core/cache/cache-manager.js +176 -0
  12. package/dist/core/cache/controller.d.ts +15 -0
  13. package/dist/core/cache/controller.d.ts.map +1 -0
  14. package/dist/core/cache/controller.js +58 -0
  15. package/dist/core/cache/key.d.ts +11 -0
  16. package/dist/core/cache/key.d.ts.map +1 -0
  17. package/dist/core/cache/key.js +26 -0
  18. package/dist/core/cache/providers/memory-lru.d.ts +31 -0
  19. package/dist/core/cache/providers/memory-lru.d.ts.map +1 -0
  20. package/dist/core/cache/providers/memory-lru.js +156 -0
  21. package/dist/core/cache/providers/noop.d.ts +7 -0
  22. package/dist/core/cache/providers/noop.d.ts.map +1 -0
  23. package/dist/core/cache/providers/noop.js +11 -0
  24. package/dist/core/cache/runtime-context.d.ts +30 -0
  25. package/dist/core/cache/runtime-context.d.ts.map +1 -0
  26. package/dist/core/cache/runtime-context.js +58 -0
  27. package/dist/core/cache/serialization.d.ts +6 -0
  28. package/dist/core/cache/serialization.d.ts.map +1 -0
  29. package/dist/core/cache/serialization.js +166 -0
  30. package/dist/core/cache/types.d.ts +52 -0
  31. package/dist/core/cache/types.d.ts.map +1 -0
  32. package/dist/core/cache/types.js +1 -0
  33. package/dist/core/cache/utils.d.ts +9 -0
  34. package/dist/core/cache/utils.d.ts.map +1 -0
  35. package/dist/core/cache/utils.js +30 -0
  36. package/dist/core/connection.d.ts +112 -0
  37. package/dist/core/connection.d.ts.map +1 -0
  38. package/dist/core/connection.js +150 -0
  39. package/dist/core/cross-filter.d.ts +73 -0
  40. package/dist/core/cross-filter.d.ts.map +1 -0
  41. package/dist/core/cross-filter.js +142 -0
  42. package/dist/core/env/auto-client.browser.d.ts +3 -0
  43. package/dist/core/env/auto-client.browser.d.ts.map +1 -0
  44. package/dist/core/env/auto-client.browser.js +3 -0
  45. package/dist/core/env/auto-client.d.ts +9 -0
  46. package/dist/core/env/auto-client.d.ts.map +1 -0
  47. package/dist/core/env/auto-client.js +21 -0
  48. package/dist/core/features/aggregations.d.ts +98 -0
  49. package/dist/core/features/aggregations.d.ts.map +1 -0
  50. package/dist/core/features/aggregations.js +36 -0
  51. package/dist/core/features/analytics.d.ts +81 -0
  52. package/dist/core/features/analytics.d.ts.map +1 -0
  53. package/dist/core/features/analytics.js +45 -0
  54. package/dist/core/features/cross-filtering.d.ts +11 -0
  55. package/dist/core/features/cross-filtering.d.ts.map +1 -0
  56. package/dist/core/features/cross-filtering.js +90 -0
  57. package/dist/core/features/executor.d.ts +21 -0
  58. package/dist/core/features/executor.d.ts.map +1 -0
  59. package/dist/core/features/executor.js +146 -0
  60. package/dist/core/features/filtering.d.ts +99 -0
  61. package/dist/core/features/filtering.d.ts.map +1 -0
  62. package/dist/core/features/filtering.js +118 -0
  63. package/dist/core/features/joins.d.ts +26 -0
  64. package/dist/core/features/joins.d.ts.map +1 -0
  65. package/dist/core/features/joins.js +17 -0
  66. package/dist/core/features/query-modifiers.d.ts +116 -0
  67. package/dist/core/features/query-modifiers.d.ts.map +1 -0
  68. package/dist/core/features/query-modifiers.js +51 -0
  69. package/dist/core/formatters/sql-formatter.d.ts +9 -0
  70. package/dist/core/formatters/sql-formatter.d.ts.map +1 -0
  71. package/dist/core/formatters/sql-formatter.js +131 -0
  72. package/dist/core/join-relationships.d.ts +51 -0
  73. package/dist/core/join-relationships.d.ts.map +1 -0
  74. package/dist/core/join-relationships.js +54 -0
  75. package/dist/core/query-builder.d.ts +236 -0
  76. package/dist/core/query-builder.d.ts.map +1 -0
  77. package/dist/core/query-builder.js +495 -0
  78. package/dist/core/tests/index.d.ts +2 -0
  79. package/dist/core/tests/index.d.ts.map +1 -0
  80. package/dist/core/tests/index.js +1 -0
  81. package/dist/core/tests/integration/setup.d.ts +48 -0
  82. package/dist/core/tests/integration/setup.d.ts.map +1 -0
  83. package/dist/core/tests/integration/setup.js +349 -0
  84. package/dist/core/tests/integration/test-config.d.ts +15 -0
  85. package/dist/core/tests/integration/test-config.d.ts.map +1 -0
  86. package/dist/core/tests/integration/test-config.js +14 -0
  87. package/dist/core/tests/integration/test-data.json +190 -0
  88. package/dist/core/tests/test-utils.d.ts +44 -0
  89. package/dist/core/tests/test-utils.d.ts.map +1 -0
  90. package/dist/core/tests/test-utils.js +65 -0
  91. package/dist/core/types/builder-state.d.ts +27 -0
  92. package/dist/core/types/builder-state.d.ts.map +1 -0
  93. package/dist/core/types/builder-state.js +1 -0
  94. package/dist/core/types/select-types.d.ts +33 -0
  95. package/dist/core/types/select-types.d.ts.map +1 -0
  96. package/dist/core/types/select-types.js +1 -0
  97. package/dist/core/types/type-helpers.d.ts +5 -0
  98. package/dist/core/types/type-helpers.d.ts.map +1 -0
  99. package/dist/core/types/type-helpers.js +1 -0
  100. package/dist/core/utils/logger.d.ts +43 -0
  101. package/dist/core/utils/logger.d.ts.map +1 -0
  102. package/dist/core/utils/logger.js +104 -0
  103. package/dist/core/utils/predicate-builder.d.ts +33 -0
  104. package/dist/core/utils/predicate-builder.d.ts.map +1 -0
  105. package/dist/core/utils/predicate-builder.js +95 -0
  106. package/dist/core/utils/sql-expressions.d.ts +77 -0
  107. package/dist/core/utils/sql-expressions.d.ts.map +1 -0
  108. package/dist/core/utils/sql-expressions.js +54 -0
  109. package/dist/core/utils/streaming-helpers.d.ts +2 -0
  110. package/dist/core/utils/streaming-helpers.d.ts.map +1 -0
  111. package/dist/core/utils/streaming-helpers.js +137 -0
  112. package/dist/core/utils.d.ts +3 -0
  113. package/dist/core/utils.d.ts.map +1 -0
  114. package/dist/core/utils.js +29 -0
  115. package/dist/core/validators/filter-validator.d.ts +9 -0
  116. package/dist/core/validators/filter-validator.d.ts.map +1 -0
  117. package/dist/core/validators/filter-validator.js +19 -0
  118. package/dist/core/validators/value-validator.d.ts +7 -0
  119. package/dist/core/validators/value-validator.d.ts.map +1 -0
  120. package/dist/core/validators/value-validator.js +47 -0
  121. package/dist/formatters/index.d.ts +2 -0
  122. package/dist/formatters/index.d.ts.map +1 -0
  123. package/dist/formatters/index.js +1 -0
  124. package/dist/index.d.ts +20 -0
  125. package/dist/index.d.ts.map +1 -0
  126. package/dist/index.js +21 -0
  127. package/dist/types/base.d.ts +50 -0
  128. package/dist/types/base.d.ts.map +1 -0
  129. package/dist/types/base.js +1 -0
  130. package/dist/types/clickhouse-types.d.ts +17 -0
  131. package/dist/types/clickhouse-types.d.ts.map +1 -0
  132. package/dist/types/clickhouse-types.js +1 -0
  133. package/dist/types/filters.d.ts +53 -0
  134. package/dist/types/filters.d.ts.map +1 -0
  135. package/dist/types/filters.js +1 -0
  136. package/dist/types/index.d.ts +5 -0
  137. package/dist/types/index.d.ts.map +1 -0
  138. package/dist/types/index.js +4 -0
  139. package/dist/types/schema.d.ts +19 -0
  140. package/dist/types/schema.d.ts.map +1 -0
  141. package/dist/types/schema.js +1 -0
  142. package/package.json +90 -0
@@ -0,0 +1,176 @@
1
+ import { computeCacheKey } from './key.js';
2
+ import { mergeCacheOptions } from './runtime-context.js';
3
+ import { logger } from '../utils/logger.js';
4
+ import { substituteParameters } from '../utils.js';
5
+ function isCacheable(options) {
6
+ const ttl = options.ttlMs ?? 0;
7
+ const stale = options.staleTtlMs ?? 0;
8
+ return ttl > 0 || stale > 0;
9
+ }
10
+ function deriveTags(builder) {
11
+ const tags = new Set();
12
+ tags.add(builder.getTableName());
13
+ const joins = builder.getConfig().joins || [];
14
+ joins.forEach(join => tags.add(join.table));
15
+ return Array.from(tags);
16
+ }
17
+ async function logCacheHit({ sql, parameters, status, cacheKey, options, rowCount, ageMs, queryId }) {
18
+ const finalSQL = substituteParameters(sql, parameters);
19
+ const timestamp = Date.now();
20
+ logger.logQuery({
21
+ query: finalSQL,
22
+ parameters,
23
+ startTime: timestamp,
24
+ endTime: timestamp,
25
+ duration: 0,
26
+ status: 'completed',
27
+ rowCount,
28
+ queryId,
29
+ cacheStatus: status,
30
+ cacheKey,
31
+ cacheMode: options.mode,
32
+ cacheAgeMs: ageMs,
33
+ cacheRowCount: rowCount
34
+ });
35
+ }
36
+ export async function executeWithCache(builder, options) {
37
+ const runtime = builder.getRuntimeContext();
38
+ const provider = runtime.provider;
39
+ const normalizedExecuteCache = options?.cache === false
40
+ ? { mode: 'no-store' }
41
+ : options?.cache;
42
+ const mergedOptions = mergeCacheOptions(runtime.defaults, builder.getCacheOptions(), normalizedExecuteCache);
43
+ const mode = mergedOptions.mode ?? 'no-store';
44
+ if (!provider || mode === 'no-store' || !isCacheable(mergedOptions)) {
45
+ return runWithoutCache('bypass');
46
+ }
47
+ const activeProvider = provider;
48
+ const { sql, parameters } = builder.toSQLWithParams();
49
+ const tableName = builder.getTableName();
50
+ const namespace = mergedOptions.namespace || runtime.namespace;
51
+ const key = mergedOptions.key || computeCacheKey({
52
+ namespace,
53
+ sql,
54
+ parameters,
55
+ settings: builder.getConfig().settings ? { settings: builder.getConfig().settings } : undefined,
56
+ version: runtime.versionTag,
57
+ tableName
58
+ });
59
+ const entry = await activeProvider.get(key);
60
+ if (!entry) {
61
+ runtime.parsedValues.delete(key);
62
+ }
63
+ const fresh = entry ? Date.now() < entry.createdAt + entry.ttlMs : false;
64
+ const staleAcceptable = entry ? Date.now() < entry.createdAt + entry.ttlMs + entry.staleTtlMs : false;
65
+ const deserialize = mergedOptions.deserialize || runtime.deserialize;
66
+ const serialize = mergedOptions.serialize || runtime.serialize;
67
+ const respondFromCache = async (cacheEntry, status) => {
68
+ const memoized = runtime.parsedValues.get(key);
69
+ let rows;
70
+ if (memoized && memoized.createdAt === cacheEntry.createdAt) {
71
+ rows = memoized.rows;
72
+ }
73
+ else {
74
+ rows = await deserialize(cacheEntry.value);
75
+ runtime.parsedValues.set(key, { createdAt: cacheEntry.createdAt, rows, tags: cacheEntry.tags });
76
+ }
77
+ const cacheAge = Date.now() - cacheEntry.createdAt;
78
+ if (status === 'hit') {
79
+ runtime.stats.hits += 1;
80
+ }
81
+ else if (status === 'stale-hit') {
82
+ runtime.stats.staleHits += 1;
83
+ }
84
+ await logCacheHit({
85
+ sql,
86
+ parameters,
87
+ status,
88
+ cacheKey: key,
89
+ options: mergedOptions,
90
+ rowCount: cacheEntry.rowCount ?? rows.length,
91
+ ageMs: cacheAge,
92
+ queryId: options?.queryId
93
+ });
94
+ return rows;
95
+ };
96
+ if (mode === 'cache-first') {
97
+ if (entry && fresh) {
98
+ return respondFromCache(entry, 'hit');
99
+ }
100
+ runtime.stats.misses += 1;
101
+ return fetchAndStore('miss');
102
+ }
103
+ if (mode === 'stale-while-revalidate') {
104
+ if (entry && fresh) {
105
+ return respondFromCache(entry, 'hit');
106
+ }
107
+ if (entry && staleAcceptable) {
108
+ scheduleRevalidation();
109
+ return respondFromCache(entry, 'stale-hit');
110
+ }
111
+ runtime.stats.misses += 1;
112
+ return fetchAndStore('miss');
113
+ }
114
+ if (mode === 'network-first') {
115
+ try {
116
+ runtime.stats.misses += 1;
117
+ return await fetchAndStore('miss');
118
+ }
119
+ catch (error) {
120
+ if (mergedOptions.staleIfError && entry && staleAcceptable) {
121
+ return respondFromCache(entry, 'stale-hit');
122
+ }
123
+ throw error;
124
+ }
125
+ }
126
+ return runWithoutCache('bypass');
127
+ async function fetchAndStore(cacheStatus) {
128
+ if (mergedOptions.dedupe !== false && runtime.inFlight.has(key)) {
129
+ return runtime.inFlight.get(key);
130
+ }
131
+ const promise = (async () => {
132
+ const rows = await builder.getExecutor().execute({
133
+ queryId: options?.queryId,
134
+ logContext: { cacheStatus, cacheKey: key, cacheMode: mode }
135
+ });
136
+ const encoded = await serialize(rows);
137
+ const ttlMs = mergedOptions.ttlMs ?? 0;
138
+ const staleTtlMs = mergedOptions.staleTtlMs ?? 0;
139
+ const cacheTimeMs = mergedOptions.cacheTimeMs ?? ttlMs + staleTtlMs;
140
+ const derivedTags = deriveTags(builder);
141
+ const tagSet = new Set([...(mergedOptions.tags || []), ...derivedTags]);
142
+ const newEntry = {
143
+ value: encoded.payload,
144
+ createdAt: Date.now(),
145
+ ttlMs,
146
+ staleTtlMs,
147
+ cacheTimeMs,
148
+ tags: Array.from(tagSet),
149
+ rowCount: rows.length,
150
+ byteSize: encoded.byteSize,
151
+ sqlFingerprint: key
152
+ };
153
+ await activeProvider.set(key, newEntry);
154
+ runtime.parsedValues.set(key, { createdAt: newEntry.createdAt, rows, tags: newEntry.tags });
155
+ return rows;
156
+ })();
157
+ if (mergedOptions.dedupe !== false) {
158
+ runtime.inFlight.set(key, promise);
159
+ promise.finally(() => runtime.inFlight.delete(key));
160
+ }
161
+ return promise;
162
+ }
163
+ function scheduleRevalidation() {
164
+ runtime.stats.revalidations += 1;
165
+ fetchAndStore('revalidate').catch(() => undefined);
166
+ }
167
+ function runWithoutCache(cacheStatus) {
168
+ if (provider) {
169
+ runtime.stats.misses += 1;
170
+ }
171
+ return builder.getExecutor().execute({
172
+ queryId: options?.queryId,
173
+ logContext: { cacheStatus, cacheMode: mode }
174
+ });
175
+ }
176
+ }
@@ -0,0 +1,15 @@
1
+ import type { QueryRuntimeContext } from './runtime-context.js';
2
+ import type { CacheStats } from './types.js';
3
+ export declare class CacheController {
4
+ private context;
5
+ constructor(context: QueryRuntimeContext);
6
+ invalidateKey(key: string): Promise<void>;
7
+ invalidateTags(tags: string[]): Promise<void>;
8
+ clear(): Promise<void>;
9
+ warm(queries: Array<() => Promise<unknown>>): Promise<void>;
10
+ getStats(): CacheStats & {
11
+ hitRate: number;
12
+ };
13
+ private removeParsedValuesByTags;
14
+ }
15
+ //# sourceMappingURL=controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../src/core/cache/controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,qBAAa,eAAe;IACd,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,mBAAmB;IAE1C,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAe7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtB,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjE,QAAQ,IAAI,UAAU,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE;IAO5C,OAAO,CAAC,wBAAwB;CAWjC"}
@@ -0,0 +1,58 @@
1
+ import { logger } from '../utils/logger.js';
2
+ export class CacheController {
3
+ context;
4
+ constructor(context) {
5
+ this.context = context;
6
+ }
7
+ async invalidateKey(key) {
8
+ this.context.parsedValues.delete(key);
9
+ if (!this.context.provider)
10
+ return;
11
+ await this.context.provider.delete(key);
12
+ }
13
+ async invalidateTags(tags) {
14
+ if (!tags.length)
15
+ return;
16
+ const deleteByTag = this.context.provider?.deleteByTag;
17
+ if (!deleteByTag) {
18
+ logger.warn('Cache provider does not support tag invalidation. Tags ignored.', {
19
+ namespace: this.context.namespace,
20
+ tags
21
+ });
22
+ this.removeParsedValuesByTags(tags);
23
+ return;
24
+ }
25
+ await Promise.all(tags.map(tag => deleteByTag.call(this.context.provider, this.context.namespace, tag)));
26
+ this.removeParsedValuesByTags(tags);
27
+ }
28
+ async clear() {
29
+ if (this.context.provider?.clearNamespace) {
30
+ await this.context.provider.clearNamespace(this.context.namespace);
31
+ }
32
+ this.context.parsedValues.clear();
33
+ }
34
+ async warm(queries) {
35
+ if (!queries.length)
36
+ return;
37
+ await Promise.all(queries.map(query => query()));
38
+ }
39
+ getStats() {
40
+ const stats = { ...this.context.stats };
41
+ const total = stats.hits + stats.misses + stats.staleHits;
42
+ const hitRate = total > 0 ? (stats.hits + stats.staleHits) / total : 0;
43
+ return { ...stats, hitRate };
44
+ }
45
+ removeParsedValuesByTags(tags) {
46
+ if (!tags.length)
47
+ return;
48
+ const target = new Set(tags);
49
+ for (const [key, record] of this.context.parsedValues) {
50
+ if (!record.tags?.length)
51
+ continue;
52
+ const intersects = record.tags.some(tag => target.has(tag));
53
+ if (intersects) {
54
+ this.context.parsedValues.delete(key);
55
+ }
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,11 @@
1
+ interface CacheKeyInput {
2
+ namespace: string;
3
+ sql: string;
4
+ parameters: unknown[];
5
+ settings?: Record<string, unknown> | undefined;
6
+ version?: string;
7
+ tableName?: string;
8
+ }
9
+ export declare function computeCacheKey({ namespace, sql, parameters, settings, version, tableName }: CacheKeyInput): string;
10
+ export {};
11
+ //# sourceMappingURL=key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key.d.ts","sourceRoot":"","sources":["../../../src/core/cache/key.ts"],"names":[],"mappings":"AAEA,UAAU,aAAa;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,OAAO,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqBD,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,GAAG,EACH,UAAU,EACV,QAAQ,EACR,OAAc,EACd,SAAS,EACV,EAAE,aAAa,GAAG,MAAM,CAOxB"}
@@ -0,0 +1,26 @@
1
+ import { stableStringify } from './serialization.js';
2
+ const FNV_OFFSET = BigInt('0xcbf29ce484222325');
3
+ const FNV_PRIME = BigInt('0x100000001b3');
4
+ const FNV_MOD = BigInt('0x10000000000000000');
5
+ function fnv1a64(value) {
6
+ let hash = FNV_OFFSET;
7
+ for (let i = 0; i < value.length; i++) {
8
+ hash ^= BigInt(value.charCodeAt(i));
9
+ hash = (hash * FNV_PRIME) % FNV_MOD;
10
+ }
11
+ return hash.toString(16);
12
+ }
13
+ function formatSegment(segment) {
14
+ if (!segment)
15
+ return 'query';
16
+ const sanitized = segment.replace(/[^a-zA-Z0-9_-]/g, '-');
17
+ return sanitized.slice(0, 48) || 'query';
18
+ }
19
+ export function computeCacheKey({ namespace, sql, parameters, settings, version = 'v1', tableName }) {
20
+ const serializedParams = stableStringify(parameters);
21
+ const serializedSettings = stableStringify(settings || null);
22
+ const material = `${sql}\n${serializedParams}\n${serializedSettings}`;
23
+ const digest = fnv1a64(material);
24
+ const tableSegment = formatSegment(tableName);
25
+ return `hq:${version}:${namespace}:${tableSegment}:${digest}`;
26
+ }
@@ -0,0 +1,31 @@
1
+ import type { CacheEntry, CacheProvider } from '../types.js';
2
+ export interface MemoryLRUCacheOptions {
3
+ maxEntries?: number;
4
+ maxBytes?: number;
5
+ cleanupIntervalMs?: number;
6
+ }
7
+ export declare class MemoryCacheProvider implements CacheProvider {
8
+ private entries;
9
+ private tagIndex;
10
+ private currentBytes;
11
+ private maxEntries;
12
+ private maxBytes;
13
+ private cleanupIntervalMs;
14
+ private cleanupTimer?;
15
+ constructor(options?: MemoryLRUCacheOptions);
16
+ dispose(): void;
17
+ get(key: string): Promise<CacheEntry | null>;
18
+ set(key: string, entry: CacheEntry): Promise<void>;
19
+ delete(key: string): Promise<void>;
20
+ deleteByTag(namespace: string, tag: string): Promise<void>;
21
+ clearNamespace(namespace: string): Promise<void>;
22
+ private touch;
23
+ private enforceLimits;
24
+ private cleanup;
25
+ private indexTags;
26
+ private unindexTags;
27
+ private getTagIndexKey;
28
+ private cleanupTagIndex;
29
+ }
30
+ export { MemoryCacheProvider as MemoryLRUCacheProvider };
31
+ //# sourceMappingURL=memory-lru.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-lru.d.ts","sourceRoot":"","sources":["../../../../src/core/cache/providers/memory-lru.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE7D,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAgBD,qBAAa,mBAAoB,YAAW,aAAa;IACvD,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,YAAY,CAAC,CAAiC;gBAE1C,OAAO,GAAE,qBAA0B;IAa/C,OAAO,IAAI,IAAI;IAOT,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAY5C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAYlD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlC,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU1D,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtD,OAAO,CAAC,KAAK;IAKb,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,eAAe;CAYxB;AAED,OAAO,EAAE,mBAAmB,IAAI,sBAAsB,EAAE,CAAC"}
@@ -0,0 +1,156 @@
1
+ function extractNamespace(key) {
2
+ const parts = key.split(':');
3
+ if (parts.length < 4) {
4
+ return 'default';
5
+ }
6
+ if (parts.length === 4) {
7
+ return parts[2];
8
+ }
9
+ // Namespace may contain additional ':' (e.g., protocol prefixes); rejoin everything
10
+ // between the version segment and the trailing table/digest tokens.
11
+ const namespace = parts.slice(2, -2).join(':');
12
+ return namespace || 'default';
13
+ }
14
+ export class MemoryCacheProvider {
15
+ entries = new Map();
16
+ tagIndex = new Map();
17
+ currentBytes = 0;
18
+ maxEntries;
19
+ maxBytes;
20
+ cleanupIntervalMs;
21
+ cleanupTimer;
22
+ constructor(options = {}) {
23
+ this.maxEntries = options.maxEntries ?? 1000;
24
+ this.maxBytes = options.maxBytes ?? 50 * 1024 * 1024;
25
+ this.cleanupIntervalMs = options.cleanupIntervalMs ?? 30_000;
26
+ if (this.cleanupIntervalMs > 0 && typeof setInterval !== 'undefined') {
27
+ this.cleanupTimer = setInterval(() => this.cleanup(), this.cleanupIntervalMs);
28
+ const timer = this.cleanupTimer;
29
+ if (typeof timer.unref === 'function') {
30
+ timer.unref();
31
+ }
32
+ }
33
+ }
34
+ dispose() {
35
+ if (this.cleanupTimer) {
36
+ clearInterval(this.cleanupTimer);
37
+ this.cleanupTimer = undefined;
38
+ }
39
+ }
40
+ async get(key) {
41
+ this.cleanup();
42
+ const entry = this.entries.get(key);
43
+ if (!entry)
44
+ return null;
45
+ if (entry.cacheTimeMs > 0 && Date.now() > entry.createdAt + entry.cacheTimeMs) {
46
+ await this.delete(key);
47
+ return null;
48
+ }
49
+ this.touch(key, entry);
50
+ return entry;
51
+ }
52
+ async set(key, entry) {
53
+ const existing = this.entries.get(key);
54
+ if (existing) {
55
+ this.currentBytes -= existing.byteSize || 0;
56
+ this.unindexTags(key, existing.tags);
57
+ }
58
+ this.entries.set(key, entry);
59
+ this.currentBytes += entry.byteSize || 0;
60
+ this.indexTags(key, entry.tags);
61
+ this.enforceLimits();
62
+ }
63
+ async delete(key) {
64
+ const entry = this.entries.get(key);
65
+ if (!entry)
66
+ return;
67
+ this.entries.delete(key);
68
+ this.currentBytes -= entry.byteSize || 0;
69
+ this.unindexTags(key, entry.tags);
70
+ }
71
+ async deleteByTag(namespace, tag) {
72
+ const indexKey = this.getTagIndexKey(namespace, tag);
73
+ const keys = this.tagIndex.get(indexKey);
74
+ if (!keys)
75
+ return;
76
+ for (const key of keys) {
77
+ await this.delete(key);
78
+ }
79
+ this.tagIndex.delete(indexKey);
80
+ }
81
+ async clearNamespace(namespace) {
82
+ const keys = Array.from(this.entries.keys());
83
+ await Promise.all(keys.map(key => {
84
+ if (extractNamespace(key) === namespace) {
85
+ return this.delete(key);
86
+ }
87
+ return Promise.resolve();
88
+ }));
89
+ }
90
+ touch(key, entry) {
91
+ this.entries.delete(key);
92
+ this.entries.set(key, entry);
93
+ }
94
+ enforceLimits() {
95
+ while (this.entries.size > this.maxEntries || this.currentBytes > this.maxBytes) {
96
+ const oldestKey = this.entries.keys().next().value;
97
+ if (!oldestKey)
98
+ break;
99
+ this.delete(oldestKey);
100
+ }
101
+ }
102
+ cleanup() {
103
+ if (!this.entries.size)
104
+ return;
105
+ const now = Date.now();
106
+ for (const [key, entry] of this.entries) {
107
+ if (entry.cacheTimeMs > 0 && now > entry.createdAt + entry.cacheTimeMs) {
108
+ this.delete(key);
109
+ }
110
+ }
111
+ this.cleanupTagIndex();
112
+ }
113
+ indexTags(key, tags) {
114
+ if (!tags || !tags.length)
115
+ return;
116
+ const namespace = extractNamespace(key);
117
+ tags.forEach(tag => {
118
+ const indexKey = this.getTagIndexKey(namespace, tag);
119
+ if (!this.tagIndex.has(indexKey)) {
120
+ this.tagIndex.set(indexKey, new Set());
121
+ }
122
+ this.tagIndex.get(indexKey).add(key);
123
+ });
124
+ }
125
+ unindexTags(key, tags) {
126
+ if (!tags || !tags.length)
127
+ return;
128
+ const namespace = extractNamespace(key);
129
+ tags.forEach(tag => {
130
+ const indexKey = this.getTagIndexKey(namespace, tag);
131
+ const bucket = this.tagIndex.get(indexKey);
132
+ if (!bucket)
133
+ return;
134
+ bucket.delete(key);
135
+ if (!bucket.size) {
136
+ this.tagIndex.delete(indexKey);
137
+ }
138
+ });
139
+ }
140
+ getTagIndexKey(namespace, tag) {
141
+ return `${namespace}:${tag}`;
142
+ }
143
+ cleanupTagIndex() {
144
+ for (const [indexKey, keys] of this.tagIndex) {
145
+ for (const key of keys) {
146
+ if (!this.entries.has(key)) {
147
+ keys.delete(key);
148
+ }
149
+ }
150
+ if (!keys.size) {
151
+ this.tagIndex.delete(indexKey);
152
+ }
153
+ }
154
+ }
155
+ }
156
+ export { MemoryCacheProvider as MemoryLRUCacheProvider };
@@ -0,0 +1,7 @@
1
+ import type { CacheEntry, CacheProvider } from '../types.js';
2
+ export declare class NoopCacheProvider implements CacheProvider {
3
+ get(_key: string): Promise<CacheEntry | null>;
4
+ set(_key: string, _entry: CacheEntry): Promise<void>;
5
+ delete(_key: string): Promise<void>;
6
+ }
7
+ //# sourceMappingURL=noop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noop.d.ts","sourceRoot":"","sources":["../../../../src/core/cache/providers/noop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE7D,qBAAa,iBAAkB,YAAW,aAAa;IAC/C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAI7C,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG1C"}
@@ -0,0 +1,11 @@
1
+ export class NoopCacheProvider {
2
+ async get(_key) {
3
+ return null;
4
+ }
5
+ async set(_key, _entry) {
6
+ return;
7
+ }
8
+ async delete(_key) {
9
+ return;
10
+ }
11
+ }
@@ -0,0 +1,30 @@
1
+ import type { CacheConfig, CacheOptions, CacheProvider, CacheSerializeFn, CacheDeserializeFn, CacheStats } from './types.js';
2
+ export interface QueryRuntimeContext {
3
+ provider?: CacheProvider;
4
+ defaults: CacheOptions;
5
+ namespace: string;
6
+ versionTag: string;
7
+ serialize: CacheSerializeFn;
8
+ deserialize: CacheDeserializeFn;
9
+ inFlight: Map<string, Promise<unknown>>;
10
+ stats: CacheStats;
11
+ parsedValues: Map<string, ParsedValueEntry>;
12
+ }
13
+ export interface ParsedValueEntry {
14
+ createdAt: number;
15
+ rows: unknown;
16
+ tags?: string[];
17
+ }
18
+ export declare function createCacheStats(): CacheStats;
19
+ export declare function mergeCacheOptions(...candidates: Array<CacheOptions | undefined>): CacheOptions;
20
+ export interface CacheRuntimeConfig {
21
+ namespace: string;
22
+ versionTag: string;
23
+ provider?: CacheProvider;
24
+ defaults: CacheOptions;
25
+ serialize: CacheSerializeFn;
26
+ deserialize: CacheDeserializeFn;
27
+ }
28
+ export declare function buildRuntimeContext(config: CacheRuntimeConfig): QueryRuntimeContext;
29
+ export declare function resolveCacheConfig(config: CacheConfig | undefined, fallbackNamespace: string): CacheRuntimeConfig;
30
+ //# sourceMappingURL=runtime-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-context.d.ts","sourceRoot":"","sources":["../../../src/core/cache/runtime-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACX,MAAM,YAAY,CAAC;AAWpB,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,WAAW,EAAE,kBAAkB,CAAC;IAChC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IACxC,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAgB,gBAAgB,IAAI,UAAU,CAE7C;AAQD,wBAAgB,iBAAiB,CAAC,GAAG,UAAU,EAAE,KAAK,CAAC,YAAY,GAAG,SAAS,CAAC,GAAG,YAAY,CAe9F;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,WAAW,EAAE,kBAAkB,CAAC;CACjC;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,kBAAkB,GAAG,mBAAmB,CAYnF;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,EAAE,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,CAUjH"}
@@ -0,0 +1,58 @@
1
+ import { defaultSerialize, defaultDeserialize } from './serialization.js';
2
+ const DEFAULT_CACHE_OPTIONS = {
3
+ mode: 'no-store',
4
+ ttlMs: 0,
5
+ staleTtlMs: 0,
6
+ staleIfError: false,
7
+ dedupe: true
8
+ };
9
+ export function createCacheStats() {
10
+ return { hits: 0, misses: 0, staleHits: 0, revalidations: 0 };
11
+ }
12
+ function uniqueTags(left, right) {
13
+ const combined = [...(left || []), ...(right || [])];
14
+ if (!combined.length)
15
+ return undefined;
16
+ return Array.from(new Set(combined));
17
+ }
18
+ export function mergeCacheOptions(...candidates) {
19
+ return candidates.reduce((acc, candidate) => {
20
+ if (!candidate)
21
+ return acc;
22
+ const next = { ...acc };
23
+ for (const [key, value] of Object.entries(candidate)) {
24
+ if (key === 'tags') {
25
+ next.tags = uniqueTags(next.tags, value);
26
+ continue;
27
+ }
28
+ if (value !== undefined) {
29
+ next[key] = value;
30
+ }
31
+ }
32
+ return next;
33
+ }, { ...DEFAULT_CACHE_OPTIONS });
34
+ }
35
+ export function buildRuntimeContext(config) {
36
+ return {
37
+ provider: config.provider,
38
+ defaults: { ...config.defaults },
39
+ namespace: config.namespace,
40
+ versionTag: config.versionTag,
41
+ serialize: config.serialize,
42
+ deserialize: config.deserialize,
43
+ inFlight: new Map(),
44
+ stats: createCacheStats(),
45
+ parsedValues: new Map()
46
+ };
47
+ }
48
+ export function resolveCacheConfig(config, fallbackNamespace) {
49
+ const defaults = mergeCacheOptions(config);
50
+ return {
51
+ namespace: config?.namespace || fallbackNamespace,
52
+ versionTag: config?.versionTag || 'v1',
53
+ provider: config?.provider,
54
+ defaults,
55
+ serialize: config?.serialize || ((value) => defaultSerialize(value)),
56
+ deserialize: config?.deserialize || ((raw) => defaultDeserialize(raw))
57
+ };
58
+ }
@@ -0,0 +1,6 @@
1
+ import type { CacheSerializationResult } from './types.js';
2
+ export declare function stableStringify(value: unknown): string;
3
+ export declare function defaultSerialize(value: unknown): CacheSerializationResult;
4
+ export declare function defaultDeserialize<T = unknown>(raw: string | Uint8Array): T;
5
+ export declare function estimateByteSize(value: unknown): number;
6
+ //# sourceMappingURL=serialization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialization.d.ts","sourceRoot":"","sources":["../../../src/core/cache/serialization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAiH3D,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEtD;AAyDD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,wBAAwB,CAOzE;AAED,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,CAAC,CAQ3E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAMvD"}