@pattern-stack/codegen 0.2.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 +67 -0
- package/README.md +214 -0
- package/dist/runtime/analytics/index.d.ts +6 -0
- package/dist/runtime/analytics/index.js +49 -0
- package/dist/runtime/analytics/index.js.map +1 -0
- package/dist/runtime/analytics/metrics.d.ts +75 -0
- package/dist/runtime/analytics/metrics.js +1 -0
- package/dist/runtime/analytics/metrics.js.map +1 -0
- package/dist/runtime/analytics/packs/crm-entity-measures.d.ts +21 -0
- package/dist/runtime/analytics/packs/crm-entity-measures.js +1 -0
- package/dist/runtime/analytics/packs/crm-entity-measures.js.map +1 -0
- package/dist/runtime/analytics/packs/index.d.ts +3 -0
- package/dist/runtime/analytics/packs/index.js +1 -0
- package/dist/runtime/analytics/packs/index.js.map +1 -0
- package/dist/runtime/analytics/packs/monetary-measures.d.ts +21 -0
- package/dist/runtime/analytics/packs/monetary-measures.js +1 -0
- package/dist/runtime/analytics/packs/monetary-measures.js.map +1 -0
- package/dist/runtime/analytics/specs.d.ts +49 -0
- package/dist/runtime/analytics/specs.js +1 -0
- package/dist/runtime/analytics/specs.js.map +1 -0
- package/dist/runtime/analytics/types.d.ts +85 -0
- package/dist/runtime/analytics/types.js +49 -0
- package/dist/runtime/analytics/types.js.map +1 -0
- package/dist/runtime/base-classes/activity-entity-repository.d.ts +26 -0
- package/dist/runtime/base-classes/activity-entity-repository.js +195 -0
- package/dist/runtime/base-classes/activity-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/activity-entity-service.d.ts +39 -0
- package/dist/runtime/base-classes/activity-entity-service.js +214 -0
- package/dist/runtime/base-classes/activity-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/base-read-use-cases.d.ts +68 -0
- package/dist/runtime/base-classes/base-read-use-cases.js +32 -0
- package/dist/runtime/base-classes/base-read-use-cases.js.map +1 -0
- package/dist/runtime/base-classes/base-repository.d.ts +99 -0
- package/dist/runtime/base-classes/base-repository.js +160 -0
- package/dist/runtime/base-classes/base-repository.js.map +1 -0
- package/dist/runtime/base-classes/base-service.d.ts +98 -0
- package/dist/runtime/base-classes/base-service.js +186 -0
- package/dist/runtime/base-classes/base-service.js.map +1 -0
- package/dist/runtime/base-classes/index.d.ts +18 -0
- package/dist/runtime/base-classes/index.js +617 -0
- package/dist/runtime/base-classes/index.js.map +1 -0
- package/dist/runtime/base-classes/knowledge-entity-repository.d.ts +17 -0
- package/dist/runtime/base-classes/knowledge-entity-repository.js +166 -0
- package/dist/runtime/base-classes/knowledge-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/knowledge-entity-service.d.ts +15 -0
- package/dist/runtime/base-classes/knowledge-entity-service.js +192 -0
- package/dist/runtime/base-classes/knowledge-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/lifecycle-events.d.ts +49 -0
- package/dist/runtime/base-classes/lifecycle-events.js +76 -0
- package/dist/runtime/base-classes/lifecycle-events.js.map +1 -0
- package/dist/runtime/base-classes/metadata-entity-repository.d.ts +27 -0
- package/dist/runtime/base-classes/metadata-entity-repository.js +212 -0
- package/dist/runtime/base-classes/metadata-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/metadata-entity-service.d.ts +39 -0
- package/dist/runtime/base-classes/metadata-entity-service.js +214 -0
- package/dist/runtime/base-classes/metadata-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/synced-entity-repository.d.ts +32 -0
- package/dist/runtime/base-classes/synced-entity-repository.js +203 -0
- package/dist/runtime/base-classes/synced-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/synced-entity-service.d.ts +41 -0
- package/dist/runtime/base-classes/synced-entity-service.js +215 -0
- package/dist/runtime/base-classes/synced-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/with-analytics.d.ts +18 -0
- package/dist/runtime/base-classes/with-analytics.js +11 -0
- package/dist/runtime/base-classes/with-analytics.js.map +1 -0
- package/dist/runtime/constants/tokens.d.ts +29 -0
- package/dist/runtime/constants/tokens.js +8 -0
- package/dist/runtime/constants/tokens.js.map +1 -0
- package/dist/runtime/subsystems/analytics/analytics-query.protocol.d.ts +30 -0
- package/dist/runtime/subsystems/analytics/analytics-query.protocol.js +1 -0
- package/dist/runtime/subsystems/analytics/analytics-query.protocol.js.map +1 -0
- package/dist/runtime/subsystems/analytics/analytics.module.d.ts +34 -0
- package/dist/runtime/subsystems/analytics/analytics.module.js +117 -0
- package/dist/runtime/subsystems/analytics/analytics.module.js.map +1 -0
- package/dist/runtime/subsystems/analytics/analytics.tokens.d.ts +24 -0
- package/dist/runtime/subsystems/analytics/analytics.tokens.js +10 -0
- package/dist/runtime/subsystems/analytics/analytics.tokens.js.map +1 -0
- package/dist/runtime/subsystems/analytics/cube-backend.d.ts +28 -0
- package/dist/runtime/subsystems/analytics/cube-backend.js +71 -0
- package/dist/runtime/subsystems/analytics/cube-backend.js.map +1 -0
- package/dist/runtime/subsystems/analytics/index.d.ts +6 -0
- package/dist/runtime/subsystems/analytics/index.js +122 -0
- package/dist/runtime/subsystems/analytics/index.js.map +1 -0
- package/dist/runtime/subsystems/analytics/noop-backend.d.ts +7 -0
- package/dist/runtime/subsystems/analytics/noop-backend.js +25 -0
- package/dist/runtime/subsystems/analytics/noop-backend.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.drizzle-backend.d.ts +43 -0
- package/dist/runtime/subsystems/cache/cache.drizzle-backend.js +133 -0
- package/dist/runtime/subsystems/cache/cache.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.memory-backend.d.ts +21 -0
- package/dist/runtime/subsystems/cache/cache.memory-backend.js +100 -0
- package/dist/runtime/subsystems/cache/cache.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.module.d.ts +37 -0
- package/dist/runtime/subsystems/cache/cache.module.js +272 -0
- package/dist/runtime/subsystems/cache/cache.module.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.protocol.d.ts +42 -0
- package/dist/runtime/subsystems/cache/cache.protocol.js +1 -0
- package/dist/runtime/subsystems/cache/cache.protocol.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.schema.d.ts +64 -0
- package/dist/runtime/subsystems/cache/cache.schema.js +18 -0
- package/dist/runtime/subsystems/cache/cache.schema.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.tokens.d.ts +18 -0
- package/dist/runtime/subsystems/cache/cache.tokens.js +8 -0
- package/dist/runtime/subsystems/cache/cache.tokens.js.map +1 -0
- package/dist/runtime/subsystems/cache/index.d.ts +11 -0
- package/dist/runtime/subsystems/cache/index.js +277 -0
- package/dist/runtime/subsystems/cache/index.js.map +1 -0
- package/dist/runtime/subsystems/events/domain-events.schema.d.ts +187 -0
- package/dist/runtime/subsystems/events/domain-events.schema.js +32 -0
- package/dist/runtime/subsystems/events/domain-events.schema.js.map +1 -0
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.d.ts +38 -0
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +199 -0
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/events/event-bus.memory-backend.d.ts +18 -0
- package/dist/runtime/subsystems/events/event-bus.memory-backend.js +71 -0
- package/dist/runtime/subsystems/events/event-bus.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/events/event-bus.protocol.d.ts +52 -0
- package/dist/runtime/subsystems/events/event-bus.protocol.js +1 -0
- package/dist/runtime/subsystems/events/event-bus.protocol.js.map +1 -0
- package/dist/runtime/subsystems/events/event-bus.redis-backend.d.ts +95 -0
- package/dist/runtime/subsystems/events/event-bus.redis-backend.js +229 -0
- package/dist/runtime/subsystems/events/event-bus.redis-backend.js.map +1 -0
- package/dist/runtime/subsystems/events/events.module.d.ts +46 -0
- package/dist/runtime/subsystems/events/events.module.js +531 -0
- package/dist/runtime/subsystems/events/events.module.js.map +1 -0
- package/dist/runtime/subsystems/events/events.tokens.d.ts +19 -0
- package/dist/runtime/subsystems/events/events.tokens.js +8 -0
- package/dist/runtime/subsystems/events/events.tokens.js.map +1 -0
- package/dist/runtime/subsystems/events/index.d.ts +12 -0
- package/dist/runtime/subsystems/events/index.js +536 -0
- package/dist/runtime/subsystems/events/index.js.map +1 -0
- package/dist/runtime/subsystems/index.d.ts +24 -0
- package/dist/runtime/subsystems/index.js +1643 -0
- package/dist/runtime/subsystems/index.js.map +1 -0
- package/dist/runtime/subsystems/jobs/index.d.ts +14 -0
- package/dist/runtime/subsystems/jobs/index.js +680 -0
- package/dist/runtime/subsystems/jobs/index.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.bullmq-backend.d.ts +54 -0
- package/dist/runtime/subsystems/jobs/job-queue.bullmq-backend.js +186 -0
- package/dist/runtime/subsystems/jobs/job-queue.bullmq-backend.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.drizzle-backend.d.ts +38 -0
- package/dist/runtime/subsystems/jobs/job-queue.drizzle-backend.js +228 -0
- package/dist/runtime/subsystems/jobs/job-queue.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.memory-backend.d.ts +12 -0
- package/dist/runtime/subsystems/jobs/job-queue.memory-backend.js +44 -0
- package/dist/runtime/subsystems/jobs/job-queue.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.protocol.d.ts +48 -0
- package/dist/runtime/subsystems/jobs/job-queue.protocol.js +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.protocol.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.redis-backend.d.ts +46 -0
- package/dist/runtime/subsystems/jobs/job-queue.redis-backend.js +187 -0
- package/dist/runtime/subsystems/jobs/job-queue.redis-backend.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.schema.d.ts +237 -0
- package/dist/runtime/subsystems/jobs/job-queue.schema.js +44 -0
- package/dist/runtime/subsystems/jobs/job-queue.schema.js.map +1 -0
- package/dist/runtime/subsystems/jobs/jobs.module.d.ts +18 -0
- package/dist/runtime/subsystems/jobs/jobs.module.js +676 -0
- package/dist/runtime/subsystems/jobs/jobs.module.js.map +1 -0
- package/dist/runtime/subsystems/jobs/jobs.tokens.d.ts +13 -0
- package/dist/runtime/subsystems/jobs/jobs.tokens.js +8 -0
- package/dist/runtime/subsystems/jobs/jobs.tokens.js.map +1 -0
- package/dist/runtime/subsystems/storage/index.d.ts +6 -0
- package/dist/runtime/subsystems/storage/index.js +204 -0
- package/dist/runtime/subsystems/storage/index.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.local-backend.d.ts +18 -0
- package/dist/runtime/subsystems/storage/storage.local-backend.js +108 -0
- package/dist/runtime/subsystems/storage/storage.local-backend.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.memory-backend.d.ts +28 -0
- package/dist/runtime/subsystems/storage/storage.memory-backend.js +72 -0
- package/dist/runtime/subsystems/storage/storage.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.module.d.ts +40 -0
- package/dist/runtime/subsystems/storage/storage.module.js +201 -0
- package/dist/runtime/subsystems/storage/storage.module.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.protocol.d.ts +69 -0
- package/dist/runtime/subsystems/storage/storage.protocol.js +1 -0
- package/dist/runtime/subsystems/storage/storage.protocol.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.tokens.d.ts +11 -0
- package/dist/runtime/subsystems/storage/storage.tokens.js +6 -0
- package/dist/runtime/subsystems/storage/storage.tokens.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.utils.d.ts +9 -0
- package/dist/runtime/subsystems/storage/storage.utils.js +18 -0
- package/dist/runtime/subsystems/storage/storage.utils.js.map +1 -0
- package/dist/runtime/types/drizzle.d.ts +17 -0
- package/dist/runtime/types/drizzle.js +1 -0
- package/dist/runtime/types/drizzle.js.map +1 -0
- package/dist/src/cli/index.d.ts +1 -0
- package/dist/src/cli/index.js +7365 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/index.d.ts +2384 -0
- package/dist/src/index.js +2198 -0
- package/dist/src/index.js.map +1 -0
- package/package.json +114 -0
- package/templates/broadcast/new/backend-interface.ejs.t +47 -0
- package/templates/broadcast/new/bridge-listener.ejs.t +67 -0
- package/templates/broadcast/new/channel.ejs.t +77 -0
- package/templates/broadcast/new/index.ejs.t +21 -0
- package/templates/broadcast/new/memory-backend.ejs.t +87 -0
- package/templates/broadcast/new/module.ejs.t +57 -0
- package/templates/broadcast/new/prompt.js +268 -0
- package/templates/broadcast/new/websocket-backend.ejs.t +259 -0
- package/templates/entity/new/backend/application/commands/create.ejs.t +55 -0
- package/templates/entity/new/backend/application/commands/delete.ejs.t +45 -0
- package/templates/entity/new/backend/application/commands/grouped-index.ejs.t +149 -0
- package/templates/entity/new/backend/application/commands/index.ejs.t +15 -0
- package/templates/entity/new/backend/application/commands/update.ejs.t +58 -0
- package/templates/entity/new/backend/application/queries/declarative-queries.ejs.t +36 -0
- package/templates/entity/new/backend/application/queries/get-by-id.ejs.t +42 -0
- package/templates/entity/new/backend/application/queries/grouped-index.ejs.t +81 -0
- package/templates/entity/new/backend/application/queries/index.ejs.t +14 -0
- package/templates/entity/new/backend/application/queries/list.ejs.t +36 -0
- package/templates/entity/new/backend/application/schemas/_inject-index.ejs.t +7 -0
- package/templates/entity/new/backend/application/schemas/dto.ejs.t +45 -0
- package/templates/entity/new/backend/database/_inject-index.ejs.t +7 -0
- package/templates/entity/new/backend/database/electric-migration.ejs.t +21 -0
- package/templates/entity/new/backend/database/repository.ejs.t +450 -0
- package/templates/entity/new/backend/database/schema.ejs.t +248 -0
- package/templates/entity/new/backend/domain/_inject-index.ejs.t +12 -0
- package/templates/entity/new/backend/domain/entity.ejs.t +108 -0
- package/templates/entity/new/backend/domain/grouped-index.ejs.t +163 -0
- package/templates/entity/new/backend/domain/index.ejs.t +15 -0
- package/templates/entity/new/backend/domain/repository-interface.ejs.t +71 -0
- package/templates/entity/new/backend/modules/core/_ensure-anchor-tokens.ejs.t +10 -0
- package/templates/entity/new/backend/modules/core/_inject-token.ejs.t +7 -0
- package/templates/entity/new/backend/modules/core/module.ejs.t +67 -0
- package/templates/entity/new/backend/modules/trpc/module.ejs.t +67 -0
- package/templates/entity/new/backend/presentation/controller.ejs.t +201 -0
- package/templates/entity/new/clean-lite-ps/controller.ejs.t +37 -0
- package/templates/entity/new/clean-lite-ps/dto/create.ejs.t +17 -0
- package/templates/entity/new/clean-lite-ps/dto/output.ejs.t +25 -0
- package/templates/entity/new/clean-lite-ps/dto/update.ejs.t +11 -0
- package/templates/entity/new/clean-lite-ps/entity.ejs.t +52 -0
- package/templates/entity/new/clean-lite-ps/index.ejs.t +20 -0
- package/templates/entity/new/clean-lite-ps/module.ejs.t +43 -0
- package/templates/entity/new/clean-lite-ps/prompt-extension.js +617 -0
- package/templates/entity/new/clean-lite-ps/repository.ejs.t +62 -0
- package/templates/entity/new/clean-lite-ps/service.ejs.t +34 -0
- package/templates/entity/new/clean-lite-ps/use-cases/declarative-queries.ejs.t +34 -0
- package/templates/entity/new/clean-lite-ps/use-cases/find-by-id.ejs.t +16 -0
- package/templates/entity/new/clean-lite-ps/use-cases/list.ejs.t +16 -0
- package/templates/entity/new/frontend/_inject-entities-entry.ejs.t +7 -0
- package/templates/entity/new/frontend/_inject-entities-import.ejs.t +7 -0
- package/templates/entity/new/frontend/collections/_ensure-anchor-collections.ejs.t +10 -0
- package/templates/entity/new/frontend/collections/_inject-index.ejs.t +9 -0
- package/templates/entity/new/frontend/collections/_inject-schema-import.ejs.t +9 -0
- package/templates/entity/new/frontend/collections/collection.ejs.t +61 -0
- package/templates/entity/new/frontend/collections/collections-base.ejs.t +24 -0
- package/templates/entity/new/frontend/entity/collection.ejs.t +172 -0
- package/templates/entity/new/frontend/entity/combined.ejs.t +474 -0
- package/templates/entity/new/frontend/entity/fields.ejs.t +104 -0
- package/templates/entity/new/frontend/entity/hooks.ejs.t +73 -0
- package/templates/entity/new/frontend/entity/index.ejs.t +21 -0
- package/templates/entity/new/frontend/entity/mutation-hooks.ejs.t +84 -0
- package/templates/entity/new/frontend/entity/mutations.ejs.t +38 -0
- package/templates/entity/new/frontend/entity/types.ejs.t +59 -0
- package/templates/entity/new/frontend/generated/_inject-index-export.ejs.t +7 -0
- package/templates/entity/new/frontend/generated/_inject-index-import.ejs.t +7 -0
- package/templates/entity/new/frontend/generated/_inject-index-registry.ejs.t +7 -0
- package/templates/entity/new/frontend/store/_inject-collection-import.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-collections.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-entity.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-import.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-lookups.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-resolve.ejs.t +10 -0
- package/templates/entity/new/frontend/store/hooks.ejs.t +72 -0
- package/templates/entity/new/frontend/unified-entity.ejs.t +28 -0
- package/templates/entity/new/prompt.js +1421 -0
- package/templates/relationship/new/controller.ejs.t +36 -0
- package/templates/relationship/new/dto/create.ejs.t +41 -0
- package/templates/relationship/new/dto/output.ejs.t +44 -0
- package/templates/relationship/new/dto/update.ejs.t +10 -0
- package/templates/relationship/new/entity.ejs.t +98 -0
- package/templates/relationship/new/index.ejs.t +19 -0
- package/templates/relationship/new/module.ejs.t +35 -0
- package/templates/relationship/new/prompt.js +682 -0
- package/templates/relationship/new/repository.ejs.t +54 -0
- package/templates/relationship/new/service.ejs.t +31 -0
- package/templates/relationship/new/use-cases/declarative-queries.ejs.t +34 -0
- package/templates/relationship/new/use-cases/find-by-id.ejs.t +16 -0
- package/templates/relationship/new/use-cases/list.ejs.t +16 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
12
|
+
|
|
13
|
+
// runtime/subsystems/cache/cache.drizzle-backend.ts
|
|
14
|
+
import { Injectable, Inject, Optional } from "@nestjs/common";
|
|
15
|
+
import { gt, or, like, sql, eq } from "drizzle-orm";
|
|
16
|
+
|
|
17
|
+
// runtime/subsystems/cache/cache.schema.ts
|
|
18
|
+
import { pgTable, text, jsonb, timestamp } from "drizzle-orm/pg-core";
|
|
19
|
+
var cacheEntries = pgTable(
|
|
20
|
+
"cache_entries",
|
|
21
|
+
{
|
|
22
|
+
/** Cache key — primary key, text (not uuid) to support arbitrary key namespacing. */
|
|
23
|
+
key: text("key").primaryKey(),
|
|
24
|
+
/** Cached value serialised as JSONB. */
|
|
25
|
+
value: jsonb("value").notNull(),
|
|
26
|
+
/** NULL means the entry never expires. */
|
|
27
|
+
expiresAt: timestamp("expires_at", { withTimezone: true })
|
|
28
|
+
}
|
|
29
|
+
// Index: add (expires_at) via migration for cleanup queries
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// runtime/constants/tokens.ts
|
|
33
|
+
var DRIZZLE = "DRIZZLE";
|
|
34
|
+
|
|
35
|
+
// runtime/subsystems/cache/cache.tokens.ts
|
|
36
|
+
var CACHE_DEFAULT_TTL = /* @__PURE__ */ Symbol("CACHE_DEFAULT_TTL");
|
|
37
|
+
|
|
38
|
+
// runtime/subsystems/cache/cache.drizzle-backend.ts
|
|
39
|
+
var CLEANUP_INTERVAL_MS = 5 * 60 * 1e3;
|
|
40
|
+
var DrizzleCacheService = class {
|
|
41
|
+
constructor(db, defaultTtl = null) {
|
|
42
|
+
this.db = db;
|
|
43
|
+
this.defaultTtl = defaultTtl;
|
|
44
|
+
}
|
|
45
|
+
db;
|
|
46
|
+
defaultTtl;
|
|
47
|
+
cleanupTimer = null;
|
|
48
|
+
/** In-flight getOrSet promises — keyed by cache key to deduplicate stampedes. */
|
|
49
|
+
inflight = /* @__PURE__ */ new Map();
|
|
50
|
+
async onModuleInit() {
|
|
51
|
+
this.cleanupTimer = setInterval(() => {
|
|
52
|
+
void this.deleteExpired();
|
|
53
|
+
}, CLEANUP_INTERVAL_MS);
|
|
54
|
+
}
|
|
55
|
+
async onModuleDestroy() {
|
|
56
|
+
if (this.cleanupTimer !== null) {
|
|
57
|
+
clearInterval(this.cleanupTimer);
|
|
58
|
+
this.cleanupTimer = null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async get(key) {
|
|
62
|
+
try {
|
|
63
|
+
const rows = await this.db.select().from(cacheEntries).where(
|
|
64
|
+
sql`${cacheEntries.key} = ${key} AND (${cacheEntries.expiresAt} IS NULL OR ${cacheEntries.expiresAt} > now())`
|
|
65
|
+
).limit(1);
|
|
66
|
+
if (rows.length === 0) return null;
|
|
67
|
+
return rows[0].value;
|
|
68
|
+
} catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async set(key, value, ttlSeconds) {
|
|
73
|
+
const effectiveTtl = ttlSeconds ?? this.defaultTtl ?? null;
|
|
74
|
+
const expiresAt = effectiveTtl !== null ? new Date(Date.now() + effectiveTtl * 1e3) : null;
|
|
75
|
+
const jsonValue = value;
|
|
76
|
+
await this.db.insert(cacheEntries).values({ key, value: jsonValue, expiresAt }).onConflictDoUpdate({
|
|
77
|
+
target: cacheEntries.key,
|
|
78
|
+
set: { value: jsonValue, expiresAt }
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async delete(key) {
|
|
82
|
+
await this.db.delete(cacheEntries).where(eq(cacheEntries.key, key));
|
|
83
|
+
}
|
|
84
|
+
async invalidateByPrefix(prefix) {
|
|
85
|
+
const escaped = prefix.replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
86
|
+
const result = await this.db.delete(cacheEntries).where(like(cacheEntries.key, `${escaped}%`)).returning({ key: cacheEntries.key });
|
|
87
|
+
return result.length;
|
|
88
|
+
}
|
|
89
|
+
async has(key) {
|
|
90
|
+
try {
|
|
91
|
+
const result = await this.get(key);
|
|
92
|
+
return result !== null;
|
|
93
|
+
} catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async getOrSet(key, factory, ttlSeconds) {
|
|
98
|
+
const cached = await this.get(key);
|
|
99
|
+
if (cached !== null) return cached;
|
|
100
|
+
const existing = this.inflight.get(key);
|
|
101
|
+
if (existing !== void 0) return existing;
|
|
102
|
+
const promise = factory().then(async (value) => {
|
|
103
|
+
await this.set(key, value, ttlSeconds);
|
|
104
|
+
return value;
|
|
105
|
+
}).finally(() => {
|
|
106
|
+
this.inflight.delete(key);
|
|
107
|
+
});
|
|
108
|
+
this.inflight.set(key, promise);
|
|
109
|
+
return promise;
|
|
110
|
+
}
|
|
111
|
+
/** Remove all expired entries. Called by the cleanup timer. */
|
|
112
|
+
async deleteExpired() {
|
|
113
|
+
try {
|
|
114
|
+
await this.db.delete(cacheEntries).where(
|
|
115
|
+
or(
|
|
116
|
+
gt(sql`now()`, cacheEntries.expiresAt)
|
|
117
|
+
)
|
|
118
|
+
);
|
|
119
|
+
} catch {
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
DrizzleCacheService = __decorateClass([
|
|
124
|
+
Injectable(),
|
|
125
|
+
__decorateParam(0, Inject(DRIZZLE)),
|
|
126
|
+
__decorateParam(1, Optional()),
|
|
127
|
+
__decorateParam(1, Inject(CACHE_DEFAULT_TTL))
|
|
128
|
+
], DrizzleCacheService);
|
|
129
|
+
export {
|
|
130
|
+
CACHE_DEFAULT_TTL,
|
|
131
|
+
DrizzleCacheService
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=cache.drizzle-backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../runtime/subsystems/cache/cache.drizzle-backend.ts","../../../../runtime/subsystems/cache/cache.schema.ts","../../../../runtime/constants/tokens.ts","../../../../runtime/subsystems/cache/cache.tokens.ts"],"sourcesContent":["/**\n * DrizzleCacheService — Postgres-backed ICacheService via Drizzle ORM.\n *\n * Storage: `cache_entries` table with key (text pk), value (jsonb), expiresAt (timestamp).\n * TTL enforcement: reads filter by `expiresAt > now() OR expiresAt IS NULL`.\n * Prefix invalidation: `DELETE WHERE key LIKE 'escaped_prefix%'`.\n *\n * Lifecycle:\n * - OnModuleInit: starts periodic cleanup of expired entries.\n * Uses the Jobs subsystem if available (optional injection); falls back to setInterval.\n * - OnModuleDestroy: clears the setInterval timer if used.\n *\n * Error behavior per ADR-008:\n * - get() / has() return null/false on any error (never throw for reads).\n * - set() / delete() / invalidateByPrefix() throw on failure.\n */\nimport { Injectable, Inject, Optional, type OnModuleInit, type OnModuleDestroy } from '@nestjs/common';\nimport { gt, or, like, sql, eq } from 'drizzle-orm';\nimport type { DrizzleClient } from '../../types/drizzle';\nimport type { ICacheService } from './cache.protocol';\nimport { cacheEntries } from './cache.schema';\nimport { DRIZZLE } from '../../constants/tokens';\nimport { CACHE_DEFAULT_TTL } from './cache.tokens';\n\n// Re-export for backward compatibility\nexport { CACHE_DEFAULT_TTL } from './cache.tokens';\n\n/** Cleanup interval in milliseconds when jobs subsystem is unavailable. */\nconst CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes\n\n@Injectable()\nexport class DrizzleCacheService implements ICacheService, OnModuleInit, OnModuleDestroy {\n private cleanupTimer: ReturnType<typeof setInterval> | null = null;\n /** In-flight getOrSet promises — keyed by cache key to deduplicate stampedes. */\n private readonly inflight = new Map<string, Promise<unknown>>();\n\n constructor(\n @Inject(DRIZZLE) private readonly db: DrizzleClient,\n @Optional() @Inject(CACHE_DEFAULT_TTL) private readonly defaultTtl: number | null = null,\n ) {}\n\n async onModuleInit(): Promise<void> {\n this.cleanupTimer = setInterval(() => {\n void this.deleteExpired();\n }, CLEANUP_INTERVAL_MS);\n }\n\n async onModuleDestroy(): Promise<void> {\n if (this.cleanupTimer !== null) {\n clearInterval(this.cleanupTimer);\n this.cleanupTimer = null;\n }\n }\n\n async get<T = unknown>(key: string): Promise<T | null> {\n try {\n const rows = await this.db\n .select()\n .from(cacheEntries)\n .where(\n sql`${cacheEntries.key} = ${key} AND (${cacheEntries.expiresAt} IS NULL OR ${cacheEntries.expiresAt} > now())`,\n )\n .limit(1);\n\n if (rows.length === 0) return null;\n return rows[0]!.value as T;\n } catch {\n return null;\n }\n }\n\n async set<T = unknown>(key: string, value: T, ttlSeconds?: number): Promise<void> {\n const effectiveTtl = ttlSeconds ?? this.defaultTtl ?? null;\n const expiresAt =\n effectiveTtl !== null\n ? new Date(Date.now() + effectiveTtl * 1000)\n : null;\n\n const jsonValue = value as Parameters<typeof cacheEntries.value.mapFromDriverValue>[0];\n await this.db\n .insert(cacheEntries)\n .values({ key, value: jsonValue, expiresAt })\n .onConflictDoUpdate({\n target: cacheEntries.key,\n set: { value: jsonValue, expiresAt },\n });\n }\n\n async delete(key: string): Promise<void> {\n await this.db.delete(cacheEntries).where(eq(cacheEntries.key, key));\n }\n\n async invalidateByPrefix(prefix: string): Promise<number> {\n // Escape LIKE wildcards to prevent prefix characters from matching unintended entries\n const escaped = prefix.replace(/%/g, '\\\\%').replace(/_/g, '\\\\_');\n const result = await this.db\n .delete(cacheEntries)\n .where(like(cacheEntries.key, `${escaped}%`))\n .returning({ key: cacheEntries.key });\n return result.length;\n }\n\n async has(key: string): Promise<boolean> {\n try {\n const result = await this.get(key);\n return result !== null;\n } catch {\n return false;\n }\n }\n\n async getOrSet<T = unknown>(\n key: string,\n factory: () => Promise<T>,\n ttlSeconds?: number,\n ): Promise<T> {\n // Fast path: cache hit\n const cached = await this.get<T>(key);\n if (cached !== null) return cached;\n\n // Stampede protection: if another call is already computing this key, reuse its promise\n const existing = this.inflight.get(key) as Promise<T> | undefined;\n if (existing !== undefined) return existing;\n\n const promise = factory().then(async (value) => {\n await this.set(key, value, ttlSeconds);\n return value;\n }).finally(() => {\n this.inflight.delete(key);\n });\n\n this.inflight.set(key, promise as Promise<unknown>);\n return promise;\n }\n\n /** Remove all expired entries. Called by the cleanup timer. */\n private async deleteExpired(): Promise<void> {\n try {\n await this.db\n .delete(cacheEntries)\n .where(\n or(\n gt(sql`now()`, cacheEntries.expiresAt),\n ),\n );\n } catch {\n // Cleanup failures are non-fatal — stale rows are filtered at read time\n }\n }\n}\n","/**\n * Drizzle schema for the cache_entries table.\n *\n * This table backs the DrizzleCacheService. TTL is enforced by filtering\n * on expiresAt at read time; a periodic cleanup job removes stale rows.\n *\n * Indexes:\n * - PRIMARY KEY on key (point-lookup)\n * - (expiresAt) for the cleanup query\n */\nimport { pgTable, text, jsonb, timestamp } from 'drizzle-orm/pg-core';\nimport type { InferSelectModel } from 'drizzle-orm';\n\nexport const cacheEntries = pgTable(\n 'cache_entries',\n {\n /** Cache key — primary key, text (not uuid) to support arbitrary key namespacing. */\n key: text('key').primaryKey(),\n /** Cached value serialised as JSONB. */\n value: jsonb('value').notNull(),\n /** NULL means the entry never expires. */\n expiresAt: timestamp('expires_at', { withTimezone: true }),\n },\n // Index: add (expires_at) via migration for cleanup queries\n);\n\nexport type CacheEntry = InferSelectModel<typeof cacheEntries>;\n","/**\n * NestJS injection tokens\n *\n * Used with @Inject() decorator in concrete repository constructors.\n */\n\n/**\n * Injection token for the Drizzle ORM database client.\n *\n * Usage in concrete repositories:\n * ```typescript\n * constructor(@Inject(DRIZZLE) db: DrizzleClient) { super(db); }\n * ```\n */\nexport const DRIZZLE = 'DRIZZLE' as const;\n\n/**\n * Injection token for the event bus (IEventBus).\n *\n * Optional — only resolved when EventsModule.forRoot() is registered.\n * BaseService uses this with @Optional() to emit lifecycle events\n * without requiring the events subsystem to be installed.\n *\n * Usage in services/use cases:\n * ```typescript\n * @Optional() @Inject(EVENT_BUS) eventBus?: IEventBus\n * ```\n */\nexport const EVENT_BUS = 'EVENT_BUS' as const;\n","/**\n * Injection token for the cache service.\n *\n * Usage in use cases:\n * ```typescript\n * constructor(@Inject(CACHE) private readonly cache: ICacheService) {}\n * ```\n *\n * Services may also inject CACHE for reads (get, has) per ADR-003.\n */\nexport const CACHE = Symbol('CACHE');\n\n/**\n * Injection token for the default TTL (in seconds) passed from CacheModule.forRoot().\n * Optional — omit for no-expiry behavior.\n */\nexport const CACHE_DEFAULT_TTL = Symbol('CACHE_DEFAULT_TTL');\n"],"mappings":";;;;;;;;;;;;;AAgBA,SAAS,YAAY,QAAQ,gBAAyD;AACtF,SAAS,IAAI,IAAI,MAAM,KAAK,UAAU;;;ACPtC,SAAS,SAAS,MAAM,OAAO,iBAAiB;AAGzC,IAAM,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA;AAAA,IAEE,KAAK,KAAK,KAAK,EAAE,WAAW;AAAA;AAAA,IAE5B,OAAO,MAAM,OAAO,EAAE,QAAQ;AAAA;AAAA,IAE9B,WAAW,UAAU,cAAc,EAAE,cAAc,KAAK,CAAC;AAAA,EAC3D;AAAA;AAEF;;;ACVO,IAAM,UAAU;;;ACEhB,IAAM,oBAAoB,uBAAO,mBAAmB;;;AHY3D,IAAM,sBAAsB,IAAI,KAAK;AAG9B,IAAM,sBAAN,MAAkF;AAAA,EAKvF,YACoC,IACsB,aAA4B,MACpF;AAFkC;AACsB;AAAA,EACvD;AAAA,EAFiC;AAAA,EACsB;AAAA,EANlD,eAAsD;AAAA;AAAA,EAE7C,WAAW,oBAAI,IAA8B;AAAA,EAO9D,MAAM,eAA8B;AAClC,SAAK,eAAe,YAAY,MAAM;AACpC,WAAK,KAAK,cAAc;AAAA,IAC1B,GAAG,mBAAmB;AAAA,EACxB;AAAA,EAEA,MAAM,kBAAiC;AACrC,QAAI,KAAK,iBAAiB,MAAM;AAC9B,oBAAc,KAAK,YAAY;AAC/B,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAM,IAAiB,KAAgC;AACrD,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,GACrB,OAAO,EACP,KAAK,YAAY,EACjB;AAAA,QACC,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,aAAa,SAAS,eAAe,aAAa,SAAS;AAAA,MACrG,EACC,MAAM,CAAC;AAEV,UAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,aAAO,KAAK,CAAC,EAAG;AAAA,IAClB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,IAAiB,KAAa,OAAU,YAAoC;AAChF,UAAM,eAAe,cAAc,KAAK,cAAc;AACtD,UAAM,YACJ,iBAAiB,OACb,IAAI,KAAK,KAAK,IAAI,IAAI,eAAe,GAAI,IACzC;AAEN,UAAM,YAAY;AAClB,UAAM,KAAK,GACR,OAAO,YAAY,EACnB,OAAO,EAAE,KAAK,OAAO,WAAW,UAAU,CAAC,EAC3C,mBAAmB;AAAA,MAClB,QAAQ,aAAa;AAAA,MACrB,KAAK,EAAE,OAAO,WAAW,UAAU;AAAA,IACrC,CAAC;AAAA,EACL;AAAA,EAEA,MAAM,OAAO,KAA4B;AACvC,UAAM,KAAK,GAAG,OAAO,YAAY,EAAE,MAAM,GAAG,aAAa,KAAK,GAAG,CAAC;AAAA,EACpE;AAAA,EAEA,MAAM,mBAAmB,QAAiC;AAExD,UAAM,UAAU,OAAO,QAAQ,MAAM,KAAK,EAAE,QAAQ,MAAM,KAAK;AAC/D,UAAM,SAAS,MAAM,KAAK,GACvB,OAAO,YAAY,EACnB,MAAM,KAAK,aAAa,KAAK,GAAG,OAAO,GAAG,CAAC,EAC3C,UAAU,EAAE,KAAK,aAAa,IAAI,CAAC;AACtC,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,MAAM,IAAI,KAA+B;AACvC,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,IAAI,GAAG;AACjC,aAAO,WAAW;AAAA,IACpB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,SACJ,KACA,SACA,YACY;AAEZ,UAAM,SAAS,MAAM,KAAK,IAAO,GAAG;AACpC,QAAI,WAAW,KAAM,QAAO;AAG5B,UAAM,WAAW,KAAK,SAAS,IAAI,GAAG;AACtC,QAAI,aAAa,OAAW,QAAO;AAEnC,UAAM,UAAU,QAAQ,EAAE,KAAK,OAAO,UAAU;AAC9C,YAAM,KAAK,IAAI,KAAK,OAAO,UAAU;AACrC,aAAO;AAAA,IACT,CAAC,EAAE,QAAQ,MAAM;AACf,WAAK,SAAS,OAAO,GAAG;AAAA,IAC1B,CAAC;AAED,SAAK,SAAS,IAAI,KAAK,OAA2B;AAClD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAc,gBAA+B;AAC3C,QAAI;AACF,YAAM,KAAK,GACR,OAAO,YAAY,EACnB;AAAA,QACC;AAAA,UACE,GAAG,YAAY,aAAa,SAAS;AAAA,QACvC;AAAA,MACF;AAAA,IACJ,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAtHa,sBAAN;AAAA,EADN,WAAW;AAAA,EAOP,0BAAO,OAAO;AAAA,EACd,4BAAS;AAAA,EAAG,0BAAO,iBAAiB;AAAA,GAP5B;","names":[]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ICacheService } from './cache.protocol.js';
|
|
2
|
+
|
|
3
|
+
declare class MemoryCacheService implements ICacheService {
|
|
4
|
+
private readonly defaultTtl;
|
|
5
|
+
private readonly store;
|
|
6
|
+
private readonly timers;
|
|
7
|
+
/** In-flight getOrSet promises — keyed by cache key to deduplicate stampedes. */
|
|
8
|
+
private readonly inflight;
|
|
9
|
+
constructor(defaultTtl?: number | null);
|
|
10
|
+
get<T = unknown>(key: string): Promise<T | null>;
|
|
11
|
+
set<T = unknown>(key: string, value: T, ttlSeconds?: number): Promise<void>;
|
|
12
|
+
delete(key: string): Promise<void>;
|
|
13
|
+
invalidateByPrefix(prefix: string): Promise<number>;
|
|
14
|
+
has(key: string): Promise<boolean>;
|
|
15
|
+
getOrSet<T = unknown>(key: string, factory: () => Promise<T>, ttlSeconds?: number): Promise<T>;
|
|
16
|
+
/** Remove a key from store and cancel its expiry timer. */
|
|
17
|
+
private evict;
|
|
18
|
+
private clearTimer;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { MemoryCacheService };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
12
|
+
|
|
13
|
+
// runtime/subsystems/cache/cache.memory-backend.ts
|
|
14
|
+
import { Injectable, Inject, Optional } from "@nestjs/common";
|
|
15
|
+
|
|
16
|
+
// runtime/subsystems/cache/cache.tokens.ts
|
|
17
|
+
var CACHE_DEFAULT_TTL = /* @__PURE__ */ Symbol("CACHE_DEFAULT_TTL");
|
|
18
|
+
|
|
19
|
+
// runtime/subsystems/cache/cache.memory-backend.ts
|
|
20
|
+
var MemoryCacheService = class {
|
|
21
|
+
constructor(defaultTtl = null) {
|
|
22
|
+
this.defaultTtl = defaultTtl;
|
|
23
|
+
}
|
|
24
|
+
defaultTtl;
|
|
25
|
+
store = /* @__PURE__ */ new Map();
|
|
26
|
+
timers = /* @__PURE__ */ new Map();
|
|
27
|
+
/** In-flight getOrSet promises — keyed by cache key to deduplicate stampedes. */
|
|
28
|
+
inflight = /* @__PURE__ */ new Map();
|
|
29
|
+
async get(key) {
|
|
30
|
+
const record = this.store.get(key);
|
|
31
|
+
if (!record) return null;
|
|
32
|
+
if (record.expiresAt !== null && record.expiresAt <= Date.now()) {
|
|
33
|
+
this.evict(key);
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return record.value;
|
|
37
|
+
}
|
|
38
|
+
async set(key, value, ttlSeconds) {
|
|
39
|
+
const effectiveTtl = ttlSeconds ?? this.defaultTtl ?? null;
|
|
40
|
+
this.clearTimer(key);
|
|
41
|
+
const expiresAt = effectiveTtl !== null ? Date.now() + effectiveTtl * 1e3 : null;
|
|
42
|
+
this.store.set(key, { value, expiresAt });
|
|
43
|
+
if (effectiveTtl !== null) {
|
|
44
|
+
const timer = setTimeout(() => this.evict(key), effectiveTtl * 1e3);
|
|
45
|
+
this.timers.set(key, timer);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async delete(key) {
|
|
49
|
+
this.evict(key);
|
|
50
|
+
}
|
|
51
|
+
async invalidateByPrefix(prefix) {
|
|
52
|
+
let count = 0;
|
|
53
|
+
for (const key of this.store.keys()) {
|
|
54
|
+
if (key.startsWith(prefix)) {
|
|
55
|
+
this.evict(key);
|
|
56
|
+
count++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return count;
|
|
60
|
+
}
|
|
61
|
+
async has(key) {
|
|
62
|
+
const value = await this.get(key);
|
|
63
|
+
return value !== null;
|
|
64
|
+
}
|
|
65
|
+
async getOrSet(key, factory, ttlSeconds) {
|
|
66
|
+
const cached = await this.get(key);
|
|
67
|
+
if (cached !== null) return cached;
|
|
68
|
+
const existing = this.inflight.get(key);
|
|
69
|
+
if (existing !== void 0) return existing;
|
|
70
|
+
const promise = factory().then(async (value) => {
|
|
71
|
+
await this.set(key, value, ttlSeconds);
|
|
72
|
+
return value;
|
|
73
|
+
}).finally(() => {
|
|
74
|
+
this.inflight.delete(key);
|
|
75
|
+
});
|
|
76
|
+
this.inflight.set(key, promise);
|
|
77
|
+
return promise;
|
|
78
|
+
}
|
|
79
|
+
/** Remove a key from store and cancel its expiry timer. */
|
|
80
|
+
evict(key) {
|
|
81
|
+
this.store.delete(key);
|
|
82
|
+
this.clearTimer(key);
|
|
83
|
+
}
|
|
84
|
+
clearTimer(key) {
|
|
85
|
+
const timer = this.timers.get(key);
|
|
86
|
+
if (timer !== void 0) {
|
|
87
|
+
clearTimeout(timer);
|
|
88
|
+
this.timers.delete(key);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
MemoryCacheService = __decorateClass([
|
|
93
|
+
Injectable(),
|
|
94
|
+
__decorateParam(0, Optional()),
|
|
95
|
+
__decorateParam(0, Inject(CACHE_DEFAULT_TTL))
|
|
96
|
+
], MemoryCacheService);
|
|
97
|
+
export {
|
|
98
|
+
MemoryCacheService
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=cache.memory-backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../runtime/subsystems/cache/cache.memory-backend.ts","../../../../runtime/subsystems/cache/cache.tokens.ts"],"sourcesContent":["/**\n * MemoryCacheService — Map-backed ICacheService for tests and development.\n *\n * TTL is enforced via setTimeout — expired entries are deleted from the Map\n * when the timer fires. get() / has() also check the expiry time defensively\n * in case the timer fires late.\n *\n * No lifecycle hooks required — all state is in-process.\n *\n * Error behavior:\n * - get() / has() never throw; they return null/false.\n * - set() / delete() / invalidateByPrefix() throw on failure (consistent with protocol).\n */\nimport { Injectable, Inject, Optional } from '@nestjs/common';\nimport type { ICacheService } from './cache.protocol';\nimport { CACHE_DEFAULT_TTL } from './cache.tokens';\n\ninterface CacheRecord {\n value: unknown;\n expiresAt: number | null; // epoch ms, or null for no expiry\n}\n\n@Injectable()\nexport class MemoryCacheService implements ICacheService {\n private readonly store = new Map<string, CacheRecord>();\n private readonly timers = new Map<string, ReturnType<typeof setTimeout>>();\n /** In-flight getOrSet promises — keyed by cache key to deduplicate stampedes. */\n private readonly inflight = new Map<string, Promise<unknown>>();\n\n constructor(\n @Optional() @Inject(CACHE_DEFAULT_TTL) private readonly defaultTtl: number | null = null,\n ) {}\n\n async get<T = unknown>(key: string): Promise<T | null> {\n const record = this.store.get(key);\n if (!record) return null;\n if (record.expiresAt !== null && record.expiresAt <= Date.now()) {\n this.evict(key);\n return null;\n }\n return record.value as T;\n }\n\n async set<T = unknown>(key: string, value: T, ttlSeconds?: number): Promise<void> {\n const effectiveTtl = ttlSeconds ?? this.defaultTtl ?? null;\n\n // Clear any existing timer for this key\n this.clearTimer(key);\n\n const expiresAt = effectiveTtl !== null ? Date.now() + effectiveTtl * 1000 : null;\n this.store.set(key, { value, expiresAt });\n\n if (effectiveTtl !== null) {\n const timer = setTimeout(() => this.evict(key), effectiveTtl * 1000);\n this.timers.set(key, timer);\n }\n }\n\n async delete(key: string): Promise<void> {\n this.evict(key);\n }\n\n async invalidateByPrefix(prefix: string): Promise<number> {\n let count = 0;\n for (const key of this.store.keys()) {\n if (key.startsWith(prefix)) {\n this.evict(key);\n count++;\n }\n }\n return count;\n }\n\n async has(key: string): Promise<boolean> {\n const value = await this.get(key);\n return value !== null;\n }\n\n async getOrSet<T = unknown>(\n key: string,\n factory: () => Promise<T>,\n ttlSeconds?: number,\n ): Promise<T> {\n // Fast path: cache hit\n const cached = await this.get<T>(key);\n if (cached !== null) return cached;\n\n // Stampede protection: if another call is already computing this key, reuse its promise\n const existing = this.inflight.get(key) as Promise<T> | undefined;\n if (existing !== undefined) return existing;\n\n const promise = factory().then(async (value) => {\n await this.set(key, value, ttlSeconds);\n return value;\n }).finally(() => {\n this.inflight.delete(key);\n });\n\n this.inflight.set(key, promise as Promise<unknown>);\n return promise;\n }\n\n /** Remove a key from store and cancel its expiry timer. */\n private evict(key: string): void {\n this.store.delete(key);\n this.clearTimer(key);\n }\n\n private clearTimer(key: string): void {\n const timer = this.timers.get(key);\n if (timer !== undefined) {\n clearTimeout(timer);\n this.timers.delete(key);\n }\n }\n}\n","/**\n * Injection token for the cache service.\n *\n * Usage in use cases:\n * ```typescript\n * constructor(@Inject(CACHE) private readonly cache: ICacheService) {}\n * ```\n *\n * Services may also inject CACHE for reads (get, has) per ADR-003.\n */\nexport const CACHE = Symbol('CACHE');\n\n/**\n * Injection token for the default TTL (in seconds) passed from CacheModule.forRoot().\n * Optional — omit for no-expiry behavior.\n */\nexport const CACHE_DEFAULT_TTL = Symbol('CACHE_DEFAULT_TTL');\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAS,YAAY,QAAQ,gBAAgB;;;ACGtC,IAAM,oBAAoB,uBAAO,mBAAmB;;;ADOpD,IAAM,qBAAN,MAAkD;AAAA,EAMvD,YAC0D,aAA4B,MACpF;AADwD;AAAA,EACvD;AAAA,EADuD;AAAA,EANzC,QAAQ,oBAAI,IAAyB;AAAA,EACrC,SAAS,oBAAI,IAA2C;AAAA;AAAA,EAExD,WAAW,oBAAI,IAA8B;AAAA,EAM9D,MAAM,IAAiB,KAAgC;AACrD,UAAM,SAAS,KAAK,MAAM,IAAI,GAAG;AACjC,QAAI,CAAC,OAAQ,QAAO;AACpB,QAAI,OAAO,cAAc,QAAQ,OAAO,aAAa,KAAK,IAAI,GAAG;AAC/D,WAAK,MAAM,GAAG;AACd,aAAO;AAAA,IACT;AACA,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,MAAM,IAAiB,KAAa,OAAU,YAAoC;AAChF,UAAM,eAAe,cAAc,KAAK,cAAc;AAGtD,SAAK,WAAW,GAAG;AAEnB,UAAM,YAAY,iBAAiB,OAAO,KAAK,IAAI,IAAI,eAAe,MAAO;AAC7E,SAAK,MAAM,IAAI,KAAK,EAAE,OAAO,UAAU,CAAC;AAExC,QAAI,iBAAiB,MAAM;AACzB,YAAM,QAAQ,WAAW,MAAM,KAAK,MAAM,GAAG,GAAG,eAAe,GAAI;AACnE,WAAK,OAAO,IAAI,KAAK,KAAK;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,KAA4B;AACvC,SAAK,MAAM,GAAG;AAAA,EAChB;AAAA,EAEA,MAAM,mBAAmB,QAAiC;AACxD,QAAI,QAAQ;AACZ,eAAW,OAAO,KAAK,MAAM,KAAK,GAAG;AACnC,UAAI,IAAI,WAAW,MAAM,GAAG;AAC1B,aAAK,MAAM,GAAG;AACd;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,IAAI,KAA+B;AACvC,UAAM,QAAQ,MAAM,KAAK,IAAI,GAAG;AAChC,WAAO,UAAU;AAAA,EACnB;AAAA,EAEA,MAAM,SACJ,KACA,SACA,YACY;AAEZ,UAAM,SAAS,MAAM,KAAK,IAAO,GAAG;AACpC,QAAI,WAAW,KAAM,QAAO;AAG5B,UAAM,WAAW,KAAK,SAAS,IAAI,GAAG;AACtC,QAAI,aAAa,OAAW,QAAO;AAEnC,UAAM,UAAU,QAAQ,EAAE,KAAK,OAAO,UAAU;AAC9C,YAAM,KAAK,IAAI,KAAK,OAAO,UAAU;AACrC,aAAO;AAAA,IACT,CAAC,EAAE,QAAQ,MAAM;AACf,WAAK,SAAS,OAAO,GAAG;AAAA,IAC1B,CAAC;AAED,SAAK,SAAS,IAAI,KAAK,OAA2B;AAClD,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,MAAM,KAAmB;AAC/B,SAAK,MAAM,OAAO,GAAG;AACrB,SAAK,WAAW,GAAG;AAAA,EACrB;AAAA,EAEQ,WAAW,KAAmB;AACpC,UAAM,QAAQ,KAAK,OAAO,IAAI,GAAG;AACjC,QAAI,UAAU,QAAW;AACvB,mBAAa,KAAK;AAClB,WAAK,OAAO,OAAO,GAAG;AAAA,IACxB;AAAA,EACF;AACF;AA5Fa,qBAAN;AAAA,EADN,WAAW;AAAA,EAQP,4BAAS;AAAA,EAAG,0BAAO,iBAAiB;AAAA,GAP5B;","names":[]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { DynamicModule } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CacheModule — DynamicModule factory for the cache subsystem.
|
|
5
|
+
*
|
|
6
|
+
* Usage in AppModule:
|
|
7
|
+
* ```typescript
|
|
8
|
+
* CacheModule.forRoot({ backend: 'drizzle', defaultTtl: 300 })
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* Usage in tests:
|
|
12
|
+
* ```typescript
|
|
13
|
+
* CacheModule.forRoot({ backend: 'memory' })
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* `global: true` means any module that needs ICacheService can inject CACHE
|
|
17
|
+
* directly without importing CacheModule. Register once in AppModule.
|
|
18
|
+
*
|
|
19
|
+
* The drizzle backend requires DRIZZLE to be provided globally (e.g., via DatabaseModule).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
interface CacheModuleOptions {
|
|
23
|
+
backend: 'drizzle' | 'memory';
|
|
24
|
+
/** Default TTL in seconds for entries that don't specify their own TTL. Null = no expiry. */
|
|
25
|
+
defaultTtl?: number;
|
|
26
|
+
}
|
|
27
|
+
interface CacheModuleAsyncOptions {
|
|
28
|
+
useFactory: (...args: unknown[]) => Promise<CacheModuleOptions> | CacheModuleOptions;
|
|
29
|
+
inject?: unknown[];
|
|
30
|
+
imports?: unknown[];
|
|
31
|
+
}
|
|
32
|
+
declare class CacheModule {
|
|
33
|
+
static forRootAsync(asyncOptions: CacheModuleAsyncOptions): DynamicModule;
|
|
34
|
+
static forRoot(options?: CacheModuleOptions): DynamicModule;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { CacheModule, type CacheModuleAsyncOptions, type CacheModuleOptions };
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
12
|
+
|
|
13
|
+
// runtime/subsystems/cache/cache.module.ts
|
|
14
|
+
import { Module } from "@nestjs/common";
|
|
15
|
+
|
|
16
|
+
// runtime/subsystems/cache/cache.tokens.ts
|
|
17
|
+
var CACHE = /* @__PURE__ */ Symbol("CACHE");
|
|
18
|
+
var CACHE_DEFAULT_TTL = /* @__PURE__ */ Symbol("CACHE_DEFAULT_TTL");
|
|
19
|
+
|
|
20
|
+
// runtime/subsystems/cache/cache.drizzle-backend.ts
|
|
21
|
+
import { Injectable, Inject, Optional } from "@nestjs/common";
|
|
22
|
+
import { gt, or, like, sql, eq } from "drizzle-orm";
|
|
23
|
+
|
|
24
|
+
// runtime/subsystems/cache/cache.schema.ts
|
|
25
|
+
import { pgTable, text, jsonb, timestamp } from "drizzle-orm/pg-core";
|
|
26
|
+
var cacheEntries = pgTable(
|
|
27
|
+
"cache_entries",
|
|
28
|
+
{
|
|
29
|
+
/** Cache key — primary key, text (not uuid) to support arbitrary key namespacing. */
|
|
30
|
+
key: text("key").primaryKey(),
|
|
31
|
+
/** Cached value serialised as JSONB. */
|
|
32
|
+
value: jsonb("value").notNull(),
|
|
33
|
+
/** NULL means the entry never expires. */
|
|
34
|
+
expiresAt: timestamp("expires_at", { withTimezone: true })
|
|
35
|
+
}
|
|
36
|
+
// Index: add (expires_at) via migration for cleanup queries
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// runtime/constants/tokens.ts
|
|
40
|
+
var DRIZZLE = "DRIZZLE";
|
|
41
|
+
|
|
42
|
+
// runtime/subsystems/cache/cache.drizzle-backend.ts
|
|
43
|
+
var CLEANUP_INTERVAL_MS = 5 * 60 * 1e3;
|
|
44
|
+
var DrizzleCacheService = class {
|
|
45
|
+
constructor(db, defaultTtl = null) {
|
|
46
|
+
this.db = db;
|
|
47
|
+
this.defaultTtl = defaultTtl;
|
|
48
|
+
}
|
|
49
|
+
db;
|
|
50
|
+
defaultTtl;
|
|
51
|
+
cleanupTimer = null;
|
|
52
|
+
/** In-flight getOrSet promises — keyed by cache key to deduplicate stampedes. */
|
|
53
|
+
inflight = /* @__PURE__ */ new Map();
|
|
54
|
+
async onModuleInit() {
|
|
55
|
+
this.cleanupTimer = setInterval(() => {
|
|
56
|
+
void this.deleteExpired();
|
|
57
|
+
}, CLEANUP_INTERVAL_MS);
|
|
58
|
+
}
|
|
59
|
+
async onModuleDestroy() {
|
|
60
|
+
if (this.cleanupTimer !== null) {
|
|
61
|
+
clearInterval(this.cleanupTimer);
|
|
62
|
+
this.cleanupTimer = null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async get(key) {
|
|
66
|
+
try {
|
|
67
|
+
const rows = await this.db.select().from(cacheEntries).where(
|
|
68
|
+
sql`${cacheEntries.key} = ${key} AND (${cacheEntries.expiresAt} IS NULL OR ${cacheEntries.expiresAt} > now())`
|
|
69
|
+
).limit(1);
|
|
70
|
+
if (rows.length === 0) return null;
|
|
71
|
+
return rows[0].value;
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async set(key, value, ttlSeconds) {
|
|
77
|
+
const effectiveTtl = ttlSeconds ?? this.defaultTtl ?? null;
|
|
78
|
+
const expiresAt = effectiveTtl !== null ? new Date(Date.now() + effectiveTtl * 1e3) : null;
|
|
79
|
+
const jsonValue = value;
|
|
80
|
+
await this.db.insert(cacheEntries).values({ key, value: jsonValue, expiresAt }).onConflictDoUpdate({
|
|
81
|
+
target: cacheEntries.key,
|
|
82
|
+
set: { value: jsonValue, expiresAt }
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async delete(key) {
|
|
86
|
+
await this.db.delete(cacheEntries).where(eq(cacheEntries.key, key));
|
|
87
|
+
}
|
|
88
|
+
async invalidateByPrefix(prefix) {
|
|
89
|
+
const escaped = prefix.replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
90
|
+
const result = await this.db.delete(cacheEntries).where(like(cacheEntries.key, `${escaped}%`)).returning({ key: cacheEntries.key });
|
|
91
|
+
return result.length;
|
|
92
|
+
}
|
|
93
|
+
async has(key) {
|
|
94
|
+
try {
|
|
95
|
+
const result = await this.get(key);
|
|
96
|
+
return result !== null;
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async getOrSet(key, factory, ttlSeconds) {
|
|
102
|
+
const cached = await this.get(key);
|
|
103
|
+
if (cached !== null) return cached;
|
|
104
|
+
const existing = this.inflight.get(key);
|
|
105
|
+
if (existing !== void 0) return existing;
|
|
106
|
+
const promise = factory().then(async (value) => {
|
|
107
|
+
await this.set(key, value, ttlSeconds);
|
|
108
|
+
return value;
|
|
109
|
+
}).finally(() => {
|
|
110
|
+
this.inflight.delete(key);
|
|
111
|
+
});
|
|
112
|
+
this.inflight.set(key, promise);
|
|
113
|
+
return promise;
|
|
114
|
+
}
|
|
115
|
+
/** Remove all expired entries. Called by the cleanup timer. */
|
|
116
|
+
async deleteExpired() {
|
|
117
|
+
try {
|
|
118
|
+
await this.db.delete(cacheEntries).where(
|
|
119
|
+
or(
|
|
120
|
+
gt(sql`now()`, cacheEntries.expiresAt)
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
DrizzleCacheService = __decorateClass([
|
|
128
|
+
Injectable(),
|
|
129
|
+
__decorateParam(0, Inject(DRIZZLE)),
|
|
130
|
+
__decorateParam(1, Optional()),
|
|
131
|
+
__decorateParam(1, Inject(CACHE_DEFAULT_TTL))
|
|
132
|
+
], DrizzleCacheService);
|
|
133
|
+
|
|
134
|
+
// runtime/subsystems/cache/cache.memory-backend.ts
|
|
135
|
+
import { Injectable as Injectable2, Inject as Inject2, Optional as Optional2 } from "@nestjs/common";
|
|
136
|
+
var MemoryCacheService = class {
|
|
137
|
+
constructor(defaultTtl = null) {
|
|
138
|
+
this.defaultTtl = defaultTtl;
|
|
139
|
+
}
|
|
140
|
+
defaultTtl;
|
|
141
|
+
store = /* @__PURE__ */ new Map();
|
|
142
|
+
timers = /* @__PURE__ */ new Map();
|
|
143
|
+
/** In-flight getOrSet promises — keyed by cache key to deduplicate stampedes. */
|
|
144
|
+
inflight = /* @__PURE__ */ new Map();
|
|
145
|
+
async get(key) {
|
|
146
|
+
const record = this.store.get(key);
|
|
147
|
+
if (!record) return null;
|
|
148
|
+
if (record.expiresAt !== null && record.expiresAt <= Date.now()) {
|
|
149
|
+
this.evict(key);
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
return record.value;
|
|
153
|
+
}
|
|
154
|
+
async set(key, value, ttlSeconds) {
|
|
155
|
+
const effectiveTtl = ttlSeconds ?? this.defaultTtl ?? null;
|
|
156
|
+
this.clearTimer(key);
|
|
157
|
+
const expiresAt = effectiveTtl !== null ? Date.now() + effectiveTtl * 1e3 : null;
|
|
158
|
+
this.store.set(key, { value, expiresAt });
|
|
159
|
+
if (effectiveTtl !== null) {
|
|
160
|
+
const timer = setTimeout(() => this.evict(key), effectiveTtl * 1e3);
|
|
161
|
+
this.timers.set(key, timer);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async delete(key) {
|
|
165
|
+
this.evict(key);
|
|
166
|
+
}
|
|
167
|
+
async invalidateByPrefix(prefix) {
|
|
168
|
+
let count = 0;
|
|
169
|
+
for (const key of this.store.keys()) {
|
|
170
|
+
if (key.startsWith(prefix)) {
|
|
171
|
+
this.evict(key);
|
|
172
|
+
count++;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return count;
|
|
176
|
+
}
|
|
177
|
+
async has(key) {
|
|
178
|
+
const value = await this.get(key);
|
|
179
|
+
return value !== null;
|
|
180
|
+
}
|
|
181
|
+
async getOrSet(key, factory, ttlSeconds) {
|
|
182
|
+
const cached = await this.get(key);
|
|
183
|
+
if (cached !== null) return cached;
|
|
184
|
+
const existing = this.inflight.get(key);
|
|
185
|
+
if (existing !== void 0) return existing;
|
|
186
|
+
const promise = factory().then(async (value) => {
|
|
187
|
+
await this.set(key, value, ttlSeconds);
|
|
188
|
+
return value;
|
|
189
|
+
}).finally(() => {
|
|
190
|
+
this.inflight.delete(key);
|
|
191
|
+
});
|
|
192
|
+
this.inflight.set(key, promise);
|
|
193
|
+
return promise;
|
|
194
|
+
}
|
|
195
|
+
/** Remove a key from store and cancel its expiry timer. */
|
|
196
|
+
evict(key) {
|
|
197
|
+
this.store.delete(key);
|
|
198
|
+
this.clearTimer(key);
|
|
199
|
+
}
|
|
200
|
+
clearTimer(key) {
|
|
201
|
+
const timer = this.timers.get(key);
|
|
202
|
+
if (timer !== void 0) {
|
|
203
|
+
clearTimeout(timer);
|
|
204
|
+
this.timers.delete(key);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
MemoryCacheService = __decorateClass([
|
|
209
|
+
Injectable2(),
|
|
210
|
+
__decorateParam(0, Optional2()),
|
|
211
|
+
__decorateParam(0, Inject2(CACHE_DEFAULT_TTL))
|
|
212
|
+
], MemoryCacheService);
|
|
213
|
+
|
|
214
|
+
// runtime/subsystems/cache/cache.module.ts
|
|
215
|
+
var CacheModule = class {
|
|
216
|
+
static forRootAsync(asyncOptions) {
|
|
217
|
+
return {
|
|
218
|
+
module: CacheModule,
|
|
219
|
+
global: true,
|
|
220
|
+
imports: asyncOptions.imports ?? [],
|
|
221
|
+
providers: [
|
|
222
|
+
{
|
|
223
|
+
provide: "CACHE_MODULE_OPTIONS",
|
|
224
|
+
useFactory: asyncOptions.useFactory,
|
|
225
|
+
inject: asyncOptions.inject ?? []
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
provide: CACHE,
|
|
229
|
+
useFactory: (options) => {
|
|
230
|
+
if (options.backend === "drizzle") {
|
|
231
|
+
return new DrizzleCacheService(
|
|
232
|
+
null,
|
|
233
|
+
options.defaultTtl ?? null
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
return new MemoryCacheService(options.defaultTtl ?? null);
|
|
237
|
+
},
|
|
238
|
+
inject: ["CACHE_MODULE_OPTIONS"]
|
|
239
|
+
},
|
|
240
|
+
{ provide: DrizzleCacheService, useExisting: CACHE },
|
|
241
|
+
{ provide: MemoryCacheService, useExisting: CACHE }
|
|
242
|
+
],
|
|
243
|
+
exports: [CACHE]
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
static forRoot(options = { backend: "drizzle" }) {
|
|
247
|
+
const ConcreteClass = options.backend === "drizzle" ? DrizzleCacheService : MemoryCacheService;
|
|
248
|
+
const providers = options.defaultTtl !== void 0 ? [
|
|
249
|
+
// Register the concrete class as the canonical instance
|
|
250
|
+
ConcreteClass,
|
|
251
|
+
{ provide: CACHE_DEFAULT_TTL, useValue: options.defaultTtl },
|
|
252
|
+
// CACHE token points to the same instance — no duplicate
|
|
253
|
+
{ provide: CACHE, useExisting: ConcreteClass }
|
|
254
|
+
] : [
|
|
255
|
+
ConcreteClass,
|
|
256
|
+
{ provide: CACHE, useExisting: ConcreteClass }
|
|
257
|
+
];
|
|
258
|
+
return {
|
|
259
|
+
module: CacheModule,
|
|
260
|
+
global: true,
|
|
261
|
+
providers,
|
|
262
|
+
exports: [CACHE]
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
CacheModule = __decorateClass([
|
|
267
|
+
Module({})
|
|
268
|
+
], CacheModule);
|
|
269
|
+
export {
|
|
270
|
+
CacheModule
|
|
271
|
+
};
|
|
272
|
+
//# sourceMappingURL=cache.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../runtime/subsystems/cache/cache.module.ts","../../../../runtime/subsystems/cache/cache.tokens.ts","../../../../runtime/subsystems/cache/cache.drizzle-backend.ts","../../../../runtime/subsystems/cache/cache.schema.ts","../../../../runtime/constants/tokens.ts","../../../../runtime/subsystems/cache/cache.memory-backend.ts"],"sourcesContent":["/**\n * CacheModule — DynamicModule factory for the cache subsystem.\n *\n * Usage in AppModule:\n * ```typescript\n * CacheModule.forRoot({ backend: 'drizzle', defaultTtl: 300 })\n * ```\n *\n * Usage in tests:\n * ```typescript\n * CacheModule.forRoot({ backend: 'memory' })\n * ```\n *\n * `global: true` means any module that needs ICacheService can inject CACHE\n * directly without importing CacheModule. Register once in AppModule.\n *\n * The drizzle backend requires DRIZZLE to be provided globally (e.g., via DatabaseModule).\n */\nimport { Module, type DynamicModule } from '@nestjs/common';\nimport { CACHE, CACHE_DEFAULT_TTL } from './cache.tokens';\nimport { DrizzleCacheService } from './cache.drizzle-backend';\nimport { MemoryCacheService } from './cache.memory-backend';\n\nexport interface CacheModuleOptions {\n backend: 'drizzle' | 'memory';\n /** Default TTL in seconds for entries that don't specify their own TTL. Null = no expiry. */\n defaultTtl?: number;\n}\n\nexport interface CacheModuleAsyncOptions {\n useFactory: (...args: unknown[]) => Promise<CacheModuleOptions> | CacheModuleOptions;\n inject?: unknown[];\n imports?: unknown[];\n}\n\n@Module({})\nexport class CacheModule {\n static forRootAsync(asyncOptions: CacheModuleAsyncOptions): DynamicModule {\n return {\n module: CacheModule,\n global: true,\n imports: (asyncOptions.imports ?? []) as Parameters<typeof Module>[0]['imports'],\n providers: [\n {\n provide: 'CACHE_MODULE_OPTIONS',\n useFactory: asyncOptions.useFactory,\n inject: (asyncOptions.inject ?? []) as (string | symbol | Function)[],\n },\n {\n provide: CACHE,\n useFactory: (options: CacheModuleOptions) => {\n if (options.backend === 'drizzle') {\n return new DrizzleCacheService(\n null as unknown as Parameters<typeof DrizzleCacheService.prototype.get>[0] extends never ? never : Parameters<typeof DrizzleCacheService['prototype']['get']>[0] extends never ? never : never,\n options.defaultTtl ?? null,\n );\n }\n return new MemoryCacheService(options.defaultTtl ?? null);\n },\n inject: ['CACHE_MODULE_OPTIONS'],\n },\n { provide: DrizzleCacheService, useExisting: CACHE },\n { provide: MemoryCacheService, useExisting: CACHE },\n ],\n exports: [CACHE],\n };\n }\n\n static forRoot(options: CacheModuleOptions = { backend: 'drizzle' }): DynamicModule {\n const ConcreteClass = options.backend === 'drizzle' ? DrizzleCacheService : MemoryCacheService;\n\n const providers = options.defaultTtl !== undefined\n ? [\n // Register the concrete class as the canonical instance\n ConcreteClass,\n { provide: CACHE_DEFAULT_TTL, useValue: options.defaultTtl },\n // CACHE token points to the same instance — no duplicate\n { provide: CACHE, useExisting: ConcreteClass },\n ]\n : [\n ConcreteClass,\n { provide: CACHE, useExisting: ConcreteClass },\n ];\n\n return {\n module: CacheModule,\n global: true,\n providers,\n exports: [CACHE],\n };\n }\n}\n","/**\n * Injection token for the cache service.\n *\n * Usage in use cases:\n * ```typescript\n * constructor(@Inject(CACHE) private readonly cache: ICacheService) {}\n * ```\n *\n * Services may also inject CACHE for reads (get, has) per ADR-003.\n */\nexport const CACHE = Symbol('CACHE');\n\n/**\n * Injection token for the default TTL (in seconds) passed from CacheModule.forRoot().\n * Optional — omit for no-expiry behavior.\n */\nexport const CACHE_DEFAULT_TTL = Symbol('CACHE_DEFAULT_TTL');\n","/**\n * DrizzleCacheService — Postgres-backed ICacheService via Drizzle ORM.\n *\n * Storage: `cache_entries` table with key (text pk), value (jsonb), expiresAt (timestamp).\n * TTL enforcement: reads filter by `expiresAt > now() OR expiresAt IS NULL`.\n * Prefix invalidation: `DELETE WHERE key LIKE 'escaped_prefix%'`.\n *\n * Lifecycle:\n * - OnModuleInit: starts periodic cleanup of expired entries.\n * Uses the Jobs subsystem if available (optional injection); falls back to setInterval.\n * - OnModuleDestroy: clears the setInterval timer if used.\n *\n * Error behavior per ADR-008:\n * - get() / has() return null/false on any error (never throw for reads).\n * - set() / delete() / invalidateByPrefix() throw on failure.\n */\nimport { Injectable, Inject, Optional, type OnModuleInit, type OnModuleDestroy } from '@nestjs/common';\nimport { gt, or, like, sql, eq } from 'drizzle-orm';\nimport type { DrizzleClient } from '../../types/drizzle';\nimport type { ICacheService } from './cache.protocol';\nimport { cacheEntries } from './cache.schema';\nimport { DRIZZLE } from '../../constants/tokens';\nimport { CACHE_DEFAULT_TTL } from './cache.tokens';\n\n// Re-export for backward compatibility\nexport { CACHE_DEFAULT_TTL } from './cache.tokens';\n\n/** Cleanup interval in milliseconds when jobs subsystem is unavailable. */\nconst CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes\n\n@Injectable()\nexport class DrizzleCacheService implements ICacheService, OnModuleInit, OnModuleDestroy {\n private cleanupTimer: ReturnType<typeof setInterval> | null = null;\n /** In-flight getOrSet promises — keyed by cache key to deduplicate stampedes. */\n private readonly inflight = new Map<string, Promise<unknown>>();\n\n constructor(\n @Inject(DRIZZLE) private readonly db: DrizzleClient,\n @Optional() @Inject(CACHE_DEFAULT_TTL) private readonly defaultTtl: number | null = null,\n ) {}\n\n async onModuleInit(): Promise<void> {\n this.cleanupTimer = setInterval(() => {\n void this.deleteExpired();\n }, CLEANUP_INTERVAL_MS);\n }\n\n async onModuleDestroy(): Promise<void> {\n if (this.cleanupTimer !== null) {\n clearInterval(this.cleanupTimer);\n this.cleanupTimer = null;\n }\n }\n\n async get<T = unknown>(key: string): Promise<T | null> {\n try {\n const rows = await this.db\n .select()\n .from(cacheEntries)\n .where(\n sql`${cacheEntries.key} = ${key} AND (${cacheEntries.expiresAt} IS NULL OR ${cacheEntries.expiresAt} > now())`,\n )\n .limit(1);\n\n if (rows.length === 0) return null;\n return rows[0]!.value as T;\n } catch {\n return null;\n }\n }\n\n async set<T = unknown>(key: string, value: T, ttlSeconds?: number): Promise<void> {\n const effectiveTtl = ttlSeconds ?? this.defaultTtl ?? null;\n const expiresAt =\n effectiveTtl !== null\n ? new Date(Date.now() + effectiveTtl * 1000)\n : null;\n\n const jsonValue = value as Parameters<typeof cacheEntries.value.mapFromDriverValue>[0];\n await this.db\n .insert(cacheEntries)\n .values({ key, value: jsonValue, expiresAt })\n .onConflictDoUpdate({\n target: cacheEntries.key,\n set: { value: jsonValue, expiresAt },\n });\n }\n\n async delete(key: string): Promise<void> {\n await this.db.delete(cacheEntries).where(eq(cacheEntries.key, key));\n }\n\n async invalidateByPrefix(prefix: string): Promise<number> {\n // Escape LIKE wildcards to prevent prefix characters from matching unintended entries\n const escaped = prefix.replace(/%/g, '\\\\%').replace(/_/g, '\\\\_');\n const result = await this.db\n .delete(cacheEntries)\n .where(like(cacheEntries.key, `${escaped}%`))\n .returning({ key: cacheEntries.key });\n return result.length;\n }\n\n async has(key: string): Promise<boolean> {\n try {\n const result = await this.get(key);\n return result !== null;\n } catch {\n return false;\n }\n }\n\n async getOrSet<T = unknown>(\n key: string,\n factory: () => Promise<T>,\n ttlSeconds?: number,\n ): Promise<T> {\n // Fast path: cache hit\n const cached = await this.get<T>(key);\n if (cached !== null) return cached;\n\n // Stampede protection: if another call is already computing this key, reuse its promise\n const existing = this.inflight.get(key) as Promise<T> | undefined;\n if (existing !== undefined) return existing;\n\n const promise = factory().then(async (value) => {\n await this.set(key, value, ttlSeconds);\n return value;\n }).finally(() => {\n this.inflight.delete(key);\n });\n\n this.inflight.set(key, promise as Promise<unknown>);\n return promise;\n }\n\n /** Remove all expired entries. Called by the cleanup timer. */\n private async deleteExpired(): Promise<void> {\n try {\n await this.db\n .delete(cacheEntries)\n .where(\n or(\n gt(sql`now()`, cacheEntries.expiresAt),\n ),\n );\n } catch {\n // Cleanup failures are non-fatal — stale rows are filtered at read time\n }\n }\n}\n","/**\n * Drizzle schema for the cache_entries table.\n *\n * This table backs the DrizzleCacheService. TTL is enforced by filtering\n * on expiresAt at read time; a periodic cleanup job removes stale rows.\n *\n * Indexes:\n * - PRIMARY KEY on key (point-lookup)\n * - (expiresAt) for the cleanup query\n */\nimport { pgTable, text, jsonb, timestamp } from 'drizzle-orm/pg-core';\nimport type { InferSelectModel } from 'drizzle-orm';\n\nexport const cacheEntries = pgTable(\n 'cache_entries',\n {\n /** Cache key — primary key, text (not uuid) to support arbitrary key namespacing. */\n key: text('key').primaryKey(),\n /** Cached value serialised as JSONB. */\n value: jsonb('value').notNull(),\n /** NULL means the entry never expires. */\n expiresAt: timestamp('expires_at', { withTimezone: true }),\n },\n // Index: add (expires_at) via migration for cleanup queries\n);\n\nexport type CacheEntry = InferSelectModel<typeof cacheEntries>;\n","/**\n * NestJS injection tokens\n *\n * Used with @Inject() decorator in concrete repository constructors.\n */\n\n/**\n * Injection token for the Drizzle ORM database client.\n *\n * Usage in concrete repositories:\n * ```typescript\n * constructor(@Inject(DRIZZLE) db: DrizzleClient) { super(db); }\n * ```\n */\nexport const DRIZZLE = 'DRIZZLE' as const;\n\n/**\n * Injection token for the event bus (IEventBus).\n *\n * Optional — only resolved when EventsModule.forRoot() is registered.\n * BaseService uses this with @Optional() to emit lifecycle events\n * without requiring the events subsystem to be installed.\n *\n * Usage in services/use cases:\n * ```typescript\n * @Optional() @Inject(EVENT_BUS) eventBus?: IEventBus\n * ```\n */\nexport const EVENT_BUS = 'EVENT_BUS' as const;\n","/**\n * MemoryCacheService — Map-backed ICacheService for tests and development.\n *\n * TTL is enforced via setTimeout — expired entries are deleted from the Map\n * when the timer fires. get() / has() also check the expiry time defensively\n * in case the timer fires late.\n *\n * No lifecycle hooks required — all state is in-process.\n *\n * Error behavior:\n * - get() / has() never throw; they return null/false.\n * - set() / delete() / invalidateByPrefix() throw on failure (consistent with protocol).\n */\nimport { Injectable, Inject, Optional } from '@nestjs/common';\nimport type { ICacheService } from './cache.protocol';\nimport { CACHE_DEFAULT_TTL } from './cache.tokens';\n\ninterface CacheRecord {\n value: unknown;\n expiresAt: number | null; // epoch ms, or null for no expiry\n}\n\n@Injectable()\nexport class MemoryCacheService implements ICacheService {\n private readonly store = new Map<string, CacheRecord>();\n private readonly timers = new Map<string, ReturnType<typeof setTimeout>>();\n /** In-flight getOrSet promises — keyed by cache key to deduplicate stampedes. */\n private readonly inflight = new Map<string, Promise<unknown>>();\n\n constructor(\n @Optional() @Inject(CACHE_DEFAULT_TTL) private readonly defaultTtl: number | null = null,\n ) {}\n\n async get<T = unknown>(key: string): Promise<T | null> {\n const record = this.store.get(key);\n if (!record) return null;\n if (record.expiresAt !== null && record.expiresAt <= Date.now()) {\n this.evict(key);\n return null;\n }\n return record.value as T;\n }\n\n async set<T = unknown>(key: string, value: T, ttlSeconds?: number): Promise<void> {\n const effectiveTtl = ttlSeconds ?? this.defaultTtl ?? null;\n\n // Clear any existing timer for this key\n this.clearTimer(key);\n\n const expiresAt = effectiveTtl !== null ? Date.now() + effectiveTtl * 1000 : null;\n this.store.set(key, { value, expiresAt });\n\n if (effectiveTtl !== null) {\n const timer = setTimeout(() => this.evict(key), effectiveTtl * 1000);\n this.timers.set(key, timer);\n }\n }\n\n async delete(key: string): Promise<void> {\n this.evict(key);\n }\n\n async invalidateByPrefix(prefix: string): Promise<number> {\n let count = 0;\n for (const key of this.store.keys()) {\n if (key.startsWith(prefix)) {\n this.evict(key);\n count++;\n }\n }\n return count;\n }\n\n async has(key: string): Promise<boolean> {\n const value = await this.get(key);\n return value !== null;\n }\n\n async getOrSet<T = unknown>(\n key: string,\n factory: () => Promise<T>,\n ttlSeconds?: number,\n ): Promise<T> {\n // Fast path: cache hit\n const cached = await this.get<T>(key);\n if (cached !== null) return cached;\n\n // Stampede protection: if another call is already computing this key, reuse its promise\n const existing = this.inflight.get(key) as Promise<T> | undefined;\n if (existing !== undefined) return existing;\n\n const promise = factory().then(async (value) => {\n await this.set(key, value, ttlSeconds);\n return value;\n }).finally(() => {\n this.inflight.delete(key);\n });\n\n this.inflight.set(key, promise as Promise<unknown>);\n return promise;\n }\n\n /** Remove a key from store and cancel its expiry timer. */\n private evict(key: string): void {\n this.store.delete(key);\n this.clearTimer(key);\n }\n\n private clearTimer(key: string): void {\n const timer = this.timers.get(key);\n if (timer !== undefined) {\n clearTimeout(timer);\n this.timers.delete(key);\n }\n }\n}\n"],"mappings":"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","names":["Injectable","Inject","Optional","Injectable","Optional","Inject"]}
|