@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,49 @@
|
|
|
1
|
+
// runtime/analytics/types.ts
|
|
2
|
+
var AggregationType = /* @__PURE__ */ ((AggregationType2) => {
|
|
3
|
+
AggregationType2["SUM"] = "sum";
|
|
4
|
+
AggregationType2["MIN"] = "min";
|
|
5
|
+
AggregationType2["MAX"] = "max";
|
|
6
|
+
AggregationType2["COUNT"] = "count";
|
|
7
|
+
AggregationType2["COUNT_DISTINCT"] = "count_distinct";
|
|
8
|
+
AggregationType2["AVERAGE"] = "average";
|
|
9
|
+
AggregationType2["MEDIAN"] = "median";
|
|
10
|
+
AggregationType2["PERCENTILE"] = "percentile";
|
|
11
|
+
AggregationType2["SUM_BOOLEAN"] = "sum_boolean";
|
|
12
|
+
return AggregationType2;
|
|
13
|
+
})(AggregationType || {});
|
|
14
|
+
var DimensionType = /* @__PURE__ */ ((DimensionType2) => {
|
|
15
|
+
DimensionType2["CATEGORICAL"] = "categorical";
|
|
16
|
+
DimensionType2["TIME"] = "time";
|
|
17
|
+
return DimensionType2;
|
|
18
|
+
})(DimensionType || {});
|
|
19
|
+
var EntityType = /* @__PURE__ */ ((EntityType2) => {
|
|
20
|
+
EntityType2["PRIMARY"] = "primary";
|
|
21
|
+
EntityType2["UNIQUE"] = "unique";
|
|
22
|
+
EntityType2["FOREIGN"] = "foreign";
|
|
23
|
+
EntityType2["NATURAL"] = "natural";
|
|
24
|
+
return EntityType2;
|
|
25
|
+
})(EntityType || {});
|
|
26
|
+
var MetricType = /* @__PURE__ */ ((MetricType2) => {
|
|
27
|
+
MetricType2["SIMPLE"] = "simple";
|
|
28
|
+
MetricType2["DERIVED"] = "derived";
|
|
29
|
+
MetricType2["RATIO"] = "ratio";
|
|
30
|
+
MetricType2["CUMULATIVE"] = "cumulative";
|
|
31
|
+
MetricType2["CONVERSION"] = "conversion";
|
|
32
|
+
return MetricType2;
|
|
33
|
+
})(MetricType || {});
|
|
34
|
+
var TimeGranularity = /* @__PURE__ */ ((TimeGranularity2) => {
|
|
35
|
+
TimeGranularity2["DAY"] = "day";
|
|
36
|
+
TimeGranularity2["WEEK"] = "week";
|
|
37
|
+
TimeGranularity2["MONTH"] = "month";
|
|
38
|
+
TimeGranularity2["QUARTER"] = "quarter";
|
|
39
|
+
TimeGranularity2["YEAR"] = "year";
|
|
40
|
+
return TimeGranularity2;
|
|
41
|
+
})(TimeGranularity || {});
|
|
42
|
+
export {
|
|
43
|
+
AggregationType,
|
|
44
|
+
DimensionType,
|
|
45
|
+
EntityType,
|
|
46
|
+
MetricType,
|
|
47
|
+
TimeGranularity
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../runtime/analytics/types.ts"],"sourcesContent":["/**\n * Semantic type definitions for the analytics layer.\n *\n * These types mirror MetricFlow / dbt-semantic-interfaces enums but are\n * defined locally so consumers don't need the full dependency for basic\n * field configuration. When the cube.js backend is used, these values\n * map 1:1 to cube measure/dimension types.\n */\n\n// ============================================================================\n// Enums\n// ============================================================================\n\nexport enum AggregationType {\n SUM = 'sum',\n MIN = 'min',\n MAX = 'max',\n COUNT = 'count',\n COUNT_DISTINCT = 'count_distinct',\n AVERAGE = 'average',\n MEDIAN = 'median',\n PERCENTILE = 'percentile',\n SUM_BOOLEAN = 'sum_boolean',\n}\n\nexport enum DimensionType {\n CATEGORICAL = 'categorical',\n TIME = 'time',\n}\n\nexport enum EntityType {\n PRIMARY = 'primary',\n UNIQUE = 'unique',\n FOREIGN = 'foreign',\n NATURAL = 'natural',\n}\n\nexport enum MetricType {\n SIMPLE = 'simple',\n DERIVED = 'derived',\n RATIO = 'ratio',\n CUMULATIVE = 'cumulative',\n CONVERSION = 'conversion',\n}\n\n/**\n * Time granularity for time dimensions.\n *\n * Sub-day granularities (hour, minute, second) are intentionally omitted\n * because cube.js does not support them.\n */\nexport enum TimeGranularity {\n DAY = 'day',\n WEEK = 'week',\n MONTH = 'month',\n QUARTER = 'quarter',\n YEAR = 'year',\n}\n\n// ============================================================================\n// Config interfaces\n// ============================================================================\n\n/**\n * Configuration for non-additive dimensions on measures.\n *\n * Non-additive dimensions specify that a measure cannot be summed across\n * certain dimensions. For example, inventory balances cannot be summed\n * across time — you need the latest value, not the sum.\n */\nexport interface NonAdditiveDimensionConfig {\n name: string;\n window_choice?: AggregationType;\n window_groupings?: string[];\n}\n\n/**\n * Complete semantic configuration for a field.\n *\n * Used to store all semantic metadata on a field definition for later\n * extraction by the manifest builder / cube schema generator.\n */\nexport interface SemanticFieldConfig {\n // Measure configuration\n measure?: boolean;\n agg?: AggregationType;\n agg_time_dimension?: string;\n non_additive_dimension?: NonAdditiveDimensionConfig;\n\n // Dimension configuration\n dimension?: boolean;\n dimension_type?: DimensionType;\n time_granularity?: TimeGranularity;\n is_partition?: boolean;\n\n // Entity configuration\n entity?: boolean;\n entity_type?: EntityType;\n entity_role?: string;\n\n // Common\n semantic_expr?: string;\n semantic_label?: string;\n visibility?: 'internal' | 'agent' | 'public';\n}\n"],"mappings":";AAaO,IAAK,kBAAL,kBAAKA,qBAAL;AACL,EAAAA,iBAAA,SAAM;AACN,EAAAA,iBAAA,SAAM;AACN,EAAAA,iBAAA,SAAM;AACN,EAAAA,iBAAA,WAAQ;AACR,EAAAA,iBAAA,oBAAiB;AACjB,EAAAA,iBAAA,aAAU;AACV,EAAAA,iBAAA,YAAS;AACT,EAAAA,iBAAA,gBAAa;AACb,EAAAA,iBAAA,iBAAc;AATJ,SAAAA;AAAA,GAAA;AAYL,IAAK,gBAAL,kBAAKC,mBAAL;AACL,EAAAA,eAAA,iBAAc;AACd,EAAAA,eAAA,UAAO;AAFG,SAAAA;AAAA,GAAA;AAKL,IAAK,aAAL,kBAAKC,gBAAL;AACL,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,YAAS;AACT,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,aAAU;AAJA,SAAAA;AAAA,GAAA;AAOL,IAAK,aAAL,kBAAKC,gBAAL;AACL,EAAAA,YAAA,YAAS;AACT,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,WAAQ;AACR,EAAAA,YAAA,gBAAa;AACb,EAAAA,YAAA,gBAAa;AALH,SAAAA;AAAA,GAAA;AAcL,IAAK,kBAAL,kBAAKC,qBAAL;AACL,EAAAA,iBAAA,SAAM;AACN,EAAAA,iBAAA,UAAO;AACP,EAAAA,iBAAA,WAAQ;AACR,EAAAA,iBAAA,aAAU;AACV,EAAAA,iBAAA,UAAO;AALG,SAAAA;AAAA,GAAA;","names":["AggregationType","DimensionType","EntityType","MetricType","TimeGranularity"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { BaseRepository } from './base-repository.js';
|
|
2
|
+
import 'drizzle-orm/pg-core';
|
|
3
|
+
import 'drizzle-orm';
|
|
4
|
+
import '../types/drizzle.js';
|
|
5
|
+
import 'drizzle-orm/node-postgres';
|
|
6
|
+
|
|
7
|
+
declare abstract class ActivityEntityRepository<TEntity> extends BaseRepository<TEntity> {
|
|
8
|
+
/**
|
|
9
|
+
* Find activities within a date range (inclusive).
|
|
10
|
+
*/
|
|
11
|
+
findByDateRange(start: Date, end: Date): Promise<TEntity[]>;
|
|
12
|
+
/**
|
|
13
|
+
* Find all activities for a specific user.
|
|
14
|
+
*/
|
|
15
|
+
findByUserId(userId: string): Promise<TEntity[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Find all activities for a specific opportunity.
|
|
18
|
+
*/
|
|
19
|
+
findByOpportunityId(opportunityId: string): Promise<TEntity[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Find the most recent activities for an opportunity, ordered by occurredAt desc.
|
|
22
|
+
*/
|
|
23
|
+
findRecentByOpportunityId(opportunityId: string, limit?: number): Promise<TEntity[]>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { ActivityEntityRepository };
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// runtime/base-classes/activity-entity-repository.ts
|
|
2
|
+
import { eq as eq2, between, desc } from "drizzle-orm";
|
|
3
|
+
|
|
4
|
+
// runtime/base-classes/base-repository.ts
|
|
5
|
+
import { eq, inArray, isNull, sql } from "drizzle-orm";
|
|
6
|
+
var BaseRepository = class {
|
|
7
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
8
|
+
/**
|
|
9
|
+
* Behavior flags controlling automatic behavior injection.
|
|
10
|
+
* Override in concrete repositories to enable behaviors.
|
|
11
|
+
*/
|
|
12
|
+
behaviors = {
|
|
13
|
+
timestamps: false,
|
|
14
|
+
softDelete: false,
|
|
15
|
+
userTracking: false
|
|
16
|
+
};
|
|
17
|
+
db;
|
|
18
|
+
constructor(db) {
|
|
19
|
+
this.db = db;
|
|
20
|
+
}
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Read Operations
|
|
23
|
+
// ============================================================================
|
|
24
|
+
/**
|
|
25
|
+
* Find a single entity by its primary key.
|
|
26
|
+
* Returns null if not found (or soft-deleted when softDelete=true).
|
|
27
|
+
*/
|
|
28
|
+
async findById(id) {
|
|
29
|
+
const rows = await this.baseQuery().where(eq(this.table["id"], id)).limit(1);
|
|
30
|
+
return rows[0] ?? null;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Find multiple entities by their primary keys.
|
|
34
|
+
* Returns empty array immediately for empty input (avoids DB errors).
|
|
35
|
+
*/
|
|
36
|
+
async findByIds(ids) {
|
|
37
|
+
if (ids.length === 0) return [];
|
|
38
|
+
const rows = await this.baseQuery().where(inArray(this.table["id"], ids));
|
|
39
|
+
return rows;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* List entities with optional filtering, pagination, and ordering.
|
|
43
|
+
*/
|
|
44
|
+
async list(options) {
|
|
45
|
+
let query = this.baseQuery();
|
|
46
|
+
if (options?.where) {
|
|
47
|
+
query = query.where(options.where);
|
|
48
|
+
}
|
|
49
|
+
if (options?.orderBy) {
|
|
50
|
+
query = query.orderBy(options.orderBy);
|
|
51
|
+
}
|
|
52
|
+
if (options?.limit !== void 0) {
|
|
53
|
+
query = query.limit(options.limit);
|
|
54
|
+
}
|
|
55
|
+
if (options?.offset !== void 0) {
|
|
56
|
+
query = query.offset(options.offset);
|
|
57
|
+
}
|
|
58
|
+
const rows = await query;
|
|
59
|
+
return rows;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Count entities matching an optional WHERE clause.
|
|
63
|
+
* Soft-deleted rows are always excluded when softDelete=true.
|
|
64
|
+
*/
|
|
65
|
+
async count(where) {
|
|
66
|
+
let query = this.db.select({ count: sql`cast(count(*) as integer)` }).from(this.table);
|
|
67
|
+
const conditions = [];
|
|
68
|
+
if (this.behaviors.softDelete) {
|
|
69
|
+
conditions.push(isNull(this.table["deletedAt"]));
|
|
70
|
+
}
|
|
71
|
+
if (where) {
|
|
72
|
+
conditions.push(where);
|
|
73
|
+
}
|
|
74
|
+
if (conditions.length === 1) {
|
|
75
|
+
query = query.where(conditions[0]);
|
|
76
|
+
} else if (conditions.length > 1) {
|
|
77
|
+
const { and } = await import("drizzle-orm");
|
|
78
|
+
query = query.where(and(...conditions));
|
|
79
|
+
}
|
|
80
|
+
const rows = await query;
|
|
81
|
+
return rows[0]?.count ?? 0;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Check whether an entity with the given id exists.
|
|
85
|
+
*/
|
|
86
|
+
async exists(id) {
|
|
87
|
+
const result = await this.findById(id);
|
|
88
|
+
return result !== null;
|
|
89
|
+
}
|
|
90
|
+
// ============================================================================
|
|
91
|
+
// Write Operations
|
|
92
|
+
// ============================================================================
|
|
93
|
+
/**
|
|
94
|
+
* Insert a new entity. Timestamps are auto-injected when timestamps=true.
|
|
95
|
+
*/
|
|
96
|
+
async create(input) {
|
|
97
|
+
const data = this.withTimestamps(input, "create");
|
|
98
|
+
const rows = await this.db.insert(this.table).values(data).returning();
|
|
99
|
+
return rows[0];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Update an existing entity by id. updatedAt is auto-injected when timestamps=true.
|
|
103
|
+
* Returns the updated entity.
|
|
104
|
+
*/
|
|
105
|
+
async update(id, input) {
|
|
106
|
+
const data = this.withTimestamps(input, "update");
|
|
107
|
+
const rows = await this.db.update(this.table).set(data).where(eq(this.table["id"], id)).returning();
|
|
108
|
+
return rows[0];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Delete an entity by id.
|
|
112
|
+
* - softDelete=true: sets deletedAt to current timestamp
|
|
113
|
+
* - softDelete=false: hard-deletes the row
|
|
114
|
+
*/
|
|
115
|
+
async delete(id) {
|
|
116
|
+
if (this.behaviors.softDelete) {
|
|
117
|
+
await this.db.update(this.table).set({ deletedAt: /* @__PURE__ */ new Date() }).where(eq(this.table["id"], id));
|
|
118
|
+
} else {
|
|
119
|
+
await this.db.delete(this.table).where(eq(this.table["id"], id));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Insert or update multiple entities.
|
|
124
|
+
* Default naive implementation — family repositories override with
|
|
125
|
+
* proper conflict-target upsert (e.g., CrmEntityRepository).
|
|
126
|
+
*/
|
|
127
|
+
async upsertMany(inputs) {
|
|
128
|
+
return Promise.all(inputs.map((input) => this.create(input)));
|
|
129
|
+
}
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// Protected Helpers
|
|
132
|
+
// ============================================================================
|
|
133
|
+
/**
|
|
134
|
+
* Base SELECT query that automatically excludes soft-deleted rows
|
|
135
|
+
* when softDelete behavior is enabled.
|
|
136
|
+
*/
|
|
137
|
+
baseQuery() {
|
|
138
|
+
const query = this.db.select().from(this.table).$dynamic();
|
|
139
|
+
if (this.behaviors.softDelete) {
|
|
140
|
+
return query.where(isNull(this.table["deletedAt"]));
|
|
141
|
+
}
|
|
142
|
+
return query;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Merge timestamp fields into an input object.
|
|
146
|
+
* - mode='create': adds createdAt and updatedAt
|
|
147
|
+
* - mode='update': adds updatedAt only
|
|
148
|
+
*
|
|
149
|
+
* No-op when timestamps behavior is disabled.
|
|
150
|
+
*/
|
|
151
|
+
withTimestamps(input, mode) {
|
|
152
|
+
if (!this.behaviors.timestamps) return input;
|
|
153
|
+
const now = /* @__PURE__ */ new Date();
|
|
154
|
+
if (mode === "create") {
|
|
155
|
+
return { ...input, createdAt: now, updatedAt: now };
|
|
156
|
+
}
|
|
157
|
+
return { ...input, updatedAt: now };
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// runtime/base-classes/activity-entity-repository.ts
|
|
162
|
+
var ActivityEntityRepository = class extends BaseRepository {
|
|
163
|
+
/**
|
|
164
|
+
* Find activities within a date range (inclusive).
|
|
165
|
+
*/
|
|
166
|
+
async findByDateRange(start, end) {
|
|
167
|
+
const rows = await this.baseQuery().where(between(this.table["occurredAt"], start, end));
|
|
168
|
+
return rows;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Find all activities for a specific user.
|
|
172
|
+
*/
|
|
173
|
+
async findByUserId(userId) {
|
|
174
|
+
const rows = await this.baseQuery().where(eq2(this.table["userId"], userId));
|
|
175
|
+
return rows;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Find all activities for a specific opportunity.
|
|
179
|
+
*/
|
|
180
|
+
async findByOpportunityId(opportunityId) {
|
|
181
|
+
const rows = await this.baseQuery().where(eq2(this.table["opportunityId"], opportunityId));
|
|
182
|
+
return rows;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Find the most recent activities for an opportunity, ordered by occurredAt desc.
|
|
186
|
+
*/
|
|
187
|
+
async findRecentByOpportunityId(opportunityId, limit = 10) {
|
|
188
|
+
const rows = await this.baseQuery().where(eq2(this.table["opportunityId"], opportunityId)).orderBy(desc(this.table["occurredAt"])).limit(limit);
|
|
189
|
+
return rows;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
export {
|
|
193
|
+
ActivityEntityRepository
|
|
194
|
+
};
|
|
195
|
+
//# sourceMappingURL=activity-entity-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../runtime/base-classes/activity-entity-repository.ts","../../../runtime/base-classes/base-repository.ts"],"sourcesContent":["/**\n * ActivityEntityRepository<TEntity>\n *\n * Family-specific base for activity entities (emails, calls, meetings, notes).\n * Adds date-range queries, user/opportunity scoping, and recency ordering.\n *\n * Concrete repos extend this and declare their table + behaviors.\n */\nimport { eq, between, desc } from 'drizzle-orm';\nimport { BaseRepository } from './base-repository';\n\nexport abstract class ActivityEntityRepository<TEntity> extends BaseRepository<TEntity> {\n /**\n * Find activities within a date range (inclusive).\n */\n async findByDateRange(start: Date, end: Date): Promise<TEntity[]> {\n const rows = await this.baseQuery()\n .where(between(this.table['occurredAt'], start, end));\n return rows as TEntity[];\n }\n\n /**\n * Find all activities for a specific user.\n */\n async findByUserId(userId: string): Promise<TEntity[]> {\n const rows = await this.baseQuery()\n .where(eq(this.table['userId'], userId));\n return rows as TEntity[];\n }\n\n /**\n * Find all activities for a specific opportunity.\n */\n async findByOpportunityId(opportunityId: string): Promise<TEntity[]> {\n const rows = await this.baseQuery()\n .where(eq(this.table['opportunityId'], opportunityId));\n return rows as TEntity[];\n }\n\n /**\n * Find the most recent activities for an opportunity, ordered by occurredAt desc.\n */\n async findRecentByOpportunityId(opportunityId: string, limit = 10): Promise<TEntity[]> {\n const rows = await this.baseQuery()\n .where(eq(this.table['opportunityId'], opportunityId))\n .orderBy(desc(this.table['occurredAt']))\n .limit(limit);\n return rows as TEntity[];\n }\n}\n","/**\n * BaseRepository<TEntity>\n *\n * Abstract base class providing standard CRUD operations via Drizzle ORM.\n * Every generated repository extends this class.\n *\n * Family-specific bases (CrmEntityRepository, etc.) extend this in v0.1\n * without any changes to BaseRepository.\n *\n * NOT @Injectable — concrete repositories are @Injectable and inject DRIZZLE.\n */\nimport { eq, inArray, isNull, sql } from 'drizzle-orm';\nimport type { PgTableWithColumns, PgColumn } from 'drizzle-orm/pg-core';\nimport type { SQL } from 'drizzle-orm';\nimport type { DrizzleClient } from '../types/drizzle';\n\n// ============================================================================\n// Interfaces\n// ============================================================================\n\n/**\n * Behavior flags for the repository. Controls automatic timestamp injection\n * and soft-delete filtering.\n */\nexport interface BehaviorConfig {\n timestamps: boolean;\n softDelete: boolean;\n userTracking: boolean;\n}\n\n/**\n * Options for the list() method.\n */\nexport interface ListOptions {\n where?: SQL;\n limit?: number;\n offset?: number;\n orderBy?: PgColumn | SQL;\n}\n\n// ============================================================================\n// BaseRepository\n// ============================================================================\n\nexport abstract class BaseRepository<TEntity> {\n /**\n * The Drizzle table schema for this entity.\n * Concrete repositories declare this as a class property.\n */\n protected abstract readonly table: PgTableWithColumns<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n\n /**\n * Behavior flags controlling automatic behavior injection.\n * Override in concrete repositories to enable behaviors.\n */\n protected readonly behaviors: BehaviorConfig = {\n timestamps: false,\n softDelete: false,\n userTracking: false,\n };\n\n protected readonly db: DrizzleClient;\n\n constructor(db: DrizzleClient) {\n this.db = db;\n }\n\n // ============================================================================\n // Read Operations\n // ============================================================================\n\n /**\n * Find a single entity by its primary key.\n * Returns null if not found (or soft-deleted when softDelete=true).\n */\n async findById(id: string): Promise<TEntity | null> {\n const rows = await this.baseQuery()\n .where(eq(this.table['id'], id))\n .limit(1);\n return (rows[0] as TEntity) ?? null;\n }\n\n /**\n * Find multiple entities by their primary keys.\n * Returns empty array immediately for empty input (avoids DB errors).\n */\n async findByIds(ids: string[]): Promise<TEntity[]> {\n if (ids.length === 0) return [];\n const rows = await this.baseQuery().where(inArray(this.table['id'], ids));\n return rows as TEntity[];\n }\n\n /**\n * List entities with optional filtering, pagination, and ordering.\n */\n async list(options?: ListOptions): Promise<TEntity[]> {\n let query = this.baseQuery();\n\n if (options?.where) {\n query = query.where(options.where) as typeof query;\n }\n if (options?.orderBy) {\n query = query.orderBy(options.orderBy as SQL) as typeof query;\n }\n if (options?.limit !== undefined) {\n query = query.limit(options.limit) as typeof query;\n }\n if (options?.offset !== undefined) {\n query = query.offset(options.offset) as typeof query;\n }\n\n const rows = await query;\n return rows as TEntity[];\n }\n\n /**\n * Count entities matching an optional WHERE clause.\n * Soft-deleted rows are always excluded when softDelete=true.\n */\n async count(where?: SQL): Promise<number> {\n let query = this.db\n .select({ count: sql<number>`cast(count(*) as integer)` })\n .from(this.table);\n\n const conditions: SQL[] = [];\n if (this.behaviors.softDelete) {\n conditions.push(isNull(this.table['deletedAt']));\n }\n if (where) {\n conditions.push(where);\n }\n\n if (conditions.length === 1) {\n query = query.where(conditions[0]) as typeof query;\n } else if (conditions.length > 1) {\n // Combine with AND by building the condition inline\n const { and } = await import('drizzle-orm');\n query = query.where(and(...conditions)) as typeof query;\n }\n\n const rows = await query;\n return rows[0]?.count ?? 0;\n }\n\n /**\n * Check whether an entity with the given id exists.\n */\n async exists(id: string): Promise<boolean> {\n const result = await this.findById(id);\n return result !== null;\n }\n\n // ============================================================================\n // Write Operations\n // ============================================================================\n\n /**\n * Insert a new entity. Timestamps are auto-injected when timestamps=true.\n */\n async create(input: Partial<TEntity>): Promise<TEntity> {\n const data = this.withTimestamps(input as Record<string, unknown>, 'create');\n const rows = await this.db\n .insert(this.table)\n .values(data as any) // eslint-disable-line @typescript-eslint/no-explicit-any\n .returning();\n return rows[0] as TEntity;\n }\n\n /**\n * Update an existing entity by id. updatedAt is auto-injected when timestamps=true.\n * Returns the updated entity.\n */\n async update(id: string, input: Partial<TEntity>): Promise<TEntity> {\n const data = this.withTimestamps(input as Record<string, unknown>, 'update');\n const rows = await this.db\n .update(this.table)\n .set(data as any) // eslint-disable-line @typescript-eslint/no-explicit-any\n .where(eq(this.table['id'], id))\n .returning();\n return rows[0] as TEntity;\n }\n\n /**\n * Delete an entity by id.\n * - softDelete=true: sets deletedAt to current timestamp\n * - softDelete=false: hard-deletes the row\n */\n async delete(id: string): Promise<void> {\n if (this.behaviors.softDelete) {\n await this.db\n .update(this.table)\n .set({ deletedAt: new Date() } as any) // eslint-disable-line @typescript-eslint/no-explicit-any\n .where(eq(this.table['id'], id));\n } else {\n await this.db\n .delete(this.table)\n .where(eq(this.table['id'], id));\n }\n }\n\n /**\n * Insert or update multiple entities.\n * Default naive implementation — family repositories override with\n * proper conflict-target upsert (e.g., CrmEntityRepository).\n */\n async upsertMany(inputs: Array<Partial<TEntity>>): Promise<TEntity[]> {\n return Promise.all(inputs.map((input) => this.create(input)));\n }\n\n // ============================================================================\n // Protected Helpers\n // ============================================================================\n\n /**\n * Base SELECT query that automatically excludes soft-deleted rows\n * when softDelete behavior is enabled.\n */\n protected baseQuery() {\n const query = this.db.select().from(this.table).$dynamic();\n if (this.behaviors.softDelete) {\n return query.where(isNull(this.table['deletedAt']));\n }\n return query;\n }\n\n /**\n * Merge timestamp fields into an input object.\n * - mode='create': adds createdAt and updatedAt\n * - mode='update': adds updatedAt only\n *\n * No-op when timestamps behavior is disabled.\n */\n protected withTimestamps(\n input: Record<string, unknown>,\n mode: 'create' | 'update',\n ): Record<string, unknown> {\n if (!this.behaviors.timestamps) return input;\n const now = new Date();\n if (mode === 'create') {\n return { ...input, createdAt: now, updatedAt: now };\n }\n return { ...input, updatedAt: now };\n }\n}\n"],"mappings":"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","names":["eq","eq"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { IBaseRepository, BaseService } from './base-service.js';
|
|
2
|
+
import '../subsystems/events/event-bus.protocol.js';
|
|
3
|
+
import '../types/drizzle.js';
|
|
4
|
+
import 'drizzle-orm/node-postgres';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* ActivityEntityService<TRepo, TEntity>
|
|
8
|
+
*
|
|
9
|
+
* Family-specific base service for activity entities.
|
|
10
|
+
* Delegates to an activity repository that provides date-range,
|
|
11
|
+
* user, and opportunity queries.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface IActivityEntityRepository<TEntity> extends IBaseRepository<TEntity> {
|
|
15
|
+
findByDateRange(start: Date, end: Date): Promise<TEntity[]>;
|
|
16
|
+
findByUserId(userId: string): Promise<TEntity[]>;
|
|
17
|
+
findByOpportunityId(opportunityId: string): Promise<TEntity[]>;
|
|
18
|
+
findRecentByOpportunityId(opportunityId: string, limit?: number): Promise<TEntity[]>;
|
|
19
|
+
}
|
|
20
|
+
declare abstract class ActivityEntityService<TRepo extends IActivityEntityRepository<TEntity>, TEntity> extends BaseService<TRepo, TEntity> {
|
|
21
|
+
/**
|
|
22
|
+
* Find activities within a date range (inclusive).
|
|
23
|
+
*/
|
|
24
|
+
findByDateRange(start: Date, end: Date): Promise<TEntity[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Find all activities for a specific user.
|
|
27
|
+
*/
|
|
28
|
+
findByUser(userId: string): Promise<TEntity[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Find all activities for a specific opportunity.
|
|
31
|
+
*/
|
|
32
|
+
findByOpportunity(opportunityId: string): Promise<TEntity[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Find the most recent activities for an opportunity.
|
|
35
|
+
*/
|
|
36
|
+
findRecent(opportunityId: string, limit?: number): Promise<TEntity[]>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { ActivityEntityService, type IActivityEntityRepository };
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// runtime/base-classes/lifecycle-events.ts
|
|
2
|
+
import { randomUUID } from "crypto";
|
|
3
|
+
var SYSTEM_FIELDS = /* @__PURE__ */ new Set([
|
|
4
|
+
"id",
|
|
5
|
+
"createdAt",
|
|
6
|
+
"updatedAt",
|
|
7
|
+
"deletedAt"
|
|
8
|
+
]);
|
|
9
|
+
function entitySnapshot(entity) {
|
|
10
|
+
const snap = {};
|
|
11
|
+
for (const [key, value] of Object.entries(entity)) {
|
|
12
|
+
if (!SYSTEM_FIELDS.has(key)) {
|
|
13
|
+
snap[key] = value;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return snap;
|
|
17
|
+
}
|
|
18
|
+
function diffSnapshots(before, after) {
|
|
19
|
+
const changes = [];
|
|
20
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
21
|
+
for (const key of allKeys) {
|
|
22
|
+
if (SYSTEM_FIELDS.has(key)) continue;
|
|
23
|
+
const oldVal = before[key];
|
|
24
|
+
const newVal = after[key];
|
|
25
|
+
if (oldVal !== newVal && JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
|
|
26
|
+
changes.push({ field: key, oldValue: oldVal, newValue: newVal });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return changes;
|
|
30
|
+
}
|
|
31
|
+
function buildLifecycleEvent(entityName, action, entityId, snapshot) {
|
|
32
|
+
return {
|
|
33
|
+
id: randomUUID(),
|
|
34
|
+
type: `${entityName}.${action}`,
|
|
35
|
+
aggregateId: entityId,
|
|
36
|
+
aggregateType: entityName,
|
|
37
|
+
payload: snapshot ? { snapshot } : {},
|
|
38
|
+
occurredAt: /* @__PURE__ */ new Date(),
|
|
39
|
+
metadata: { category: "lifecycle" }
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function buildChangeEvents(entityName, entityId, changes) {
|
|
43
|
+
return changes.map((c) => ({
|
|
44
|
+
id: randomUUID(),
|
|
45
|
+
type: `${entityName}.field_changed`,
|
|
46
|
+
aggregateId: entityId,
|
|
47
|
+
aggregateType: entityName,
|
|
48
|
+
payload: {
|
|
49
|
+
fieldName: c.field,
|
|
50
|
+
oldValue: c.oldValue,
|
|
51
|
+
newValue: c.newValue
|
|
52
|
+
},
|
|
53
|
+
occurredAt: /* @__PURE__ */ new Date(),
|
|
54
|
+
metadata: { category: "change" }
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
async function emitSafely(eventBus, events) {
|
|
58
|
+
if (!eventBus || events.length === 0) return;
|
|
59
|
+
try {
|
|
60
|
+
if (events.length === 1) {
|
|
61
|
+
await eventBus.publish(events[0]);
|
|
62
|
+
} else {
|
|
63
|
+
await eventBus.publishMany(events);
|
|
64
|
+
}
|
|
65
|
+
} catch {
|
|
66
|
+
console.warn(`[lifecycle-events] failed to emit ${events.length} event(s)`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// runtime/base-classes/base-service.ts
|
|
71
|
+
var BaseService = class {
|
|
72
|
+
constructor(repository) {
|
|
73
|
+
this.repository = repository;
|
|
74
|
+
}
|
|
75
|
+
repository;
|
|
76
|
+
/**
|
|
77
|
+
* Entity name for event types (e.g., 'account' → 'account.created').
|
|
78
|
+
* Set by generated services. If empty, lifecycle events are skipped.
|
|
79
|
+
*/
|
|
80
|
+
entityName;
|
|
81
|
+
/**
|
|
82
|
+
* Event bus for lifecycle/change event emission.
|
|
83
|
+
* Injected via @Inject(EVENT_BUS) @Optional() on generated services.
|
|
84
|
+
* If undefined (no events subsystem installed), emission is silently skipped.
|
|
85
|
+
*/
|
|
86
|
+
eventBus;
|
|
87
|
+
/**
|
|
88
|
+
* Whether to emit lifecycle events. Default: true.
|
|
89
|
+
* Override to false in entity YAML via behaviors or in the service class.
|
|
90
|
+
*/
|
|
91
|
+
emitLifecycleEvents = true;
|
|
92
|
+
/**
|
|
93
|
+
* Find a single entity by its primary key.
|
|
94
|
+
* Returns null if not found.
|
|
95
|
+
*/
|
|
96
|
+
findById(id) {
|
|
97
|
+
return this.repository.findById(id);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Find multiple entities by their primary keys.
|
|
101
|
+
*/
|
|
102
|
+
findByIds(ids) {
|
|
103
|
+
return this.repository.findByIds(ids);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* List entities with optional filtering/pagination options.
|
|
107
|
+
*/
|
|
108
|
+
list(options) {
|
|
109
|
+
return this.repository.list(options);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Count entities matching an optional filter.
|
|
113
|
+
*/
|
|
114
|
+
count(where) {
|
|
115
|
+
return this.repository.count(where);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Check whether an entity with the given id exists.
|
|
119
|
+
*/
|
|
120
|
+
exists(id) {
|
|
121
|
+
return this.repository.exists(id);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Insert a new entity.
|
|
125
|
+
* Emits a LIFECYCLE 'created' event with entity snapshot.
|
|
126
|
+
*/
|
|
127
|
+
async create(input) {
|
|
128
|
+
const result = await this.repository.create(input);
|
|
129
|
+
if (this._shouldEmit()) {
|
|
130
|
+
const snap = entitySnapshot(result);
|
|
131
|
+
const id = result.id;
|
|
132
|
+
const event = buildLifecycleEvent(this.entityName, "created", id, snap);
|
|
133
|
+
void emitSafely(this.eventBus, [event]);
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Update an existing entity by id.
|
|
139
|
+
* Emits a LIFECYCLE 'updated' event + CHANGE events for each modified field.
|
|
140
|
+
*/
|
|
141
|
+
async update(id, input) {
|
|
142
|
+
let before;
|
|
143
|
+
if (this._shouldEmit()) {
|
|
144
|
+
const existing = await this.repository.findById(id);
|
|
145
|
+
if (existing) {
|
|
146
|
+
before = entitySnapshot(existing);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const result = await this.repository.update(id, input);
|
|
150
|
+
if (this._shouldEmit()) {
|
|
151
|
+
const after = entitySnapshot(result);
|
|
152
|
+
const events = [
|
|
153
|
+
buildLifecycleEvent(this.entityName, "updated", id, after)
|
|
154
|
+
];
|
|
155
|
+
if (before) {
|
|
156
|
+
const changes = diffSnapshots(before, after);
|
|
157
|
+
if (changes.length > 0) {
|
|
158
|
+
events.push(...buildChangeEvents(this.entityName, id, changes));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
void emitSafely(this.eventBus, events);
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Delete an entity by id.
|
|
167
|
+
* Emits a LIFECYCLE 'deleted' event.
|
|
168
|
+
*/
|
|
169
|
+
async delete(id) {
|
|
170
|
+
await this.repository.delete(id);
|
|
171
|
+
if (this._shouldEmit()) {
|
|
172
|
+
const event = buildLifecycleEvent(this.entityName, "deleted", id);
|
|
173
|
+
void emitSafely(this.eventBus, [event]);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/** Check whether lifecycle event emission is active. */
|
|
177
|
+
_shouldEmit() {
|
|
178
|
+
return Boolean(
|
|
179
|
+
this.emitLifecycleEvents && this.entityName && this.eventBus
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// runtime/base-classes/activity-entity-service.ts
|
|
185
|
+
var ActivityEntityService = class extends BaseService {
|
|
186
|
+
/**
|
|
187
|
+
* Find activities within a date range (inclusive).
|
|
188
|
+
*/
|
|
189
|
+
findByDateRange(start, end) {
|
|
190
|
+
return this.repository.findByDateRange(start, end);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Find all activities for a specific user.
|
|
194
|
+
*/
|
|
195
|
+
findByUser(userId) {
|
|
196
|
+
return this.repository.findByUserId(userId);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Find all activities for a specific opportunity.
|
|
200
|
+
*/
|
|
201
|
+
findByOpportunity(opportunityId) {
|
|
202
|
+
return this.repository.findByOpportunityId(opportunityId);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Find the most recent activities for an opportunity.
|
|
206
|
+
*/
|
|
207
|
+
findRecent(opportunityId, limit) {
|
|
208
|
+
return this.repository.findRecentByOpportunityId(opportunityId, limit);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
export {
|
|
212
|
+
ActivityEntityService
|
|
213
|
+
};
|
|
214
|
+
//# sourceMappingURL=activity-entity-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../runtime/base-classes/lifecycle-events.ts","../../../runtime/base-classes/base-service.ts","../../../runtime/base-classes/activity-entity-service.ts"],"sourcesContent":["/**\n * Lifecycle event emission for BaseService.\n *\n * Ported from pattern-stack/atoms/patterns/services/base.py — the Python\n * BaseService emits LIFECYCLE and CHANGE events on every CRUD operation.\n * This module provides the same capability for the TypeScript codegen stack.\n *\n * Design:\n * - Fire-and-forget: event emission never fails the CRUD operation.\n * - IEventBus is optional: if no EVENT_BUS is injected, emission is silently\n * skipped. This means base classes work in projects that haven't installed\n * the events subsystem.\n * - LIFECYCLE events carry an entity snapshot in payload.\n * - CHANGE events carry per-field old/new diffs.\n * - Controlled per-entity via `emitLifecycleEvents` flag (default: true).\n */\n\nimport { randomUUID } from 'crypto';\nimport type { IEventBus, DomainEvent } from '../subsystems/events/event-bus.protocol';\n\n// ============================================================================\n// Event categories (subset of pattern-stack's EventCategory)\n// ============================================================================\n\nexport type EventCategory = 'lifecycle' | 'change';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/** System fields excluded from entity snapshots and change diffs. */\nconst SYSTEM_FIELDS = new Set([\n\t'id',\n\t'createdAt',\n\t'updatedAt',\n\t'deletedAt',\n]);\n\n/**\n * Snapshot an entity's field values, excluding system fields.\n * Mirrors pattern-stack's `_get_entity_snapshot()`.\n */\nexport function entitySnapshot(entity: Record<string, unknown>): Record<string, unknown> {\n\tconst snap: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(entity)) {\n\t\tif (!SYSTEM_FIELDS.has(key)) {\n\t\t\tsnap[key] = value;\n\t\t}\n\t}\n\treturn snap;\n}\n\n/**\n * Diff two entity snapshots, returning per-field old/new pairs.\n * Only includes fields that actually changed.\n */\nexport function diffSnapshots(\n\tbefore: Record<string, unknown>,\n\tafter: Record<string, unknown>,\n): Array<{ field: string; oldValue: unknown; newValue: unknown }> {\n\tconst changes: Array<{ field: string; oldValue: unknown; newValue: unknown }> = [];\n\tconst allKeys = new Set([...Object.keys(before), ...Object.keys(after)]);\n\n\tfor (const key of allKeys) {\n\t\tif (SYSTEM_FIELDS.has(key)) continue;\n\t\tconst oldVal = before[key];\n\t\tconst newVal = after[key];\n\t\t// Simple equality — good enough for primitives and nulls.\n\t\t// For deep objects, JSON.stringify comparison.\n\t\tif (oldVal !== newVal && JSON.stringify(oldVal) !== JSON.stringify(newVal)) {\n\t\t\tchanges.push({ field: key, oldValue: oldVal, newValue: newVal });\n\t\t}\n\t}\n\n\treturn changes;\n}\n\n// ============================================================================\n// Event builders\n// ============================================================================\n\nexport function buildLifecycleEvent(\n\tentityName: string,\n\taction: 'created' | 'updated' | 'deleted',\n\tentityId: string,\n\tsnapshot?: Record<string, unknown>,\n): DomainEvent {\n\treturn {\n\t\tid: randomUUID(),\n\t\ttype: `${entityName}.${action}`,\n\t\taggregateId: entityId,\n\t\taggregateType: entityName,\n\t\tpayload: snapshot ? { snapshot } : {},\n\t\toccurredAt: new Date(),\n\t\tmetadata: { category: 'lifecycle' as EventCategory },\n\t};\n}\n\nexport function buildChangeEvents(\n\tentityName: string,\n\tentityId: string,\n\tchanges: Array<{ field: string; oldValue: unknown; newValue: unknown }>,\n): DomainEvent[] {\n\treturn changes.map((c) => ({\n\t\tid: randomUUID(),\n\t\ttype: `${entityName}.field_changed`,\n\t\taggregateId: entityId,\n\t\taggregateType: entityName,\n\t\tpayload: {\n\t\t\tfieldName: c.field,\n\t\t\toldValue: c.oldValue,\n\t\t\tnewValue: c.newValue,\n\t\t},\n\t\toccurredAt: new Date(),\n\t\tmetadata: { category: 'change' as EventCategory },\n\t}));\n}\n\n// ============================================================================\n// Emission helper (fire-and-forget)\n// ============================================================================\n\n/**\n * Emit events to the bus, swallowing errors.\n * Mirrors pattern-stack's `_emit_lifecycle_event()` try/except.\n */\nexport async function emitSafely(\n\teventBus: IEventBus | undefined,\n\tevents: DomainEvent[],\n): Promise<void> {\n\tif (!eventBus || events.length === 0) return;\n\ttry {\n\t\tif (events.length === 1) {\n\t\t\tawait eventBus.publish(events[0]);\n\t\t} else {\n\t\t\tawait eventBus.publishMany(events);\n\t\t}\n\t} catch {\n\t\t// Log but never fail the CRUD operation.\n\t\t// In production, this would use a structured logger.\n\t\tconsole.warn(`[lifecycle-events] failed to emit ${events.length} event(s)`);\n\t}\n}\n","/**\n * BaseService<TRepo, TEntity>\n *\n * Abstract base class providing 8 CRUD pass-through methods delegating to\n * an injected repository. Every generated service extends this class.\n *\n * Lifecycle event emission (LIFECYCLE + CHANGE categories) is built into\n * create/update/delete — matching pattern-stack's BaseService. Events are\n * fire-and-forget: emission never fails the CRUD operation. If no IEventBus\n * is injected (eventBus is undefined), emission is silently skipped.\n *\n * Generated services set `entityName` and optionally inject `eventBus` via\n * NestJS property injection (@Inject(EVENT_BUS) @Optional()).\n *\n * Note: @Injectable() is applied on concrete services (not here) so that\n * NestJS DI metadata is emitted at the concrete class level. This matches\n * the pattern established by BaseRepository.\n */\n\nimport type { IEventBus } from '../subsystems/events/event-bus.protocol';\nimport {\n entitySnapshot,\n diffSnapshots,\n buildLifecycleEvent,\n buildChangeEvents,\n emitSafely,\n} from './lifecycle-events';\n\n// ============================================================================\n// IBaseRepository interface\n// ============================================================================\n\n/**\n * Structural interface that BaseRepository satisfies.\n * Use this as the TRepo constraint so BaseService is not coupled to the\n * concrete Drizzle-backed BaseRepository.\n */\nexport interface IBaseRepository<TEntity> {\n findById(id: string): Promise<TEntity | null>;\n findByIds(ids: string[]): Promise<TEntity[]>;\n list(options?: unknown): Promise<TEntity[]>;\n count(where?: unknown): Promise<number>;\n exists(id: string): Promise<boolean>;\n create(input: Partial<TEntity>): Promise<TEntity>;\n update(id: string, input: Partial<TEntity>): Promise<TEntity>;\n delete(id: string): Promise<void>;\n}\n\n// ============================================================================\n// BaseService\n// ============================================================================\n\nexport abstract class BaseService<TRepo extends IBaseRepository<TEntity>, TEntity> {\n /**\n * Entity name for event types (e.g., 'account' → 'account.created').\n * Set by generated services. If empty, lifecycle events are skipped.\n */\n protected entityName?: string;\n\n /**\n * Event bus for lifecycle/change event emission.\n * Injected via @Inject(EVENT_BUS) @Optional() on generated services.\n * If undefined (no events subsystem installed), emission is silently skipped.\n */\n protected eventBus?: IEventBus;\n\n /**\n * Whether to emit lifecycle events. Default: true.\n * Override to false in entity YAML via behaviors or in the service class.\n */\n protected emitLifecycleEvents = true;\n\n constructor(protected readonly repository: TRepo) {}\n\n /**\n * Find a single entity by its primary key.\n * Returns null if not found.\n */\n findById(id: string): Promise<TEntity | null> {\n return this.repository.findById(id);\n }\n\n /**\n * Find multiple entities by their primary keys.\n */\n findByIds(ids: string[]): Promise<TEntity[]> {\n return this.repository.findByIds(ids);\n }\n\n /**\n * List entities with optional filtering/pagination options.\n */\n list(options?: unknown): Promise<TEntity[]> {\n return this.repository.list(options);\n }\n\n /**\n * Count entities matching an optional filter.\n */\n count(where?: unknown): Promise<number> {\n return this.repository.count(where);\n }\n\n /**\n * Check whether an entity with the given id exists.\n */\n exists(id: string): Promise<boolean> {\n return this.repository.exists(id);\n }\n\n /**\n * Insert a new entity.\n * Emits a LIFECYCLE 'created' event with entity snapshot.\n */\n async create(input: Partial<TEntity>): Promise<TEntity> {\n const result = await this.repository.create(input);\n\n if (this._shouldEmit()) {\n const snap = entitySnapshot(result as Record<string, unknown>);\n const id = (result as Record<string, unknown>).id as string;\n const event = buildLifecycleEvent(this.entityName!, 'created', id, snap);\n void emitSafely(this.eventBus, [event]);\n }\n\n return result;\n }\n\n /**\n * Update an existing entity by id.\n * Emits a LIFECYCLE 'updated' event + CHANGE events for each modified field.\n */\n async update(id: string, input: Partial<TEntity>): Promise<TEntity> {\n // Snapshot before for change diffing\n let before: Record<string, unknown> | undefined;\n if (this._shouldEmit()) {\n const existing = await this.repository.findById(id);\n if (existing) {\n before = entitySnapshot(existing as Record<string, unknown>);\n }\n }\n\n const result = await this.repository.update(id, input);\n\n if (this._shouldEmit()) {\n const after = entitySnapshot(result as Record<string, unknown>);\n const events = [\n buildLifecycleEvent(this.entityName!, 'updated', id, after),\n ];\n // Append per-field CHANGE events\n if (before) {\n const changes = diffSnapshots(before, after);\n if (changes.length > 0) {\n events.push(...buildChangeEvents(this.entityName!, id, changes));\n }\n }\n void emitSafely(this.eventBus, events);\n }\n\n return result;\n }\n\n /**\n * Delete an entity by id.\n * Emits a LIFECYCLE 'deleted' event.\n */\n async delete(id: string): Promise<void> {\n await this.repository.delete(id);\n\n if (this._shouldEmit()) {\n const event = buildLifecycleEvent(this.entityName!, 'deleted', id);\n void emitSafely(this.eventBus, [event]);\n }\n }\n\n /** Check whether lifecycle event emission is active. */\n private _shouldEmit(): boolean {\n return Boolean(\n this.emitLifecycleEvents &&\n this.entityName &&\n this.eventBus,\n );\n }\n}\n","/**\n * ActivityEntityService<TRepo, TEntity>\n *\n * Family-specific base service for activity entities.\n * Delegates to an activity repository that provides date-range,\n * user, and opportunity queries.\n */\nimport { BaseService, type IBaseRepository } from './base-service';\n\nexport interface IActivityEntityRepository<TEntity> extends IBaseRepository<TEntity> {\n findByDateRange(start: Date, end: Date): Promise<TEntity[]>;\n findByUserId(userId: string): Promise<TEntity[]>;\n findByOpportunityId(opportunityId: string): Promise<TEntity[]>;\n findRecentByOpportunityId(opportunityId: string, limit?: number): Promise<TEntity[]>;\n}\n\nexport abstract class ActivityEntityService<\n TRepo extends IActivityEntityRepository<TEntity>,\n TEntity,\n> extends BaseService<TRepo, TEntity> {\n /**\n * Find activities within a date range (inclusive).\n */\n findByDateRange(start: Date, end: Date): Promise<TEntity[]> {\n return this.repository.findByDateRange(start, end);\n }\n\n /**\n * Find all activities for a specific user.\n */\n findByUser(userId: string): Promise<TEntity[]> {\n return this.repository.findByUserId(userId);\n }\n\n /**\n * Find all activities for a specific opportunity.\n */\n findByOpportunity(opportunityId: string): Promise<TEntity[]> {\n return this.repository.findByOpportunityId(opportunityId);\n }\n\n /**\n * Find the most recent activities for an opportunity.\n */\n findRecent(opportunityId: string, limit?: number): Promise<TEntity[]> {\n return this.repository.findRecentByOpportunityId(opportunityId, limit);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base read use cases
|
|
3
|
+
*
|
|
4
|
+
* Abstract base classes for auto-generated per-entity read use cases.
|
|
5
|
+
* Controllers always import use case classes — never services directly (ADR-003).
|
|
6
|
+
*
|
|
7
|
+
* Each entity gets a single generated file with two named exports:
|
|
8
|
+
* - ContactFindByIdUseCase extends BaseFindByIdUseCase<ContactService, Contact>
|
|
9
|
+
* - ContactListUseCase extends BaseListUseCase<ContactService, Contact>
|
|
10
|
+
*
|
|
11
|
+
* Note: @Injectable() is applied on concrete use case classes (not here),
|
|
12
|
+
* matching the pattern established by BaseRepository and BaseService.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Structural interface for any service that supports findById.
|
|
16
|
+
* Keeps base use cases decoupled from concrete service implementations.
|
|
17
|
+
*/
|
|
18
|
+
interface IFindByIdService<TEntity> {
|
|
19
|
+
findById(id: string): Promise<TEntity | null>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Base class for generated FindById use cases.
|
|
23
|
+
*
|
|
24
|
+
* Generated usage:
|
|
25
|
+
* ```typescript
|
|
26
|
+
* @Injectable()
|
|
27
|
+
* export class ContactFindByIdUseCase extends BaseFindByIdUseCase<ContactService, Contact> {
|
|
28
|
+
* constructor(service: ContactService) { super(service); }
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare abstract class BaseFindByIdUseCase<TService extends IFindByIdService<TEntity>, TEntity> {
|
|
33
|
+
protected readonly service: TService;
|
|
34
|
+
constructor(service: TService);
|
|
35
|
+
/**
|
|
36
|
+
* Find a single entity by its primary key.
|
|
37
|
+
* Returns null if not found.
|
|
38
|
+
*/
|
|
39
|
+
execute(id: string): Promise<TEntity | null>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Structural interface for any service that supports list.
|
|
43
|
+
*/
|
|
44
|
+
interface IListService<TEntity> {
|
|
45
|
+
list(options?: unknown): Promise<TEntity[]>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Base class for generated List use cases.
|
|
49
|
+
*
|
|
50
|
+
* Generated usage:
|
|
51
|
+
* ```typescript
|
|
52
|
+
* @Injectable()
|
|
53
|
+
* export class ContactListUseCase extends BaseListUseCase<ContactService, Contact> {
|
|
54
|
+
* constructor(service: ContactService) { super(service); }
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare abstract class BaseListUseCase<TService extends IListService<TEntity>, TEntity> {
|
|
59
|
+
protected readonly service: TService;
|
|
60
|
+
constructor(service: TService);
|
|
61
|
+
/**
|
|
62
|
+
* List all entities (no filters).
|
|
63
|
+
* Controllers that need filtered lists should use a dedicated use case.
|
|
64
|
+
*/
|
|
65
|
+
execute(): Promise<TEntity[]>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { BaseFindByIdUseCase, BaseListUseCase, type IFindByIdService, type IListService };
|