@magek/adapter-read-model-store-nedb 0.0.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.
package/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # NeDB Read Model Store Adapter
2
+
3
+ This package provides a NeDB-based read model store adapter for the Magek framework.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @magek/adapter-read-model-store-nedb
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { MagekConfig } from '@magek/common'
15
+ import { readModelStore } from '@magek/adapter-read-model-store-nedb'
16
+
17
+ const config = new MagekConfig('development')
18
+ config.readModelStoreAdapter = readModelStore
19
+ ```
20
+
21
+ ## Features
22
+
23
+ - Full ReadModelStoreAdapter interface implementation
24
+ - Support for fetch, search, store, and delete operations
25
+ - Built-in health checks
26
+ - Comprehensive query capabilities with filtering, sorting, and pagination
27
+ - Optimistic concurrency control
28
+ - Field projection support
29
+
30
+ ## API
31
+
32
+ The adapter implements the standard `ReadModelStoreAdapter` interface:
33
+
34
+ - `fetch(config, readModelName, readModelID)` - Fetch a single read model by ID
35
+ - `search(config, readModelName, parameters)` - Search read models with filters
36
+ - `store(config, readModelName, readModel)` - Store or update a read model
37
+ - `delete(config, readModelName, readModelID)` - Delete a read model by ID
38
+ - `rawToEnvelopes(rawReadModels)` - Convert raw data to read model envelopes
39
+ - `healthCheck` - Health check methods (isUp, details, urls)
40
+
41
+ ## Storage
42
+
43
+ Read models are stored in a local NeDB file at `.magek/read_models.json`.
@@ -0,0 +1,5 @@
1
+ import { ReadModelStoreAdapter } from '@magek/common';
2
+ export declare const readModelStore: ReadModelStoreAdapter;
3
+ export { ReadModelRegistry } from './read-model-registry';
4
+ export { fetchReadModel, storeReadModel, searchReadModel, deleteReadModel, } from './library/read-model-adapter';
5
+ export { queryRecordFor } from './library/searcher-adapter';
package/dist/index.js ADDED
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.queryRecordFor = exports.deleteReadModel = exports.searchReadModel = exports.storeReadModel = exports.fetchReadModel = exports.ReadModelRegistry = exports.readModelStore = void 0;
4
+ const read_model_registry_1 = require("./read-model-registry");
5
+ const read_model_adapter_1 = require("./library/read-model-adapter");
6
+ const paths_1 = require("./paths");
7
+ const fs_1 = require("fs");
8
+ // Pre-built NeDB Read Model Store Adapter instance
9
+ const readModelRegistry = new read_model_registry_1.ReadModelRegistry();
10
+ async function countAll(database) {
11
+ await database.loadDatabaseIfNeeded();
12
+ const count = await database.readModels.countAsync({});
13
+ return count !== null && count !== void 0 ? count : 0;
14
+ }
15
+ exports.readModelStore = {
16
+ fetch: async (config, readModelName, readModelID, sequenceKey) => {
17
+ const result = await (0, read_model_adapter_1.fetchReadModel)(readModelRegistry, config, readModelName, readModelID, sequenceKey);
18
+ if (!result || result.length === 0) {
19
+ return undefined;
20
+ }
21
+ return result;
22
+ },
23
+ search: async (config, readModelName, filters, sortBy, limit, afterCursor, paginatedVersion, select) => {
24
+ return await (0, read_model_adapter_1.searchReadModel)(readModelRegistry, config, readModelName, filters, sortBy, limit, afterCursor, paginatedVersion !== null && paginatedVersion !== void 0 ? paginatedVersion : false, select);
25
+ },
26
+ store: async (config, readModelName, readModel) => {
27
+ var _a;
28
+ const expectedCurrentVersion = ((_a = readModel.version) !== null && _a !== void 0 ? _a : 1) - 1;
29
+ await (0, read_model_adapter_1.storeReadModel)(readModelRegistry, config, readModelName, readModel.value, expectedCurrentVersion);
30
+ // Return the stored envelope with updated timestamps
31
+ return {
32
+ ...readModel,
33
+ updatedAt: new Date().toISOString(),
34
+ };
35
+ },
36
+ delete: async (config, readModelName, readModelID) => {
37
+ // Create a minimal ReadModelInterface for the delete operation
38
+ const readModel = {
39
+ id: readModelID,
40
+ };
41
+ await (0, read_model_adapter_1.deleteReadModel)(readModelRegistry, config, readModelName, readModel);
42
+ },
43
+ rawToEnvelopes: async (config, rawReadModels) => {
44
+ // This would typically convert raw database records to envelopes
45
+ // For now, assume rawReadModels is already in the correct format
46
+ return rawReadModels;
47
+ },
48
+ healthCheck: {
49
+ isUp: async () => (0, fs_1.existsSync)(paths_1.readModelsDatabase),
50
+ details: async () => {
51
+ const count = await countAll(readModelRegistry);
52
+ return {
53
+ file: paths_1.readModelsDatabase,
54
+ count: count,
55
+ };
56
+ },
57
+ urls: async () => [paths_1.readModelsDatabase],
58
+ },
59
+ };
60
+ // Export individual components for backward compatibility
61
+ var read_model_registry_2 = require("./read-model-registry");
62
+ Object.defineProperty(exports, "ReadModelRegistry", { enumerable: true, get: function () { return read_model_registry_2.ReadModelRegistry; } });
63
+ var read_model_adapter_2 = require("./library/read-model-adapter");
64
+ Object.defineProperty(exports, "fetchReadModel", { enumerable: true, get: function () { return read_model_adapter_2.fetchReadModel; } });
65
+ Object.defineProperty(exports, "storeReadModel", { enumerable: true, get: function () { return read_model_adapter_2.storeReadModel; } });
66
+ Object.defineProperty(exports, "searchReadModel", { enumerable: true, get: function () { return read_model_adapter_2.searchReadModel; } });
67
+ Object.defineProperty(exports, "deleteReadModel", { enumerable: true, get: function () { return read_model_adapter_2.deleteReadModel; } });
68
+ var searcher_adapter_1 = require("./library/searcher-adapter");
69
+ Object.defineProperty(exports, "queryRecordFor", { enumerable: true, get: function () { return searcher_adapter_1.queryRecordFor; } });
@@ -0,0 +1,7 @@
1
+ import { MagekConfig, FilterFor, ProjectionFor, ReadModelEnvelope, ReadModelInterface, ReadModelListResult, ReadOnlyNonEmptyArray, SequenceKey, SortFor, UUID } from '@magek/common';
2
+ import { ReadModelRegistry } from '../read-model-registry';
3
+ export declare function rawReadModelEventsToEnvelopes(config: MagekConfig, rawEvents: Array<unknown>): Promise<Array<ReadModelEnvelope>>;
4
+ export declare function fetchReadModel(db: ReadModelRegistry, config: MagekConfig, readModelName: string, readModelID: UUID, sequenceKey?: SequenceKey): Promise<ReadOnlyNonEmptyArray<ReadModelInterface> | undefined>;
5
+ export declare function storeReadModel(db: ReadModelRegistry, config: MagekConfig, readModelName: string, readModel: ReadModelInterface, expectedCurrentVersion: number): Promise<void>;
6
+ export declare function searchReadModel<TReadModel extends ReadModelInterface>(db: ReadModelRegistry, config: MagekConfig, readModelName: string, filters: FilterFor<unknown>, sortBy?: SortFor<unknown>, limit?: number, afterCursor?: Record<string, string> | undefined, paginatedVersion?: boolean, select?: ProjectionFor<TReadModel>): Promise<Array<TReadModel> | ReadModelListResult<TReadModel>>;
7
+ export declare function deleteReadModel(db: ReadModelRegistry, config: MagekConfig, readModelName: string, readModel: ReadModelInterface): Promise<void>;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rawReadModelEventsToEnvelopes = rawReadModelEventsToEnvelopes;
4
+ exports.fetchReadModel = fetchReadModel;
5
+ exports.storeReadModel = storeReadModel;
6
+ exports.searchReadModel = searchReadModel;
7
+ exports.deleteReadModel = deleteReadModel;
8
+ const common_1 = require("@magek/common");
9
+ const read_model_registry_1 = require("../read-model-registry");
10
+ const searcher_adapter_1 = require("./searcher-adapter");
11
+ async function rawReadModelEventsToEnvelopes(config, rawEvents) {
12
+ return rawEvents;
13
+ }
14
+ async function fetchReadModel(db, config, readModelName, readModelID, sequenceKey) {
15
+ const logger = (0, common_1.getLogger)(config, 'read-model-adapter#fetchReadModel');
16
+ let query = { typeName: readModelName, 'value.id': readModelID };
17
+ // If sequenceKey is provided, add it to the query
18
+ if (sequenceKey) {
19
+ query[`value.${sequenceKey.name}`] = sequenceKey.value;
20
+ }
21
+ const response = await db.query(query);
22
+ if (response.length === 0) {
23
+ logger.debug(`Read model ${readModelName} with ID ${readModelID} not found`);
24
+ return undefined;
25
+ }
26
+ logger.debug(`Loaded read model ${readModelName} with ID ${readModelID} with result:`, response.map(item => item.value));
27
+ return response.map(item => item.value);
28
+ }
29
+ async function storeReadModel(db, config, readModelName, readModel, expectedCurrentVersion) {
30
+ const logger = (0, common_1.getLogger)(config, 'read-model-adapter#storeReadModel');
31
+ logger.debug('Storing readModel ' + JSON.stringify(readModel));
32
+ try {
33
+ await db.store({ typeName: readModelName, value: readModel }, expectedCurrentVersion);
34
+ }
35
+ catch (e) {
36
+ const error = e;
37
+ // The error will be thrown, but in case of a conditional check, we throw the expected error type by the core
38
+ if (error.errorType == read_model_registry_1.UNIQUE_VIOLATED_ERROR_TYPE) {
39
+ logger.warn(`Unique violated storing ReadModel ${JSON.stringify(readModel)} and expectedCurrentVersion ${expectedCurrentVersion}`);
40
+ throw new common_1.OptimisticConcurrencyUnexpectedVersionError(error.message);
41
+ }
42
+ throw e;
43
+ }
44
+ logger.debug('Read model stored');
45
+ }
46
+ async function searchReadModel(db, config, readModelName, filters, sortBy, limit, afterCursor, paginatedVersion = false, select) {
47
+ var _a, _b;
48
+ const logger = (0, common_1.getLogger)(config, 'read-model-adapter#searchReadModel');
49
+ logger.debug('Converting filter to query');
50
+ const queryFor = (0, searcher_adapter_1.queryRecordFor)(filters);
51
+ const query = { ...queryFor, typeName: readModelName };
52
+ logger.debug('Got query ', query);
53
+ const skipId = (afterCursor === null || afterCursor === void 0 ? void 0 : afterCursor.id) ? parseInt(afterCursor === null || afterCursor === void 0 ? void 0 : afterCursor.id) : 0;
54
+ const result = await db.query(query, sortBy, skipId, limit, select);
55
+ logger.debug('Search result: ', result);
56
+ const items = (_a = result === null || result === void 0 ? void 0 : result.map((envelope) => envelope.value)) !== null && _a !== void 0 ? _a : [];
57
+ if (paginatedVersion) {
58
+ return {
59
+ items: items,
60
+ count: (_b = items === null || items === void 0 ? void 0 : items.length) !== null && _b !== void 0 ? _b : 0,
61
+ cursor: { id: ((limit ? limit : 1) + skipId).toString() },
62
+ };
63
+ }
64
+ return items;
65
+ }
66
+ async function deleteReadModel(db, config, readModelName, readModel) {
67
+ const logger = (0, common_1.getLogger)(config, 'read-model-adapter#deleteReadModel');
68
+ logger.debug(`Entering to Read model deleted. ID=${readModel.id}.Name=${readModelName}`);
69
+ try {
70
+ await db.deleteById(readModel.id, readModelName);
71
+ logger.debug(`Read model deleted. ${readModelName} ID = ${readModel.id}`);
72
+ }
73
+ catch (e) {
74
+ logger.warn(`Read model to delete ${readModelName} ID = ${readModel.id} not found`);
75
+ }
76
+ }
@@ -0,0 +1,17 @@
1
+ import { FilterFor } from '@magek/common';
2
+ /**
3
+ * Creates a query record out of the read mode name and
4
+ * the GraphQL filters, ready to be passed into the `query`
5
+ * method of the read model registry.
6
+ */
7
+ export declare function queryRecordFor(filters: FilterFor<any>, nested?: string, queryFromFilters?: Record<string, object>): Record<string, QueryOperation<QueryValue>>;
8
+ export type QueryValue = number | string | boolean;
9
+ export type QueryOperation<TValue> = TValue | {
10
+ [TKey in '$lt' | '$lte' | '$gt' | '$gte' | '$ne' | '$exists']?: TValue;
11
+ } | {
12
+ [TKey in '$in' | '$nin']?: Array<TValue>;
13
+ } | {
14
+ [TKey in '$regex' | '$nin']?: RegExp;
15
+ } | {
16
+ [TKey in '$elemMatch']?: TValue;
17
+ };
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.queryRecordFor = queryRecordFor;
4
+ /**
5
+ * Creates a query record out of the read mode name and
6
+ * the GraphQL filters, ready to be passed into the `query`
7
+ * method of the read model registry.
8
+ */
9
+ function queryRecordFor(filters, nested, queryFromFilters = {}) {
10
+ if (Object.keys(filters).length != 0) {
11
+ for (const key in filters) {
12
+ const propName = nested ? `${nested}.${key}` : key;
13
+ const filter = filters[key];
14
+ switch (key) {
15
+ case 'not':
16
+ queryFromFilters[`$${propName}`] = queryRecordFor(filter);
17
+ break;
18
+ case 'or':
19
+ case 'and':
20
+ queryFromFilters[`$${propName}`] = filters[key].map((filter) => queryRecordFor(filter));
21
+ break;
22
+ default:
23
+ if (!Object.keys(queryOperatorTable).includes(Object.keys(filter)[0])) {
24
+ queryRecordFor(filter, propName, queryFromFilters);
25
+ }
26
+ else {
27
+ queryFromFilters[`value.${propName}`] = filterToQuery(filter);
28
+ }
29
+ break;
30
+ }
31
+ }
32
+ }
33
+ return { ...queryFromFilters };
34
+ }
35
+ /**
36
+ * Transforms a GraphQL Magek filter into an neDB query
37
+ */
38
+ function filterToQuery(filter) {
39
+ const [query] = Object.entries(filter).map(([propName, filter]) => {
40
+ const query = queryOperatorTable[propName];
41
+ const queryFilter = Array.isArray(filter) ? filter : [filter];
42
+ return query(queryFilter);
43
+ });
44
+ return query;
45
+ }
46
+ /**
47
+ * Table of equivalences between a GraphQL operation and the NeDB
48
+ * query operator.
49
+ *
50
+ * It is needed that we pass the values, because of the special case
51
+ * of `=`, in which the operator is the value itself.
52
+ */
53
+ const queryOperatorTable = {
54
+ eq: (values) => values[0],
55
+ ne: (values) => ({ $ne: values[0] }),
56
+ lt: (values) => ({ $lt: values[0] }),
57
+ gt: (values) => ({ $gt: values[0] }),
58
+ lte: (values) => ({ $lte: values[0] }),
59
+ gte: (values) => ({ $gte: values[0] }),
60
+ in: (values) => ({ $in: values }),
61
+ isDefined: (values) => ({ $exists: values[0] }),
62
+ contains: buildRegexQuery.bind(null, 'contains'),
63
+ beginsWith: buildRegexQuery.bind(null, 'begins-with'),
64
+ includes: buildIncludes.bind(null, 'contains'),
65
+ regex: buildRegexQuery.bind(null, 'regex'),
66
+ iRegex: buildRegexQuery.bind(null, 'iRegex'),
67
+ };
68
+ function buildIncludes(operation, values) {
69
+ const matcher = values[0];
70
+ if (typeof matcher === 'string') {
71
+ return { $regex: new RegExp(matcher) };
72
+ }
73
+ return { $elemMatch: matcher };
74
+ }
75
+ /**
76
+ * Builds a regex out of string GraphQL queries
77
+ */
78
+ function buildRegexQuery(operation, values) {
79
+ const matcher = values[0];
80
+ if (typeof matcher != 'string') {
81
+ throw new Error(`Attempted to perform a ${operation} operation on a non-string`);
82
+ }
83
+ if (operation === 'begins-with') {
84
+ return { $regex: new RegExp(`^${matcher}`) };
85
+ }
86
+ if (operation === 'iRegex') {
87
+ return { $regex: new RegExp(matcher, 'i') };
88
+ }
89
+ return { $regex: new RegExp(matcher) };
90
+ }
@@ -0,0 +1 @@
1
+ export declare const readModelsDatabase: string;
package/dist/paths.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readModelsDatabase = void 0;
4
+ const path = require("path");
5
+ exports.readModelsDatabase = internalPath('read_models.json');
6
+ function internalPath(filename) {
7
+ return path.normalize(path.join('.', '.magek', filename));
8
+ }
@@ -0,0 +1,23 @@
1
+ import { ProjectionFor, ReadModelEnvelope, SortFor, UUID } from '@magek/common';
2
+ interface LocalSortedFor {
3
+ [key: string]: number;
4
+ }
5
+ export type NedbError = Error & {
6
+ [key: string | number | symbol]: unknown;
7
+ };
8
+ export declare const UNIQUE_VIOLATED_ERROR_TYPE = "uniqueViolated";
9
+ export declare class ReadModelRegistry {
10
+ readonly readModels: any;
11
+ isLoaded: boolean;
12
+ constructor();
13
+ loadDatabaseIfNeeded(): Promise<void>;
14
+ query(query: object, sortBy?: SortFor<unknown>, skip?: number, limit?: number, select?: ProjectionFor<unknown>): Promise<Array<ReadModelEnvelope>>;
15
+ store(readModel: ReadModelEnvelope, expectedCurrentVersion: number): Promise<void>;
16
+ private insert;
17
+ private update;
18
+ deleteById(id: UUID, typeName: string): Promise<number>;
19
+ toLocalSortFor(sortBy?: SortFor<unknown>, parentKey?: string, sortedList?: LocalSortedFor): undefined | LocalSortedFor;
20
+ filterFields(obj: any, select: string[]): any;
21
+ setNestedValue(result: any, source: any, parts: string[]): void;
22
+ }
23
+ export {};
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ReadModelRegistry = exports.UNIQUE_VIOLATED_ERROR_TYPE = void 0;
4
+ const paths_1 = require("./paths");
5
+ const DataStore = require('@seald-io/nedb');
6
+ exports.UNIQUE_VIOLATED_ERROR_TYPE = 'uniqueViolated';
7
+ class ReadModelRegistry {
8
+ constructor() {
9
+ this.isLoaded = false;
10
+ this.readModels = new DataStore({ filename: paths_1.readModelsDatabase });
11
+ }
12
+ async loadDatabaseIfNeeded() {
13
+ if (!this.isLoaded) {
14
+ this.isLoaded = true;
15
+ await this.readModels.loadDatabaseAsync();
16
+ await this.readModels.ensureIndexAsync({ fieldName: 'uniqueKey', unique: true, sparse: true });
17
+ }
18
+ }
19
+ async query(query, sortBy, skip, limit, select) {
20
+ await this.loadDatabaseIfNeeded();
21
+ let cursor = this.readModels.find(query);
22
+ const sortByList = this.toLocalSortFor(sortBy);
23
+ if (sortByList) {
24
+ cursor = cursor.sort(sortByList);
25
+ }
26
+ if (skip) {
27
+ cursor = cursor.skip(skip);
28
+ }
29
+ if (limit) {
30
+ cursor = cursor.limit(limit);
31
+ }
32
+ // Fetch results from the cursor
33
+ const results = await cursor.execAsync();
34
+ // Process each result to filter the array fields
35
+ if (select && select.length > 0) {
36
+ results.forEach((result) => {
37
+ result.value = this.filterFields(result.value, select);
38
+ });
39
+ }
40
+ return results;
41
+ }
42
+ async store(readModel, expectedCurrentVersion) {
43
+ var _a, _b;
44
+ await this.loadDatabaseIfNeeded();
45
+ const uniqueReadModel = readModel;
46
+ uniqueReadModel.uniqueKey = `${readModel.typeName}_${readModel.value.id}_${(_a = readModel.value.magekMetadata) === null || _a === void 0 ? void 0 : _a.version}`;
47
+ if (((_b = uniqueReadModel.value.magekMetadata) === null || _b === void 0 ? void 0 : _b.version) === 1) {
48
+ return this.insert(readModel);
49
+ }
50
+ return this.update(uniqueReadModel, expectedCurrentVersion);
51
+ }
52
+ async insert(readModel) {
53
+ await this.loadDatabaseIfNeeded();
54
+ await this.readModels.insertAsync(readModel);
55
+ }
56
+ async update(readModel, expectedCurrentVersion) {
57
+ await this.loadDatabaseIfNeeded();
58
+ const { numAffected } = await this.readModels.updateAsync({
59
+ typeName: readModel.typeName,
60
+ 'value.id': readModel.value.id,
61
+ 'value.magekMetadata.version': expectedCurrentVersion,
62
+ }, readModel, { upsert: false, returnUpdatedDocs: true });
63
+ if (numAffected === 0) {
64
+ const error = new Error(`Can't update readModel ${JSON.stringify(readModel)} with expectedCurrentVersion = ${expectedCurrentVersion} . Optimistic concurrency error`);
65
+ error.errorType = exports.UNIQUE_VIOLATED_ERROR_TYPE;
66
+ throw error;
67
+ }
68
+ }
69
+ async deleteById(id, typeName) {
70
+ await this.loadDatabaseIfNeeded();
71
+ return await this.readModels.removeAsync({ typeName: typeName, 'value.id': id }, { multi: false });
72
+ }
73
+ toLocalSortFor(sortBy, parentKey = '', sortedList = Object.create(null)) {
74
+ if (!sortBy || Object.keys(sortBy).length === 0)
75
+ return;
76
+ const elements = sortBy;
77
+ Object.entries(elements).forEach(([key, value]) => {
78
+ if (typeof value === 'string') {
79
+ sortedList[`value.${parentKey}${key}`] = value === 'ASC' ? 1 : -1;
80
+ }
81
+ else {
82
+ this.toLocalSortFor(value, `${parentKey}${key}.`, sortedList);
83
+ }
84
+ });
85
+ return sortedList;
86
+ }
87
+ filterFields(obj, select) {
88
+ const result = Object.create(null);
89
+ select.forEach((field) => {
90
+ const parts = field.split('.');
91
+ this.setNestedValue(result, obj, parts);
92
+ });
93
+ return result;
94
+ }
95
+ setNestedValue(result, source, parts) {
96
+ let currentResult = result;
97
+ let currentSource = source;
98
+ for (let i = 0; i < parts.length; i++) {
99
+ const part = parts[i];
100
+ const isLast = i === parts.length - 1;
101
+ if (part.endsWith('[]')) {
102
+ const arrayField = part.slice(0, -2);
103
+ if (!Array.isArray(currentSource[arrayField])) {
104
+ return;
105
+ }
106
+ if (!currentResult[arrayField]) {
107
+ currentResult[arrayField] = [];
108
+ }
109
+ if (isLast) {
110
+ currentResult[arrayField] = currentSource[arrayField];
111
+ }
112
+ else {
113
+ currentSource[arrayField].forEach((item, index) => {
114
+ if (!currentResult[arrayField][index]) {
115
+ currentResult[arrayField][index] = Object.create(null);
116
+ }
117
+ this.setNestedValue(currentResult[arrayField][index], item, parts.slice(i + 1));
118
+ });
119
+ }
120
+ }
121
+ else {
122
+ if (isLast) {
123
+ if (currentSource[part] !== undefined) {
124
+ currentResult[part] = currentSource[part];
125
+ }
126
+ }
127
+ else {
128
+ if (!currentSource[part]) {
129
+ return;
130
+ }
131
+ if (!currentResult[part]) {
132
+ currentResult[part] = Array.isArray(currentSource[part]) ? [] : Object.create(null);
133
+ }
134
+ currentResult = currentResult[part];
135
+ currentSource = currentSource[part];
136
+ }
137
+ }
138
+ }
139
+ }
140
+ }
141
+ exports.ReadModelRegistry = ReadModelRegistry;
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@magek/adapter-read-model-store-nedb",
3
+ "version": "0.0.1",
4
+ "description": "Nedb-based read model store adapter for the Magek framework",
5
+ "keywords": [
6
+ "read-model-store",
7
+ "nedb"
8
+ ],
9
+ "author": "Boosterin Labs SLU",
10
+ "homepage": "https://magek.ai",
11
+ "license": "Apache-2.0",
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "main": "dist/index.js",
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/theam/magek.git"
22
+ },
23
+ "engines": {
24
+ "node": ">=22.0.0 <23.0.0"
25
+ },
26
+ "dependencies": {
27
+ "@magek/common": "^0.0.1",
28
+ "@seald-io/nedb": "4.1.2",
29
+ "tslib": "2.8.1"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/theam/magek/issues"
33
+ },
34
+ "devDependencies": {
35
+ "@magek/eslint-config": "^0.0.1",
36
+ "@types/chai": "5.2.3",
37
+ "@types/chai-as-promised": "8.0.2",
38
+ "@types/mocha": "10.0.10",
39
+ "@types/node": "22.19.3",
40
+ "@types/sinon": "21.0.0",
41
+ "@types/sinon-chai": "4.0.0",
42
+ "chai": "6.2.2",
43
+ "chai-as-promised": "8.0.2",
44
+ "@faker-js/faker": "10.2.0",
45
+ "mocha": "11.7.5",
46
+ "c8": "^10.1.3",
47
+ "rimraf": "6.1.2",
48
+ "sinon": "21.0.1",
49
+ "sinon-chai": "4.0.1",
50
+ "tsx": "^4.19.2",
51
+ "typescript": "5.9.3"
52
+ },
53
+ "scripts": {
54
+ "format": "prettier --write --ext '.js,.ts' **/*.ts **/*/*.ts",
55
+ "lint:check": "eslint \"**/*.ts\"",
56
+ "lint:fix": "eslint --quiet --fix \"**/*.ts\"",
57
+ "build": "tsc -b tsconfig.json",
58
+ "clean": "rimraf ./dist tsconfig.tsbuildinfo",
59
+ "test": "tsc --noEmit -p tsconfig.test.json && c8 mocha --forbid-only \"test/**/*.test.ts\""
60
+ }
61
+ }