@getstrata/core 0.5.40 → 0.5.41

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.
@@ -20,4 +20,5 @@ declare class SimpleCache {
20
20
  private pruneExpired;
21
21
  private evictOverflow;
22
22
  }
23
+ export { SimpleCache };
23
24
  export default SimpleCache;
@@ -13,4 +13,5 @@ declare class SimpleCacheStore implements CacheStore {
13
13
  clear(): Promise<void>;
14
14
  size(): Promise<number>;
15
15
  }
16
+ export { SimpleCacheStore };
16
17
  export default SimpleCacheStore;
@@ -13,4 +13,5 @@ declare class FailedJobService {
13
13
  delete(id: number): Promise<void>;
14
14
  flush(): Promise<number>;
15
15
  }
16
+ export { FailedJobService };
16
17
  export default FailedJobService;
@@ -0,0 +1,178 @@
1
+ // @bun
2
+ // ../../src/core/cache/simpleCache.ts
3
+ class SimpleCache {
4
+ ttlMs;
5
+ maxEntries;
6
+ cache = new Map;
7
+ inflight = new Map;
8
+ tagIndex = new Map;
9
+ keyTags = new Map;
10
+ constructor(ttlMs = 3600000, maxEntries = 100) {
11
+ this.ttlMs = ttlMs;
12
+ this.maxEntries = maxEntries;
13
+ if (!Number.isFinite(ttlMs) || ttlMs < 0) {
14
+ throw new RangeError("ttlMs must be a non-negative number.");
15
+ }
16
+ if (!Number.isInteger(maxEntries) || maxEntries < 1) {
17
+ throw new RangeError("maxEntries must be a positive integer.");
18
+ }
19
+ }
20
+ get(key) {
21
+ return this.getFreshEntry(key)?.value;
22
+ }
23
+ set(key, value, ttlMs) {
24
+ const now = Date.now();
25
+ const resolvedTtlMs = ttlMs ?? this.ttlMs;
26
+ this.cache.set(key, {
27
+ value,
28
+ expiresAt: now + resolvedTtlMs,
29
+ lastAccessedAt: now
30
+ });
31
+ this.evictOverflow();
32
+ }
33
+ async getOrSet(key, loader, ttlMs) {
34
+ this.pruneExpired();
35
+ const cachedEntry = this.getFreshEntry(key);
36
+ if (cachedEntry) {
37
+ return cachedEntry.value;
38
+ }
39
+ const inflightRequest = this.inflight.get(key);
40
+ if (inflightRequest) {
41
+ return inflightRequest;
42
+ }
43
+ const pendingRequest = loader().then((value) => {
44
+ this.set(key, value, ttlMs);
45
+ return value;
46
+ }).finally(() => {
47
+ this.inflight.delete(key);
48
+ });
49
+ this.inflight.set(key, pendingRequest);
50
+ return pendingRequest;
51
+ }
52
+ attachTags(key, tags) {
53
+ if (tags.length === 0) {
54
+ return;
55
+ }
56
+ let tagsForKey = this.keyTags.get(key);
57
+ if (!tagsForKey) {
58
+ tagsForKey = new Set;
59
+ this.keyTags.set(key, tagsForKey);
60
+ }
61
+ for (const tag of tags) {
62
+ tagsForKey.add(tag);
63
+ let keysForTag = this.tagIndex.get(tag);
64
+ if (!keysForTag) {
65
+ keysForTag = new Set;
66
+ this.tagIndex.set(tag, keysForTag);
67
+ }
68
+ keysForTag.add(key);
69
+ }
70
+ }
71
+ flushTags(tags) {
72
+ const keysToRemove = new Set;
73
+ for (const tag of tags) {
74
+ const keys = this.tagIndex.get(tag);
75
+ if (!keys) {
76
+ continue;
77
+ }
78
+ for (const key of keys) {
79
+ keysToRemove.add(key);
80
+ }
81
+ }
82
+ let removed = 0;
83
+ for (const key of keysToRemove) {
84
+ if (this.invalidate(key)) {
85
+ removed += 1;
86
+ }
87
+ }
88
+ for (const tag of tags) {
89
+ this.tagIndex.delete(tag);
90
+ }
91
+ return removed;
92
+ }
93
+ invalidate(key) {
94
+ const removed = this.cache.delete(key);
95
+ if (removed) {
96
+ this.detachKeyFromTags(key);
97
+ }
98
+ return removed;
99
+ }
100
+ invalidateByPrefix(prefix) {
101
+ let removed = 0;
102
+ for (const key of [...this.cache.keys()]) {
103
+ if (key === prefix || key.startsWith(`${prefix}?`)) {
104
+ if (this.invalidate(key)) {
105
+ removed += 1;
106
+ }
107
+ }
108
+ }
109
+ return removed;
110
+ }
111
+ clear() {
112
+ this.cache.clear();
113
+ this.inflight.clear();
114
+ this.tagIndex.clear();
115
+ this.keyTags.clear();
116
+ }
117
+ size() {
118
+ this.pruneExpired();
119
+ return this.cache.size;
120
+ }
121
+ detachKeyFromTags(key) {
122
+ const tags = this.keyTags.get(key);
123
+ if (!tags) {
124
+ return;
125
+ }
126
+ for (const tag of tags) {
127
+ const keys = this.tagIndex.get(tag);
128
+ if (!keys) {
129
+ continue;
130
+ }
131
+ keys.delete(key);
132
+ if (keys.size === 0) {
133
+ this.tagIndex.delete(tag);
134
+ }
135
+ }
136
+ this.keyTags.delete(key);
137
+ }
138
+ getFreshEntry(key) {
139
+ const entry = this.cache.get(key);
140
+ if (!entry) {
141
+ return;
142
+ }
143
+ if (entry.expiresAt <= Date.now()) {
144
+ this.invalidate(key);
145
+ return;
146
+ }
147
+ entry.lastAccessedAt = Date.now();
148
+ return entry;
149
+ }
150
+ pruneExpired() {
151
+ const now = Date.now();
152
+ for (const [key, entry] of this.cache.entries()) {
153
+ if (entry.expiresAt <= now) {
154
+ this.invalidate(key);
155
+ }
156
+ }
157
+ }
158
+ evictOverflow() {
159
+ while (this.cache.size > this.maxEntries) {
160
+ let oldestKey;
161
+ let oldestAccessTime = Number.POSITIVE_INFINITY;
162
+ for (const [key, entry] of this.cache.entries()) {
163
+ if (entry.lastAccessedAt < oldestAccessTime) {
164
+ oldestAccessTime = entry.lastAccessedAt;
165
+ oldestKey = key;
166
+ }
167
+ }
168
+ if (!oldestKey) {
169
+ return;
170
+ }
171
+ this.invalidate(oldestKey);
172
+ }
173
+ }
174
+ }
175
+ var simpleCache_default = SimpleCache;
176
+ export {
177
+ SimpleCache
178
+ };
@@ -0,0 +1,42 @@
1
+ // @bun
2
+ // ../../src/core/cache/simpleCacheStore.ts
3
+ class SimpleCacheStore {
4
+ cache;
5
+ constructor(cache) {
6
+ this.cache = cache;
7
+ }
8
+ get(key) {
9
+ return Promise.resolve(this.cache.get(key));
10
+ }
11
+ set(key, value, ttlMs) {
12
+ this.cache.set(key, value, ttlMs);
13
+ return Promise.resolve();
14
+ }
15
+ getOrSet(key, loader, ttlMs) {
16
+ return this.cache.getOrSet(key, loader, ttlMs);
17
+ }
18
+ attachTags(key, tags) {
19
+ this.cache.attachTags(key, tags);
20
+ return Promise.resolve();
21
+ }
22
+ flushTags(tags) {
23
+ return Promise.resolve(this.cache.flushTags(tags));
24
+ }
25
+ invalidate(key) {
26
+ return Promise.resolve(this.cache.invalidate(key));
27
+ }
28
+ invalidateByPrefix(prefix) {
29
+ return Promise.resolve(this.cache.invalidateByPrefix(prefix));
30
+ }
31
+ clear() {
32
+ this.cache.clear();
33
+ return Promise.resolve();
34
+ }
35
+ size() {
36
+ return Promise.resolve(this.cache.size());
37
+ }
38
+ }
39
+ var simpleCacheStore_default = SimpleCacheStore;
40
+ export {
41
+ SimpleCacheStore
42
+ };
@@ -42,3 +42,6 @@ class FailedJobService {
42
42
  }
43
43
  }
44
44
  var failedJobService_default = FailedJobService;
45
+ export {
46
+ FailedJobService
47
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/core",
3
- "version": "0.5.40",
3
+ "version": "0.5.41",
4
4
  "description": "Strata — Laravel-inspired Bun framework public API",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -110,6 +110,16 @@
110
110
  "import": "./dist/entries/cache/repository.js",
111
111
  "default": "./dist/entries/cache/repository.js"
112
112
  },
113
+ "./cache/simpleCache": {
114
+ "types": "./dist/core/cache/simpleCache.d.ts",
115
+ "import": "./dist/entries/cache/simpleCache.js",
116
+ "default": "./dist/entries/cache/simpleCache.js"
117
+ },
118
+ "./cache/simpleCacheStore": {
119
+ "types": "./dist/core/cache/simpleCacheStore.d.ts",
120
+ "import": "./dist/entries/cache/simpleCacheStore.js",
121
+ "default": "./dist/entries/cache/simpleCacheStore.js"
122
+ },
113
123
  "./config/envSchema": {
114
124
  "types": "./dist/core/config/envSchema.d.ts",
115
125
  "import": "./dist/entries/config/envSchema.js",
@@ -407,7 +417,7 @@
407
417
  "build:bundle": "bun build index.ts --outdir dist --target bun --external bun --external eta",
408
418
  "build:types": "tsc -p tsconfig.types.json",
409
419
  "prepublishOnly": "bun run build && bun ../../scripts/prepare-core-package-publish.ts",
410
- "build:subpaths": "bun build entries/auth/oauth/oidcProvider.ts entries/auth/oauth/providers.ts entries/auth/oauth/samlProvider.ts entries/auth/oauth/types.ts entries/auth/password.ts entries/auth/sessionCookie.ts entries/auth/sessionGuard.ts entries/auth/tokenHash.ts entries/audit/exportAuditLogs.ts entries/cache/tags.ts entries/cache/createCacheStore.ts entries/cache/repository.ts entries/config/envSchema.ts entries/contracts/serviceTokens.ts entries/contracts/container.ts entries/contracts/di.ts entries/crypto/fieldEncryption.ts entries/crypto/mfaSecret.ts entries/database/factory.ts entries/database/seeders.ts entries/database/types.ts entries/errors/http.ts entries/http/bodySizeLimitMiddleware.ts entries/http/cookies.ts entries/http/csrfProtection.ts entries/http/contentNegotiation.ts entries/http/csrfToken.ts entries/http/etag.ts entries/http/flashSession.ts entries/http/parseFormBody.ts entries/http/resources.ts entries/http/securedRouteModelBinding.ts entries/http/webErrorResponse.ts entries/http/webFormRequest.ts entries/jobs/dispatchWebhookJob.ts entries/jobs/invalidateCacheTagsJob.ts entries/lifecycle/gracefulShutdown.ts entries/logging/logger.ts entries/metrics/prometheus.ts entries/openapi/registeredRoute.ts entries/pagination.ts entries/queue.ts entries/queue/createAppQueue.ts entries/queue/failedJobService.ts entries/queue/jobRunner.ts entries/queue/queueMetrics.ts entries/queue/publicQueue.ts entries/queue/types.ts entries/scheduler/schedule.ts entries/security/oauthState.ts entries/security/publicReads.ts entries/security/safeUrl.ts entries/security/stripeWebhook.ts entries/security/tokenExpiry.ts entries/security/totp.ts entries/storage/storage.ts entries/validation/rules.ts entries/view.ts --outdir dist --root . --target bun --external bun --external eta",
420
+ "build:subpaths": "bun build entries/auth/oauth/oidcProvider.ts entries/auth/oauth/providers.ts entries/auth/oauth/samlProvider.ts entries/auth/oauth/types.ts entries/auth/password.ts entries/auth/sessionCookie.ts entries/auth/sessionGuard.ts entries/auth/tokenHash.ts entries/audit/exportAuditLogs.ts entries/cache/tags.ts entries/cache/createCacheStore.ts entries/cache/repository.ts entries/cache/simpleCache.ts entries/cache/simpleCacheStore.ts entries/config/envSchema.ts entries/contracts/serviceTokens.ts entries/contracts/container.ts entries/contracts/di.ts entries/crypto/fieldEncryption.ts entries/crypto/mfaSecret.ts entries/database/factory.ts entries/database/seeders.ts entries/database/types.ts entries/errors/http.ts entries/http/bodySizeLimitMiddleware.ts entries/http/cookies.ts entries/http/csrfProtection.ts entries/http/contentNegotiation.ts entries/http/csrfToken.ts entries/http/etag.ts entries/http/flashSession.ts entries/http/parseFormBody.ts entries/http/resources.ts entries/http/securedRouteModelBinding.ts entries/http/webErrorResponse.ts entries/http/webFormRequest.ts entries/jobs/dispatchWebhookJob.ts entries/jobs/invalidateCacheTagsJob.ts entries/lifecycle/gracefulShutdown.ts entries/logging/logger.ts entries/metrics/prometheus.ts entries/openapi/registeredRoute.ts entries/pagination.ts entries/queue.ts entries/queue/createAppQueue.ts entries/queue/failedJobService.ts entries/queue/jobRunner.ts entries/queue/queueMetrics.ts entries/queue/publicQueue.ts entries/queue/types.ts entries/scheduler/schedule.ts entries/security/oauthState.ts entries/security/publicReads.ts entries/security/safeUrl.ts entries/security/stripeWebhook.ts entries/security/tokenExpiry.ts entries/security/totp.ts entries/storage/storage.ts entries/validation/rules.ts entries/view.ts --outdir dist --root . --target bun --external bun --external eta",
411
421
  "build:shims": "bun ../../scripts/write-core-shared-shims.ts"
412
422
  },
413
423
  "publishConfig": {