@hazeljs/ml 0.2.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/evaluation/metrics.service.test.js +288 -0
  2. package/dist/experiments/__tests__/experiment.decorator.test.d.ts +2 -0
  3. package/dist/experiments/__tests__/experiment.decorator.test.d.ts.map +1 -0
  4. package/dist/experiments/__tests__/experiment.decorator.test.js +121 -0
  5. package/dist/experiments/__tests__/experiment.service.test.d.ts +2 -0
  6. package/dist/experiments/__tests__/experiment.service.test.d.ts.map +1 -0
  7. package/dist/experiments/__tests__/experiment.service.test.js +460 -0
  8. package/dist/experiments/experiment.decorator.d.ts +44 -0
  9. package/dist/experiments/experiment.decorator.d.ts.map +1 -0
  10. package/dist/experiments/experiment.decorator.js +51 -0
  11. package/dist/experiments/experiment.service.d.ts +42 -0
  12. package/dist/experiments/experiment.service.d.ts.map +1 -0
  13. package/dist/experiments/experiment.service.js +355 -0
  14. package/dist/experiments/experiment.types.d.ts +60 -0
  15. package/dist/experiments/experiment.types.d.ts.map +1 -0
  16. package/dist/experiments/experiment.types.js +5 -0
  17. package/dist/experiments/index.d.ts +9 -0
  18. package/dist/experiments/index.d.ts.map +1 -0
  19. package/dist/experiments/index.js +16 -0
  20. package/dist/features/__tests__/feature-view.decorator.test.d.ts +2 -0
  21. package/dist/features/__tests__/feature-view.decorator.test.d.ts.map +1 -0
  22. package/dist/features/__tests__/feature-view.decorator.test.js +168 -0
  23. package/dist/features/__tests__/feature.decorator.test.d.ts +2 -0
  24. package/dist/features/__tests__/feature.decorator.test.d.ts.map +1 -0
  25. package/dist/features/__tests__/feature.decorator.test.js +167 -0
  26. package/dist/features/feature-store.service.d.ts +59 -0
  27. package/dist/features/feature-store.service.d.ts.map +1 -0
  28. package/dist/features/feature-store.service.js +197 -0
  29. package/dist/features/feature-view.decorator.d.ts +52 -0
  30. package/dist/features/feature-view.decorator.d.ts.map +1 -0
  31. package/dist/features/feature-view.decorator.js +54 -0
  32. package/dist/features/feature.decorator.d.ts +42 -0
  33. package/dist/features/feature.decorator.d.ts.map +1 -0
  34. package/dist/features/feature.decorator.js +49 -0
  35. package/dist/features/feature.types.d.ts +93 -0
  36. package/dist/features/feature.types.d.ts.map +1 -0
  37. package/dist/features/feature.types.js +5 -0
  38. package/dist/features/index.d.ts +12 -0
  39. package/dist/features/index.d.ts.map +1 -0
  40. package/dist/features/index.js +29 -0
  41. package/dist/features/offline-store.d.ts +40 -0
  42. package/dist/features/offline-store.d.ts.map +1 -0
  43. package/dist/features/offline-store.js +215 -0
  44. package/dist/features/online-store.d.ts +45 -0
  45. package/dist/features/online-store.d.ts.map +1 -0
  46. package/dist/features/online-store.js +139 -0
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +26 -1
  50. package/dist/monitoring/__tests__/drift.service.test.d.ts +2 -0
  51. package/dist/monitoring/__tests__/drift.service.test.d.ts.map +1 -0
  52. package/dist/monitoring/__tests__/drift.service.test.js +362 -0
  53. package/dist/monitoring/__tests__/monitor.service.test.d.ts +2 -0
  54. package/dist/monitoring/__tests__/monitor.service.test.d.ts.map +1 -0
  55. package/dist/monitoring/__tests__/monitor.service.test.js +360 -0
  56. package/dist/monitoring/drift.service.d.ts +68 -0
  57. package/dist/monitoring/drift.service.d.ts.map +1 -0
  58. package/dist/monitoring/drift.service.js +360 -0
  59. package/dist/monitoring/drift.types.d.ts +44 -0
  60. package/dist/monitoring/drift.types.d.ts.map +1 -0
  61. package/dist/monitoring/drift.types.js +5 -0
  62. package/dist/monitoring/index.d.ts +10 -0
  63. package/dist/monitoring/index.d.ts.map +1 -0
  64. package/dist/monitoring/index.js +13 -0
  65. package/dist/monitoring/monitor.service.d.ts +79 -0
  66. package/dist/monitoring/monitor.service.d.ts.map +1 -0
  67. package/dist/monitoring/monitor.service.js +192 -0
  68. package/dist/training/trainer.service.test.js +105 -0
  69. package/package.json +2 -2
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const feature_decorator_1 = require("../feature.decorator");
13
+ describe('Feature decorator', () => {
14
+ it('should attach metadata to property', () => {
15
+ class TestFeatures {
16
+ }
17
+ __decorate([
18
+ (0, feature_decorator_1.Feature)({ valueType: 'number', description: 'Test feature' }),
19
+ __metadata("design:type", Number)
20
+ ], TestFeatures.prototype, "testFeature", void 0);
21
+ const metadata = (0, feature_decorator_1.getFeatureMetadata)(TestFeatures);
22
+ expect(metadata).toBeDefined();
23
+ expect(metadata).toHaveLength(1);
24
+ expect(metadata[0].propertyKey).toBe('testFeature');
25
+ expect(metadata[0].valueType).toBe('number');
26
+ expect(metadata[0].description).toBe('Test feature');
27
+ });
28
+ it('should use property key as default name', () => {
29
+ class TestFeatures {
30
+ }
31
+ __decorate([
32
+ (0, feature_decorator_1.Feature)({ valueType: 'string' }),
33
+ __metadata("design:type", String)
34
+ ], TestFeatures.prototype, "myFeature", void 0);
35
+ const metadata = (0, feature_decorator_1.getFeatureMetadata)(TestFeatures);
36
+ expect(metadata[0].name).toBe('myFeature');
37
+ });
38
+ it('should use custom name when provided', () => {
39
+ class TestFeatures {
40
+ }
41
+ __decorate([
42
+ (0, feature_decorator_1.Feature)({ name: 'custom_name', valueType: 'string' }),
43
+ __metadata("design:type", String)
44
+ ], TestFeatures.prototype, "myFeature", void 0);
45
+ const metadata = (0, feature_decorator_1.getFeatureMetadata)(TestFeatures);
46
+ expect(metadata[0].name).toBe('custom_name');
47
+ });
48
+ it('should support all value types', () => {
49
+ class TestFeatures {
50
+ }
51
+ __decorate([
52
+ (0, feature_decorator_1.Feature)({ valueType: 'string' }),
53
+ __metadata("design:type", String)
54
+ ], TestFeatures.prototype, "stringFeature", void 0);
55
+ __decorate([
56
+ (0, feature_decorator_1.Feature)({ valueType: 'number' }),
57
+ __metadata("design:type", Number)
58
+ ], TestFeatures.prototype, "numberFeature", void 0);
59
+ __decorate([
60
+ (0, feature_decorator_1.Feature)({ valueType: 'boolean' }),
61
+ __metadata("design:type", Boolean)
62
+ ], TestFeatures.prototype, "booleanFeature", void 0);
63
+ __decorate([
64
+ (0, feature_decorator_1.Feature)({ valueType: 'array' }),
65
+ __metadata("design:type", Array)
66
+ ], TestFeatures.prototype, "arrayFeature", void 0);
67
+ __decorate([
68
+ (0, feature_decorator_1.Feature)({ valueType: 'object' }),
69
+ __metadata("design:type", Object)
70
+ ], TestFeatures.prototype, "objectFeature", void 0);
71
+ const metadata = (0, feature_decorator_1.getFeatureMetadata)(TestFeatures);
72
+ expect(metadata).toHaveLength(5);
73
+ expect(metadata[0].valueType).toBe('string');
74
+ expect(metadata[1].valueType).toBe('number');
75
+ expect(metadata[2].valueType).toBe('boolean');
76
+ expect(metadata[3].valueType).toBe('array');
77
+ expect(metadata[4].valueType).toBe('object');
78
+ });
79
+ it('should handle optional tags', () => {
80
+ class TestFeatures {
81
+ }
82
+ __decorate([
83
+ (0, feature_decorator_1.Feature)({ valueType: 'string', tags: ['demographic', 'pii'] }),
84
+ __metadata("design:type", String)
85
+ ], TestFeatures.prototype, "segment", void 0);
86
+ const metadata = (0, feature_decorator_1.getFeatureMetadata)(TestFeatures);
87
+ expect(metadata[0].tags).toEqual(['demographic', 'pii']);
88
+ });
89
+ it('should handle optional ttl', () => {
90
+ class TestFeatures {
91
+ }
92
+ __decorate([
93
+ (0, feature_decorator_1.Feature)({ valueType: 'number', ttl: 3600 }),
94
+ __metadata("design:type", Number)
95
+ ], TestFeatures.prototype, "score", void 0);
96
+ const metadata = (0, feature_decorator_1.getFeatureMetadata)(TestFeatures);
97
+ expect(metadata[0].ttl).toBe(3600);
98
+ });
99
+ it('should handle optional entityExtractor', () => {
100
+ const extractor = (input) => String(input);
101
+ class TestFeatures {
102
+ }
103
+ __decorate([
104
+ (0, feature_decorator_1.Feature)({ valueType: 'string', entityExtractor: extractor }),
105
+ __metadata("design:type", String)
106
+ ], TestFeatures.prototype, "userId", void 0);
107
+ const metadata = (0, feature_decorator_1.getFeatureMetadata)(TestFeatures);
108
+ expect(metadata[0].entityExtractor).toBe(extractor);
109
+ });
110
+ it('should handle multiple features on same class', () => {
111
+ class TestFeatures {
112
+ }
113
+ __decorate([
114
+ (0, feature_decorator_1.Feature)({ valueType: 'string' }),
115
+ __metadata("design:type", String)
116
+ ], TestFeatures.prototype, "feature1", void 0);
117
+ __decorate([
118
+ (0, feature_decorator_1.Feature)({ valueType: 'number' }),
119
+ __metadata("design:type", Number)
120
+ ], TestFeatures.prototype, "feature2", void 0);
121
+ __decorate([
122
+ (0, feature_decorator_1.Feature)({ valueType: 'boolean' }),
123
+ __metadata("design:type", Boolean)
124
+ ], TestFeatures.prototype, "feature3", void 0);
125
+ const metadata = (0, feature_decorator_1.getFeatureMetadata)(TestFeatures);
126
+ expect(metadata).toHaveLength(3);
127
+ expect(metadata[0].propertyKey).toBe('feature1');
128
+ expect(metadata[1].propertyKey).toBe('feature2');
129
+ expect(metadata[2].propertyKey).toBe('feature3');
130
+ });
131
+ it('should handle features without optional fields', () => {
132
+ class TestFeatures {
133
+ }
134
+ __decorate([
135
+ (0, feature_decorator_1.Feature)({ valueType: 'string' }),
136
+ __metadata("design:type", String)
137
+ ], TestFeatures.prototype, "simpleFeature", void 0);
138
+ const metadata = (0, feature_decorator_1.getFeatureMetadata)(TestFeatures);
139
+ expect(metadata[0].description).toBeUndefined();
140
+ expect(metadata[0].tags).toBeUndefined();
141
+ expect(metadata[0].ttl).toBeUndefined();
142
+ expect(metadata[0].entityExtractor).toBeUndefined();
143
+ });
144
+ });
145
+ describe('hasFeatureMetadata', () => {
146
+ it('should return true for decorated class', () => {
147
+ class TestFeatures {
148
+ }
149
+ __decorate([
150
+ (0, feature_decorator_1.Feature)({ valueType: 'string' }),
151
+ __metadata("design:type", String)
152
+ ], TestFeatures.prototype, "feature", void 0);
153
+ expect((0, feature_decorator_1.hasFeatureMetadata)(TestFeatures)).toBe(true);
154
+ });
155
+ it('should return false for non-decorated class', () => {
156
+ class TestFeatures {
157
+ }
158
+ expect((0, feature_decorator_1.hasFeatureMetadata)(TestFeatures)).toBe(false);
159
+ });
160
+ });
161
+ describe('getFeatureMetadata', () => {
162
+ it('should return undefined for non-decorated class', () => {
163
+ class TestFeatures {
164
+ }
165
+ expect((0, feature_decorator_1.getFeatureMetadata)(TestFeatures)).toBeUndefined();
166
+ });
167
+ });
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Feature Store Service - Central registry and retrieval for ML features
3
+ */
4
+ import type { FeatureView, FeatureResponse, FeatureStoreConfig, FeatureMetadata } from './feature.types';
5
+ export interface FeatureRegistration {
6
+ name: string;
7
+ view: FeatureView;
8
+ entityExtractor?: (input: unknown) => string;
9
+ }
10
+ export declare class FeatureStoreService {
11
+ private features;
12
+ private views;
13
+ private onlineStore;
14
+ private offlineStore;
15
+ private config;
16
+ configure(config: FeatureStoreConfig): void;
17
+ /**
18
+ * Register a feature view for discovery and retrieval
19
+ */
20
+ registerView(name: string, view: FeatureView): void;
21
+ /**
22
+ * Get features for online inference (low-latency)
23
+ */
24
+ getOnlineFeatures(entityIds: string[], featureNames: string[]): Promise<FeatureResponse[]>;
25
+ /**
26
+ * Get historical features for training (point-in-time correct)
27
+ */
28
+ getOfflineFeatures(entityIds: string[], featureNames: string[], timestamp: Date): Promise<FeatureResponse[]>;
29
+ /**
30
+ * Push features to online store (for real-time serving)
31
+ */
32
+ pushOnlineFeatures(entityId: string, features: Record<string, unknown>): Promise<void>;
33
+ /**
34
+ * Write features to offline store (for training data)
35
+ */
36
+ writeOfflineFeatures(entityId: string, features: Record<string, unknown>, timestamp?: Date): Promise<void>;
37
+ /**
38
+ * Materialize a feature view - compute and store features
39
+ */
40
+ materialize(viewName: string, entityIds: string[], options?: {
41
+ toOnline?: boolean;
42
+ toOffline?: boolean;
43
+ }): Promise<void>;
44
+ /**
45
+ * List all registered features
46
+ */
47
+ listFeatures(): FeatureMetadata[];
48
+ /**
49
+ * List all registered views
50
+ */
51
+ listViews(): string[];
52
+ /**
53
+ * Get a specific view
54
+ */
55
+ getView(name: string): FeatureView | undefined;
56
+ private groupByEntity;
57
+ close(): Promise<void>;
58
+ }
59
+ //# sourceMappingURL=feature-store.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-store.service.d.ts","sourceRoot":"","sources":["../../src/features/feature-store.service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAEV,WAAW,EAEX,eAAe,EACf,kBAAkB,EAClB,eAAe,EAChB,MAAM,iBAAiB,CAAC;AAIzB,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;IAClB,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAC9C;AAED,qBACa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAA2C;IAC3D,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,MAAM,CAA0B;IAExC,SAAS,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAc3C;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI;IAkBnD;;OAEG;IACG,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAmBhG;;OAEG;IACG,kBAAkB,CACtB,SAAS,EAAE,MAAM,EAAE,EACnB,YAAY,EAAE,MAAM,EAAE,EACtB,SAAS,EAAE,IAAI,GACd,OAAO,CAAC,eAAe,EAAE,CAAC;IA2B7B;;OAEG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB5F;;OAEG;IACG,oBAAoB,CACxB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,SAAS,GAAE,IAAiB,GAC3B,OAAO,CAAC,IAAI,CAAC;IAehB;;OAEG;IACG,WAAW,CACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAO,GACxD,OAAO,CAAC,IAAI,CAAC;IA6BhB;;OAEG;IACH,YAAY,IAAI,eAAe,EAAE;IAIjC;;OAEG;IACH,SAAS,IAAI,MAAM,EAAE;IAIrB;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAI9C,OAAO,CAAC,aAAa;IAaf,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAS7B"}
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ /**
3
+ * Feature Store Service - Central registry and retrieval for ML features
4
+ */
5
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
6
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
7
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
8
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
9
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.FeatureStoreService = void 0;
16
+ const core_1 = require("@hazeljs/core");
17
+ const core_2 = __importDefault(require("@hazeljs/core"));
18
+ const online_store_1 = require("./online-store");
19
+ const offline_store_1 = require("./offline-store");
20
+ let FeatureStoreService = class FeatureStoreService {
21
+ constructor() {
22
+ this.features = new Map();
23
+ this.views = new Map();
24
+ this.onlineStore = null;
25
+ this.offlineStore = null;
26
+ this.config = {};
27
+ }
28
+ configure(config) {
29
+ this.config = config;
30
+ if (config.online) {
31
+ this.onlineStore = (0, online_store_1.createOnlineStore)(config.online);
32
+ core_2.default.debug('FeatureStore: online store configured', { type: config.online.type });
33
+ }
34
+ if (config.offline) {
35
+ this.offlineStore = (0, offline_store_1.createOfflineStore)(config.offline);
36
+ core_2.default.debug('FeatureStore: offline store configured', { type: config.offline.type });
37
+ }
38
+ }
39
+ /**
40
+ * Register a feature view for discovery and retrieval
41
+ */
42
+ registerView(name, view) {
43
+ this.views.set(name, view);
44
+ // Register individual features
45
+ for (const feature of view.features) {
46
+ this.features.set(feature.name, {
47
+ name: feature.name,
48
+ description: feature.description,
49
+ valueType: feature.valueType,
50
+ tags: [],
51
+ createdAt: new Date(),
52
+ updatedAt: new Date(),
53
+ });
54
+ }
55
+ core_2.default.debug(`Registered feature view: ${name} with ${view.features.length} features`);
56
+ }
57
+ /**
58
+ * Get features for online inference (low-latency)
59
+ */
60
+ async getOnlineFeatures(entityIds, featureNames) {
61
+ if (!this.onlineStore) {
62
+ throw new Error('Online store not configured. Call configure() first.');
63
+ }
64
+ const responses = [];
65
+ for (const entityId of entityIds) {
66
+ const features = await this.onlineStore.getMulti(entityId, featureNames);
67
+ responses.push({
68
+ entityId,
69
+ features,
70
+ timestamp: new Date(),
71
+ });
72
+ }
73
+ return responses;
74
+ }
75
+ /**
76
+ * Get historical features for training (point-in-time correct)
77
+ */
78
+ async getOfflineFeatures(entityIds, featureNames, timestamp) {
79
+ if (!this.offlineStore) {
80
+ throw new Error('Offline store not configured. Call configure() first.');
81
+ }
82
+ if (!this.config.enablePointInTime) {
83
+ // Simple read without point-in-time correction
84
+ const values = await this.offlineStore.read(entityIds, featureNames);
85
+ const grouped = this.groupByEntity(values);
86
+ return entityIds.map((entityId) => ({
87
+ entityId,
88
+ features: grouped[entityId] ?? {},
89
+ timestamp,
90
+ }));
91
+ }
92
+ // Point-in-time correct retrieval (prevents data leakage)
93
+ const pitFeatures = await this.offlineStore.readPointInTime(entityIds, featureNames, timestamp);
94
+ return entityIds.map((entityId) => ({
95
+ entityId,
96
+ features: pitFeatures[entityId] ?? {},
97
+ timestamp,
98
+ }));
99
+ }
100
+ /**
101
+ * Push features to online store (for real-time serving)
102
+ */
103
+ async pushOnlineFeatures(entityId, features) {
104
+ if (!this.onlineStore) {
105
+ throw new Error('Online store not configured');
106
+ }
107
+ const now = new Date();
108
+ const values = Object.entries(features).map(([name, value]) => ({
109
+ entityId,
110
+ featureName: name,
111
+ value,
112
+ timestamp: now,
113
+ }));
114
+ await this.onlineStore.setMulti(values);
115
+ }
116
+ /**
117
+ * Write features to offline store (for training data)
118
+ */
119
+ async writeOfflineFeatures(entityId, features, timestamp = new Date()) {
120
+ if (!this.offlineStore) {
121
+ throw new Error('Offline store not configured');
122
+ }
123
+ const values = Object.entries(features).map(([name, value]) => ({
124
+ entityId,
125
+ featureName: name,
126
+ value,
127
+ timestamp,
128
+ }));
129
+ await this.offlineStore.write(values);
130
+ }
131
+ /**
132
+ * Materialize a feature view - compute and store features
133
+ */
134
+ async materialize(viewName, entityIds, options = {}) {
135
+ const view = this.views.get(viewName);
136
+ if (!view) {
137
+ throw new Error(`Feature view not found: ${viewName}`);
138
+ }
139
+ const _featureNames = view.features.map((f) => f.name);
140
+ for (const entityId of entityIds) {
141
+ const features = {};
142
+ for (const feature of view.features) {
143
+ if (feature.transform) {
144
+ features[feature.name] = feature.transform({ entityId });
145
+ }
146
+ }
147
+ if (options.toOnline && this.onlineStore) {
148
+ await this.pushOnlineFeatures(entityId, features);
149
+ }
150
+ if (options.toOffline && this.offlineStore) {
151
+ await this.writeOfflineFeatures(entityId, features);
152
+ }
153
+ }
154
+ core_2.default.debug(`Materialized ${viewName} for ${entityIds.length} entities`);
155
+ }
156
+ /**
157
+ * List all registered features
158
+ */
159
+ listFeatures() {
160
+ return Array.from(this.features.values());
161
+ }
162
+ /**
163
+ * List all registered views
164
+ */
165
+ listViews() {
166
+ return Array.from(this.views.keys());
167
+ }
168
+ /**
169
+ * Get a specific view
170
+ */
171
+ getView(name) {
172
+ return this.views.get(name);
173
+ }
174
+ groupByEntity(values) {
175
+ const grouped = {};
176
+ for (const v of values) {
177
+ if (!grouped[v.entityId]) {
178
+ grouped[v.entityId] = {};
179
+ }
180
+ grouped[v.entityId][v.featureName] = v.value;
181
+ }
182
+ return grouped;
183
+ }
184
+ async close() {
185
+ if (this.onlineStore) {
186
+ await this.onlineStore.close();
187
+ }
188
+ if (this.offlineStore) {
189
+ await this.offlineStore.close();
190
+ }
191
+ core_2.default.debug('FeatureStore: closed all stores');
192
+ }
193
+ };
194
+ exports.FeatureStoreService = FeatureStoreService;
195
+ exports.FeatureStoreService = FeatureStoreService = __decorate([
196
+ (0, core_1.Service)()
197
+ ], FeatureStoreService);
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @FeatureView decorator - Define a collection of related features
3
+ */
4
+ import 'reflect-metadata';
5
+ export declare const FEATURE_VIEW_METADATA_KEY: unique symbol;
6
+ export interface FeatureViewOptions {
7
+ name: string;
8
+ description?: string;
9
+ entities: string[];
10
+ online?: boolean;
11
+ offline?: boolean;
12
+ ttl?: number;
13
+ source?: {
14
+ type: 'batch' | 'stream' | 'request';
15
+ config?: Record<string, unknown>;
16
+ };
17
+ }
18
+ export interface FeatureViewMetadata {
19
+ name: string;
20
+ description?: string;
21
+ entities: string[];
22
+ online: boolean;
23
+ offline: boolean;
24
+ ttl?: number;
25
+ source?: FeatureViewOptions['source'];
26
+ }
27
+ /**
28
+ * Mark a class as a Feature View - a collection of related features.
29
+ * Combine with @Feature on properties to define the feature schema.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * @FeatureView({
34
+ * name: 'user-behavior',
35
+ * entities: ['user'],
36
+ * description: 'Features derived from user behavior',
37
+ * online: true,
38
+ * offline: true
39
+ * })
40
+ * class UserBehaviorFeatures {
41
+ * @Feature({ valueType: 'number' })
42
+ * loginCount: number;
43
+ *
44
+ * @Feature({ valueType: 'number' })
45
+ * avgSessionDuration: number;
46
+ * }
47
+ * ```
48
+ */
49
+ export declare function FeatureView(options: FeatureViewOptions): ClassDecorator;
50
+ export declare function getFeatureViewMetadata(target: object): FeatureViewMetadata | undefined;
51
+ export declare function hasFeatureViewMetadata(target: object): boolean;
52
+ //# sourceMappingURL=feature-view.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-view.decorator.d.ts","sourceRoot":"","sources":["../../src/features/feature-view.decorator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,kBAAkB,CAAC;AAE1B,eAAO,MAAM,yBAAyB,eAAwC,CAAC;AAE/E,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE;QACP,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;QACrC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAClC,CAAC;CACH;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,cAAc,CAevE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAEtF;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE9D"}
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ /**
3
+ * @FeatureView decorator - Define a collection of related features
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FEATURE_VIEW_METADATA_KEY = void 0;
7
+ exports.FeatureView = FeatureView;
8
+ exports.getFeatureViewMetadata = getFeatureViewMetadata;
9
+ exports.hasFeatureViewMetadata = hasFeatureViewMetadata;
10
+ require("reflect-metadata");
11
+ exports.FEATURE_VIEW_METADATA_KEY = Symbol('hazel:feature-view:metadata');
12
+ /**
13
+ * Mark a class as a Feature View - a collection of related features.
14
+ * Combine with @Feature on properties to define the feature schema.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * @FeatureView({
19
+ * name: 'user-behavior',
20
+ * entities: ['user'],
21
+ * description: 'Features derived from user behavior',
22
+ * online: true,
23
+ * offline: true
24
+ * })
25
+ * class UserBehaviorFeatures {
26
+ * @Feature({ valueType: 'number' })
27
+ * loginCount: number;
28
+ *
29
+ * @Feature({ valueType: 'number' })
30
+ * avgSessionDuration: number;
31
+ * }
32
+ * ```
33
+ */
34
+ function FeatureView(options) {
35
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
36
+ return function (target) {
37
+ const metadata = {
38
+ name: options.name,
39
+ description: options.description,
40
+ entities: options.entities,
41
+ online: options.online ?? true,
42
+ offline: options.offline ?? true,
43
+ ttl: options.ttl,
44
+ source: options.source,
45
+ };
46
+ Reflect.defineMetadata(exports.FEATURE_VIEW_METADATA_KEY, metadata, target);
47
+ };
48
+ }
49
+ function getFeatureViewMetadata(target) {
50
+ return Reflect.getMetadata(exports.FEATURE_VIEW_METADATA_KEY, target);
51
+ }
52
+ function hasFeatureViewMetadata(target) {
53
+ return Reflect.hasMetadata(exports.FEATURE_VIEW_METADATA_KEY, target);
54
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @Feature decorator - Mark a class property as a feature
3
+ */
4
+ import 'reflect-metadata';
5
+ export declare const FEATURE_METADATA_KEY: unique symbol;
6
+ export interface FeatureOptions {
7
+ name?: string;
8
+ description?: string;
9
+ valueType: 'string' | 'number' | 'boolean' | 'array' | 'object';
10
+ tags?: string[];
11
+ ttl?: number;
12
+ entityExtractor?: (input: unknown) => string;
13
+ }
14
+ export interface FeatureMetadata {
15
+ propertyKey: string;
16
+ name: string;
17
+ description?: string;
18
+ valueType: FeatureOptions['valueType'];
19
+ tags?: string[];
20
+ ttl?: number;
21
+ entityExtractor?: (input: unknown) => string;
22
+ }
23
+ /**
24
+ * Mark a class property as a feature for the Feature Store.
25
+ * The property will be automatically discovered and registered.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * @FeatureView({ name: 'user-features', entities: ['user'] })
30
+ * class UserFeatures {
31
+ * @Feature({ valueType: 'number', description: 'User purchase count' })
32
+ * purchaseCount: number;
33
+ *
34
+ * @Feature({ valueType: 'string', tags: ['demographic'] })
35
+ * segment: string;
36
+ * }
37
+ * ```
38
+ */
39
+ export declare function Feature(options: FeatureOptions): PropertyDecorator;
40
+ export declare function getFeatureMetadata(target: object): FeatureMetadata[] | undefined;
41
+ export declare function hasFeatureMetadata(target: object): boolean;
42
+ //# sourceMappingURL=feature.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature.decorator.d.ts","sourceRoot":"","sources":["../../src/features/feature.decorator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,kBAAkB,CAAC;AAE1B,eAAO,MAAM,oBAAoB,eAAmC,CAAC;AAErE,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IAChE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAC9C;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,cAAc,CAAC,WAAW,CAAC,CAAC;IACvC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAC9C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,iBAAiB,CAgBlE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,EAAE,GAAG,SAAS,CAEhF;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE1D"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ /**
3
+ * @Feature decorator - Mark a class property as a feature
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FEATURE_METADATA_KEY = void 0;
7
+ exports.Feature = Feature;
8
+ exports.getFeatureMetadata = getFeatureMetadata;
9
+ exports.hasFeatureMetadata = hasFeatureMetadata;
10
+ require("reflect-metadata");
11
+ exports.FEATURE_METADATA_KEY = Symbol('hazel:feature:metadata');
12
+ /**
13
+ * Mark a class property as a feature for the Feature Store.
14
+ * The property will be automatically discovered and registered.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * @FeatureView({ name: 'user-features', entities: ['user'] })
19
+ * class UserFeatures {
20
+ * @Feature({ valueType: 'number', description: 'User purchase count' })
21
+ * purchaseCount: number;
22
+ *
23
+ * @Feature({ valueType: 'string', tags: ['demographic'] })
24
+ * segment: string;
25
+ * }
26
+ * ```
27
+ */
28
+ function Feature(options) {
29
+ return function (target, propertyKey) {
30
+ const metadata = {
31
+ propertyKey: propertyKey,
32
+ name: options.name ?? propertyKey,
33
+ description: options.description,
34
+ valueType: options.valueType,
35
+ tags: options.tags,
36
+ ttl: options.ttl,
37
+ entityExtractor: options.entityExtractor,
38
+ };
39
+ const existing = Reflect.getMetadata(exports.FEATURE_METADATA_KEY, target.constructor) || [];
40
+ existing.push(metadata);
41
+ Reflect.defineMetadata(exports.FEATURE_METADATA_KEY, existing, target.constructor);
42
+ };
43
+ }
44
+ function getFeatureMetadata(target) {
45
+ return Reflect.getMetadata(exports.FEATURE_METADATA_KEY, target);
46
+ }
47
+ function hasFeatureMetadata(target) {
48
+ return Reflect.hasMetadata(exports.FEATURE_METADATA_KEY, target);
49
+ }