@furystack/mongodb-store 10.1.7 → 11.0.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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [11.0.0] - 2026-04-25
4
+
5
+ ### 💥 Breaking Changes
6
+
7
+ Stores are now first-class DI tokens. See the [v7 migration guide](../../docs/migrations/v7-functional-di.md) for rationale, recipes, and pitfalls.
8
+
9
+ - Removed `useMongoDb(...)`. Declare the store at module scope with `defineMongoDbStore<T, PK>({ name, model, primaryKey, url, db, collection, options? })`.
10
+ - `MongoClientFactory` is now an exported interface + `defineService({ lifetime: 'singleton' })` token. It pools `MongoClient` instances per URL and closes every pooled client on injector teardown.
11
+ - Dropped the unused `EventHub<{ onClientCreated, onDisposed }>` surface from the factory (no consumers in-repo).
12
+
3
13
  ## [10.1.7] - 2026-04-17
4
14
 
5
15
  ### ⬆️ Dependencies
package/README.md CHANGED
@@ -12,19 +12,22 @@ yarn add @furystack/mongodb-store
12
12
 
13
13
  ## Initialization
14
14
 
15
+ `defineMongoDbStore` mints a `StoreToken` backed by a MongoDB collection.
16
+ The shared `MongoClientFactory` singleton pools `MongoClient` instances by
17
+ URL and closes them when the owning injector is disposed.
18
+
15
19
  ```ts
16
- import { Injector } from '@furystack/inject'
17
- import { StoreManager } from '@furystack/core'
18
- import { useMongoDb } from '@furystack/mongodb-store'
20
+ import { createInjector } from '@furystack/inject'
21
+ import { defineMongoDbStore } from '@furystack/mongodb-store'
22
+ import { defineDataSet } from '@furystack/repository'
19
23
 
20
24
  export class TestEntry {
21
25
  declare _id: string
22
26
  declare value: string
23
27
  }
24
28
 
25
- const myInjector = new Injector()
26
- useMongoDb({
27
- injector: myInjector,
29
+ export const TestEntryStore = defineMongoDbStore<TestEntry, '_id'>({
30
+ name: 'my-app/TestEntryStore',
28
31
  model: TestEntry,
29
32
  primaryKey: '_id',
30
33
  url: 'mongodb://localhost:27017',
@@ -32,7 +35,17 @@ useMongoDb({
32
35
  collection: 'TestEntries',
33
36
  })
34
37
 
35
- const myMongoStore = myInjector.getInstance(StoreManager).getStoreFor(TestEntry, '_id')
38
+ export const TestEntryDataSet = defineDataSet({
39
+ name: 'my-app/TestEntryDataSet',
40
+ store: TestEntryStore,
41
+ })
42
+
43
+ const myInjector = createInjector()
44
+ const dataSet = myInjector.get(TestEntryDataSet)
36
45
  ```
37
46
 
38
- > **Tip:** For application-level data access, wrap the physical store with a Repository DataSet using `getRepository(injector).createDataSet(Model, 'primaryKey')` and then use `getDataSetFor(injector, Model, 'primaryKey')` from `@furystack/repository`. This ensures authorization, hooks, and entity sync events are properly triggered.
47
+ > **Tip:** For application-level data access, always go through a
48
+ > `DataSetToken` rather than resolving the `StoreToken` directly. The
49
+ > DataSet layer runs authorization, modification hooks, and entity-sync
50
+ > events; a direct store access skips all of them. The
51
+ > `furystack/no-direct-store-token` lint rule guards against this.
@@ -0,0 +1,43 @@
1
+ import type { Constructable, StoreToken } from '@furystack/core';
2
+ import type { MongoClientOptions } from 'mongodb';
3
+ /**
4
+ * Options accepted by {@link defineMongoDbStore}.
5
+ */
6
+ export type DefineMongoDbStoreOptions<T extends object, TPrimaryKey extends keyof T> = {
7
+ /** Human-readable token name used for debug/readability. */
8
+ name: string;
9
+ /** Entity model constructor. */
10
+ model: Constructable<T>;
11
+ /** Primary key field name. */
12
+ primaryKey: TPrimaryKey;
13
+ /** MongoDB deployment URL (e.g. `mongodb://localhost:27017`). */
14
+ url: string;
15
+ /** MongoDB database name. */
16
+ db: string;
17
+ /** MongoDB collection name. */
18
+ collection: string;
19
+ /** Optional {@link MongoClientOptions}. */
20
+ options?: MongoClientOptions;
21
+ };
22
+ /**
23
+ * Mints a singleton {@link StoreToken} backed by a {@link MongodbStore}.
24
+ *
25
+ * Internally resolves the singleton {@link MongoClientFactory} so every store
26
+ * declared against the same `url` shares one {@link MongoClient}. Declare the
27
+ * token once at module scope — inline calls mint new identities and defeat
28
+ * singleton caching.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * export const UserStore = defineMongoDbStore({
33
+ * name: 'my-app/UserStore',
34
+ * model: User,
35
+ * primaryKey: 'username',
36
+ * url: 'mongodb://localhost:27017',
37
+ * db: 'my-app',
38
+ * collection: 'users',
39
+ * })
40
+ * ```
41
+ */
42
+ export declare const defineMongoDbStore: <T extends object, const TPrimaryKey extends keyof T>(options: DefineMongoDbStoreOptions<T, TPrimaryKey>) => StoreToken<T, TPrimaryKey>;
43
+ //# sourceMappingURL=define-mongodb-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-mongodb-store.d.ts","sourceRoot":"","sources":["../src/define-mongodb-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEhE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAIjD;;GAEG;AACH,MAAM,MAAM,yBAAyB,CAAC,CAAC,SAAS,MAAM,EAAE,WAAW,SAAS,MAAM,CAAC,IAAI;IACrF,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAA;IACZ,gCAAgC;IAChC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;IACvB,8BAA8B;IAC9B,UAAU,EAAE,WAAW,CAAA;IACvB,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAA;IACX,6BAA6B;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,+BAA+B;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,kBAAkB,CAAA;CAC7B,CAAA;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,MAAM,EAAE,KAAK,CAAC,WAAW,SAAS,MAAM,CAAC,EACpF,SAAS,yBAAyB,CAAC,CAAC,EAAE,WAAW,CAAC,KACjD,UAAU,CAAC,CAAC,EAAE,WAAW,CAexB,CAAA"}
@@ -0,0 +1,39 @@
1
+ import { defineStore } from '@furystack/core';
2
+ import { MongoClientFactory } from './mongo-client-factory.js';
3
+ import { MongodbStore } from './mongodb-store.js';
4
+ /**
5
+ * Mints a singleton {@link StoreToken} backed by a {@link MongodbStore}.
6
+ *
7
+ * Internally resolves the singleton {@link MongoClientFactory} so every store
8
+ * declared against the same `url` shares one {@link MongoClient}. Declare the
9
+ * token once at module scope — inline calls mint new identities and defeat
10
+ * singleton caching.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * export const UserStore = defineMongoDbStore({
15
+ * name: 'my-app/UserStore',
16
+ * model: User,
17
+ * primaryKey: 'username',
18
+ * url: 'mongodb://localhost:27017',
19
+ * db: 'my-app',
20
+ * collection: 'users',
21
+ * })
22
+ * ```
23
+ */
24
+ export const defineMongoDbStore = (options) => defineStore({
25
+ name: options.name,
26
+ model: options.model,
27
+ primaryKey: options.primaryKey,
28
+ factory: ({ inject }) => {
29
+ const factory = inject(MongoClientFactory);
30
+ return new MongodbStore({
31
+ model: options.model,
32
+ primaryKey: options.primaryKey,
33
+ db: options.db,
34
+ collection: options.collection,
35
+ mongoClient: () => factory.getClientFor(options.url, options.options),
36
+ });
37
+ },
38
+ });
39
+ //# sourceMappingURL=define-mongodb-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-mongodb-store.js","sourceRoot":"","sources":["../src/define-mongodb-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAE7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAsBjD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,OAAkD,EACtB,EAAE,CAC9B,WAAW,CAAiB;IAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;IAClB,KAAK,EAAE,OAAO,CAAC,KAAK;IACpB,UAAU,EAAE,OAAO,CAAC,UAAU;IAC9B,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;QACtB,MAAM,OAAO,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAA;QAC1C,OAAO,IAAI,YAAY,CAAiB;YACtC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC;SACtE,CAAC,CAAA;IACJ,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=define-mongodb-store.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-mongodb-store.spec.d.ts","sourceRoot":"","sources":["../src/define-mongodb-store.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,93 @@
1
+ import { createInjector } from '@furystack/inject';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+ import { defineMongoDbStore } from './define-mongodb-store.js';
4
+ import { MongoClientFactory } from './mongo-client-factory.js';
5
+ import { MongodbStore } from './mongodb-store.js';
6
+ class Item {
7
+ }
8
+ describe('defineMongoDbStore (metadata)', () => {
9
+ it('propagates name, lifetime, model and primaryKey onto the returned token', () => {
10
+ const token = defineMongoDbStore({
11
+ name: 'test/MongoItems',
12
+ model: Item,
13
+ primaryKey: 'id',
14
+ url: 'mongodb://localhost:27017',
15
+ db: 'unit-test',
16
+ collection: 'items',
17
+ });
18
+ expect(token.name).toBe('test/MongoItems');
19
+ expect(token.lifetime).toBe('singleton');
20
+ expect(token.model).toBe(Item);
21
+ expect(token.primaryKey).toBe('id');
22
+ });
23
+ it('mints a distinct token identity per call even when options match -- declare once at module scope', () => {
24
+ const options = {
25
+ name: 'test/MongoDupe',
26
+ model: Item,
27
+ primaryKey: 'id',
28
+ url: 'mongodb://localhost:27017',
29
+ db: 'unit-test',
30
+ collection: 'items',
31
+ };
32
+ const a = defineMongoDbStore(options);
33
+ const b = defineMongoDbStore(options);
34
+ expect(a.id).not.toBe(b.id);
35
+ });
36
+ });
37
+ describe('defineMongoDbStore (factory + disposal)', () => {
38
+ const buildClientStub = () => {
39
+ const createIndex = vi.fn(() => Promise.resolve('id_1'));
40
+ const collection = vi.fn(() => ({ createIndex }));
41
+ const db = vi.fn(() => ({ collection }));
42
+ const client = { db };
43
+ return { client, db, collection, createIndex };
44
+ };
45
+ it('instantiates a MongodbStore whose lazy client pulls from the injected MongoClientFactory with the right url', async () => {
46
+ const { client, db, collection, createIndex } = buildClientStub();
47
+ const getClientFor = vi.fn(() => client);
48
+ const Token = defineMongoDbStore({
49
+ name: 'test/MongoFactory',
50
+ model: Item,
51
+ primaryKey: 'id',
52
+ url: 'mongodb://stub-host:1234',
53
+ db: 'unit-db',
54
+ collection: 'unit-coll',
55
+ options: { appName: 'spec' },
56
+ });
57
+ const injector = createInjector();
58
+ injector.bind(MongoClientFactory, () => ({ getClientFor }));
59
+ try {
60
+ const store = injector.get(Token);
61
+ expect(store).toBeInstanceOf(MongodbStore);
62
+ expect(store.model).toBe(Item);
63
+ expect(store.primaryKey).toBe('id');
64
+ expect(getClientFor).not.toHaveBeenCalled();
65
+ await store.getCollection();
66
+ expect(getClientFor).toHaveBeenCalledTimes(1);
67
+ expect(getClientFor).toHaveBeenCalledWith('mongodb://stub-host:1234', { appName: 'spec' });
68
+ expect(db).toHaveBeenCalledWith('unit-db');
69
+ expect(collection).toHaveBeenCalledWith('unit-coll');
70
+ expect(createIndex).toHaveBeenCalledWith({ id: 1 }, { unique: true });
71
+ }
72
+ finally {
73
+ await injector[Symbol.asyncDispose]();
74
+ }
75
+ });
76
+ it('disposing the injector tears down the bound MongoClientFactory', async () => {
77
+ const { client } = buildClientStub();
78
+ const getClientFor = vi.fn(() => client);
79
+ const Token = defineMongoDbStore({
80
+ name: 'test/MongoDispose',
81
+ model: Item,
82
+ primaryKey: 'id',
83
+ url: 'mongodb://stub-host:1234',
84
+ db: 'unit-db',
85
+ collection: 'unit-coll',
86
+ });
87
+ const injector = createInjector();
88
+ injector.bind(MongoClientFactory, () => ({ getClientFor }));
89
+ injector.get(Token);
90
+ await expect(injector[Symbol.asyncDispose]()).resolves.toBeUndefined();
91
+ });
92
+ });
93
+ //# sourceMappingURL=define-mongodb-store.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-mongodb-store.spec.js","sourceRoot":"","sources":["../src/define-mongodb-store.spec.ts"],"names":[],"mappings":"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"}
package/esm/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './define-mongodb-store.js';
2
+ export * from './mongo-client-factory.js';
1
3
  export * from './mongodb-store.js';
2
- export * from './store-manager-helpers.js';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAA;AAClC,cAAc,4BAA4B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,oBAAoB,CAAA"}
package/esm/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './define-mongodb-store.js';
2
+ export * from './mongo-client-factory.js';
1
3
  export * from './mongodb-store.js';
2
- export * from './store-manager-helpers.js';
3
4
  //# sourceMappingURL=index.js.map
package/esm/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAA;AAClC,cAAc,4BAA4B,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,oBAAoB,CAAA"}
@@ -1,22 +1,20 @@
1
- import { EventHub, type ListenerErrorPayload } from '@furystack/utils';
1
+ import type { Token } from '@furystack/inject';
2
2
  import type { MongoClientOptions } from 'mongodb';
3
3
  import { MongoClient } from 'mongodb';
4
4
  /**
5
- * Events emitted by the {@link MongoClientFactory}
5
+ * Pools {@link MongoClient} instances by URL so every store that targets the
6
+ * same MongoDB deployment shares a single connection.
7
+ *
8
+ * Application code rarely calls this directly — {@link defineMongoDbStore}
9
+ * resolves the factory token internally. Exported for tests and advanced
10
+ * integrations that need raw client access (e.g. dropping a test database).
6
11
  */
7
- export type MongoClientFactoryEvents = {
8
- onClientCreated: {
9
- url: string;
10
- };
11
- onDisposed: undefined;
12
- onListenerError: ListenerErrorPayload;
13
- };
14
- /**
15
- * Factory for instantiating MongoDb clients
16
- */
17
- export declare class MongoClientFactory extends EventHub<MongoClientFactoryEvents> implements AsyncDisposable {
18
- private connections;
19
- [Symbol.asyncDispose](): Promise<void>;
12
+ export interface MongoClientFactory {
20
13
  getClientFor(url: string, options?: MongoClientOptions): MongoClient;
21
14
  }
15
+ /**
16
+ * Singleton token for the {@link MongoClientFactory}. Closes every pooled
17
+ * client when the owning injector is disposed.
18
+ */
19
+ export declare const MongoClientFactory: Token<MongoClientFactory, 'singleton'>;
22
20
  //# sourceMappingURL=mongo-client-factory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mongo-client-factory.d.ts","sourceRoot":"","sources":["../src/mongo-client-factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AACtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAErC;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,eAAe,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IAChC,UAAU,EAAE,SAAS,CAAA;IACrB,eAAe,EAAE,oBAAoB,CAAA;CACtC,CAAA;AAED;;GAEG;AACH,qBACa,kBAAmB,SAAQ,QAAQ,CAAC,wBAAwB,CAAE,YAAW,eAAe;IACnG,OAAO,CAAC,WAAW,CAAsC;IAE5C,CAAC,MAAM,CAAC,YAAY,CAAC;IAO3B,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB;CAW9D"}
1
+ {"version":3,"file":"mongo-client-factory.d.ts","sourceRoot":"","sources":["../src/mongo-client-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAE9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAErC;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IACjC,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,WAAW,CAAA;CACrE;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,kBAAkB,EAAE,WAAW,CAqBpE,CAAA"}
@@ -1,36 +1,29 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- 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;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- };
7
- import { Injectable } from '@furystack/inject';
8
- import { EventHub } from '@furystack/utils';
1
+ import { defineService } from '@furystack/inject';
9
2
  import { MongoClient } from 'mongodb';
10
3
  /**
11
- * Factory for instantiating MongoDb clients
4
+ * Singleton token for the {@link MongoClientFactory}. Closes every pooled
5
+ * client when the owning injector is disposed.
12
6
  */
13
- let MongoClientFactory = class MongoClientFactory extends EventHub {
14
- connections = new Map();
15
- async [Symbol.asyncDispose]() {
16
- await Promise.all([...this.connections.values()].map((c) => c.close(true)));
17
- this.connections.clear();
18
- this.emit('onDisposed', undefined);
19
- super[Symbol.dispose]();
20
- }
21
- getClientFor(url, options) {
22
- const existing = this.connections.get(url);
23
- if (existing) {
24
- return existing;
25
- }
26
- const client = new MongoClient(url, options);
27
- this.connections.set(url, client);
28
- this.emit('onClientCreated', { url });
29
- return client;
30
- }
31
- };
32
- MongoClientFactory = __decorate([
33
- Injectable({ lifetime: 'singleton' })
34
- ], MongoClientFactory);
35
- export { MongoClientFactory };
7
+ export const MongoClientFactory = defineService({
8
+ name: '@furystack/mongodb-store/MongoClientFactory',
9
+ lifetime: 'singleton',
10
+ factory: ({ onDispose }) => {
11
+ const connections = new Map();
12
+ onDispose(async () => {
13
+ await Promise.all([...connections.values()].map((c) => c.close(true)));
14
+ connections.clear();
15
+ });
16
+ return {
17
+ getClientFor(url, options) {
18
+ const existing = connections.get(url);
19
+ if (existing) {
20
+ return existing;
21
+ }
22
+ const client = new MongoClient(url, options);
23
+ connections.set(url, client);
24
+ return client;
25
+ },
26
+ };
27
+ },
28
+ });
36
29
  //# sourceMappingURL=mongo-client-factory.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mongo-client-factory.js","sourceRoot":"","sources":["../src/mongo-client-factory.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAA6B,MAAM,kBAAkB,CAAA;AAEtE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAWrC;;GAEG;AAEI,IAAM,kBAAkB,GAAxB,MAAM,kBAAmB,SAAQ,QAAkC;IAChE,WAAW,GAA6B,IAAI,GAAG,EAAE,CAAA;IAElD,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;QAChC,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC3E,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAA;QACxB,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;QAClC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAA;IACzB,CAAC;IAEM,YAAY,CAAC,GAAW,EAAE,OAA4B;QAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC1C,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,QAAQ,CAAA;QACjB,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAC5C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;QACjC,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,GAAG,EAAE,CAAC,CAAA;QACrC,OAAO,MAAM,CAAA;IACf,CAAC;CACF,CAAA;AArBY,kBAAkB;IAD9B,UAAU,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;GACzB,kBAAkB,CAqB9B"}
1
+ {"version":3,"file":"mongo-client-factory.js","sourceRoot":"","sources":["../src/mongo-client-factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEjD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAcrC;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAA2C,aAAa,CAAC;IACtF,IAAI,EAAE,6CAA6C;IACnD,QAAQ,EAAE,WAAW;IACrB,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;QACzB,MAAM,WAAW,GAAG,IAAI,GAAG,EAAuB,CAAA;QAClD,SAAS,CAAC,KAAK,IAAI,EAAE;YACnB,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACtE,WAAW,CAAC,KAAK,EAAE,CAAA;QACrB,CAAC,CAAC,CAAA;QACF,OAAO;YACL,YAAY,CAAC,GAAW,EAAE,OAA4B;gBACpD,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;gBACrC,IAAI,QAAQ,EAAE,CAAC;oBACb,OAAO,QAAQ,CAAA;gBACjB,CAAC;gBACD,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;gBAC5C,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;gBAC5B,OAAO,MAAM,CAAA;YACf,CAAC;SACF,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -1,5 +1,4 @@
1
- import type { CreateResult, FilterType, FindOptions, PartialResult, PhysicalStore, WithOptionalId } from '@furystack/core';
2
- import type { Constructable } from '@furystack/inject';
1
+ import type { Constructable, CreateResult, FilterType, FindOptions, PartialResult, PhysicalStore, WithOptionalId } from '@furystack/core';
3
2
  import { EventHub } from '@furystack/utils';
4
3
  import type { Collection, MongoClient } from 'mongodb';
5
4
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"mongodb-store.d.ts","sourceRoot":"","sources":["../src/mongodb-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,aAAa,EACb,aAAa,EACb,cAAc,EACf,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAU,WAAW,EAAgD,MAAM,SAAS,CAAA;AAQ5G;;GAEG;AACH,qBAAa,YAAY,CACvB,CAAC,SAAS,MAAM,EAChB,WAAW,SAAS,MAAM,CAAC,EAC3B,cAAc,GAAG,cAAc,CAAC,CAAC,EAAE,WAAW,CAAC,CAE/C,SAAQ,QAAQ,CAAC;IACf,aAAa,EAAE;QAAE,MAAM,EAAE,CAAC,CAAA;KAAE,CAAA;IAC5B,eAAe,EAAE;QAAE,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;KAAE,CAAA;IAC3D,eAAe,EAAE;QAAE,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC,CAAA;KAAE,CAAA;CACzC,CACD,YAAW,aAAa,CAAC,CAAC,EAAE,WAAW,EAAE,cAAc,CAAC;IA+EtD,OAAO,CAAC,QAAQ,CAAC,OAAO;IA7E1B,SAAgB,UAAU,EAAE,WAAW,CAAA;IACvC,SAAgB,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;IACvC,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,UAAU,CAAC,CAAe;IAElC,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,WAAW;IA0BN,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YAUtC,oBAAoB;gBAgBf,OAAO,EAAE;QACxB,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;QACvB,UAAU,EAAE,WAAW,CAAA;QACvB,EAAE,EAAE,MAAM,CAAA;QACV,UAAU,EAAE,MAAM,CAAA;QAClB,WAAW,EAAE,MAAM,WAAW,CAAA;KAC/B;IAOU,GAAG,CAAC,GAAG,OAAO,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAmB3D,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAS3D,KAAK,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAK9C,IAAI,CAAC,OAAO,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,EAC9C,MAAM,EAAE,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAe5C,OAAO,CAAC,aAAa;IAOR,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAOzE,MAAM,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAKnE"}
1
+ {"version":3,"file":"mongodb-store.d.ts","sourceRoot":"","sources":["../src/mongodb-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,WAAW,EACX,aAAa,EACb,aAAa,EACb,cAAc,EACf,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAU,WAAW,EAAgD,MAAM,SAAS,CAAA;AAQ5G;;GAEG;AACH,qBAAa,YAAY,CACvB,CAAC,SAAS,MAAM,EAChB,WAAW,SAAS,MAAM,CAAC,EAC3B,cAAc,GAAG,cAAc,CAAC,CAAC,EAAE,WAAW,CAAC,CAE/C,SAAQ,QAAQ,CAAC;IACf,aAAa,EAAE;QAAE,MAAM,EAAE,CAAC,CAAA;KAAE,CAAA;IAC5B,eAAe,EAAE;QAAE,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;KAAE,CAAA;IAC3D,eAAe,EAAE;QAAE,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC,CAAA;KAAE,CAAA;CACzC,CACD,YAAW,aAAa,CAAC,CAAC,EAAE,WAAW,EAAE,cAAc,CAAC;IA+EtD,OAAO,CAAC,QAAQ,CAAC,OAAO;IA7E1B,SAAgB,UAAU,EAAE,WAAW,CAAA;IACvC,SAAgB,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;IACvC,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,UAAU,CAAC,CAAe;IAElC,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,WAAW;IA0BN,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YAUtC,oBAAoB;gBAgBf,OAAO,EAAE;QACxB,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;QACvB,UAAU,EAAE,WAAW,CAAA;QACvB,EAAE,EAAE,MAAM,CAAA;QACV,UAAU,EAAE,MAAM,CAAA;QAClB,WAAW,EAAE,MAAM,WAAW,CAAA;KAC/B;IAOU,GAAG,CAAC,GAAG,OAAO,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAmB3D,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAS3D,KAAK,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAK9C,IAAI,CAAC,OAAO,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,EAC9C,MAAM,EAAE,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAe5C,OAAO,CAAC,aAAa;IAOR,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAOzE,MAAM,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAKnE"}
@@ -1,18 +1,16 @@
1
- import { StoreManager } from '@furystack/core';
2
1
  import { TestClass, createStoreTest } from '@furystack/core/create-physical-store-tests';
3
- import { Injector } from '@furystack/inject';
2
+ import { createInjector } from '@furystack/inject';
4
3
  import { usingAsync } from '@furystack/utils';
5
- import { describe, expect, it, vi } from 'vitest';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { defineMongoDbStore } from './define-mongodb-store.js';
6
6
  import { MongoClientFactory } from './mongo-client-factory.js';
7
- import { useMongoDb } from './store-manager-helpers.js';
8
7
  class TestClassWithId {
9
8
  }
10
9
  let storeCount = 0;
11
10
  const mongoDbUrl = process?.env?.MONGODB_URL || 'mongodb://localhost:27017';
12
11
  describe('MongoDB Store', () => {
13
12
  let dbIdx = 0;
14
- const getMongoOptions = (injector) => ({
15
- injector,
13
+ const getMongoOptions = () => ({
16
14
  model: TestClass,
17
15
  primaryKey: 'id',
18
16
  collection: `furystack-mongo-store-tests-${storeCount++}`,
@@ -24,12 +22,15 @@ describe('MongoDB Store', () => {
24
22
  typeName: 'mongodb-store',
25
23
  skipStringTests: true,
26
24
  createStore: (i) => {
27
- const mongoOptions = getMongoOptions(i);
28
- useMongoDb(mongoOptions);
29
- const store = i.getInstance(StoreManager).getStoreFor(TestClass, 'id');
25
+ const mongoOptions = getMongoOptions();
26
+ const token = defineMongoDbStore({
27
+ name: `mongodb-store/${mongoOptions.collection}`,
28
+ ...mongoOptions,
29
+ });
30
+ const store = i.get(token);
30
31
  const oldDispose = i[Symbol.asyncDispose];
31
32
  i[Symbol.asyncDispose] = async () => {
32
- const client = i.getInstance(MongoClientFactory).getClientFor(mongoOptions.url, mongoOptions.options);
33
+ const client = i.get(MongoClientFactory).getClientFor(mongoOptions.url, mongoOptions.options);
33
34
  const db = client.db(mongoOptions.db);
34
35
  await db.dropDatabase();
35
36
  await oldDispose.call(i);
@@ -38,19 +39,22 @@ describe('MongoDB Store', () => {
38
39
  },
39
40
  });
40
41
  const createTestClassWithIdStore = (injector) => {
41
- const mongoOptions = { ...getMongoOptions(injector), model: TestClassWithId, primaryKey: '_id' };
42
- useMongoDb(mongoOptions);
42
+ const mongoOptions = { ...getMongoOptions(), model: TestClassWithId, primaryKey: '_id' };
43
+ const token = defineMongoDbStore({
44
+ name: `mongodb-store/${mongoOptions.collection}`,
45
+ ...mongoOptions,
46
+ });
43
47
  const oldDispose = injector[Symbol.asyncDispose];
44
48
  injector[Symbol.asyncDispose] = async () => {
45
- const client = injector.getInstance(MongoClientFactory).getClientFor(mongoOptions.url, mongoOptions.options);
49
+ const client = injector.get(MongoClientFactory).getClientFor(mongoOptions.url, mongoOptions.options);
46
50
  const db = client.db(mongoOptions.db);
47
51
  await db.dropDatabase();
48
52
  await oldDispose.call(injector);
49
53
  };
50
- return injector.getInstance(StoreManager).getStoreFor(TestClassWithId, '_id');
54
+ return injector.get(token);
51
55
  };
52
56
  it('Should retrieve an entity with its id', async () => {
53
- await usingAsync(new Injector(), async (injector) => {
57
+ await usingAsync(createInjector(), async (injector) => {
54
58
  const store = createTestClassWithIdStore(injector);
55
59
  const { created } = await store.add({ value: 'value1' });
56
60
  expect(typeof created[0]._id).toBe('string');
@@ -59,7 +63,7 @@ describe('MongoDB Store', () => {
59
63
  });
60
64
  });
61
65
  it('Should retrieve more entities with theis ids', async () => {
62
- await usingAsync(new Injector(), async (injector) => {
66
+ await usingAsync(createInjector(), async (injector) => {
63
67
  const store = createTestClassWithIdStore(injector);
64
68
  const { created } = await store.add({ value: 'value1' }, { value: 'value2' }, { value: 'value3' });
65
69
  const retrieved = await store.find({ filter: { _id: { $in: created.map((c) => c._id) } } });
@@ -68,7 +72,7 @@ describe('MongoDB Store', () => {
68
72
  });
69
73
  });
70
74
  it('Should filter by _id as a string', async () => {
71
- await usingAsync(new Injector(), async (injector) => {
75
+ await usingAsync(createInjector(), async (injector) => {
72
76
  const store = createTestClassWithIdStore(injector);
73
77
  const { created } = await store.add({ value: 'value1' });
74
78
  const retrieved = await store.find({ filter: { _id: { $eq: created[0]._id } } });
@@ -77,7 +81,7 @@ describe('MongoDB Store', () => {
77
81
  });
78
82
  });
79
83
  it('Should filter by _id.$eq as a string', async () => {
80
- await usingAsync(new Injector(), async (injector) => {
84
+ await usingAsync(createInjector(), async (injector) => {
81
85
  const store = createTestClassWithIdStore(injector);
82
86
  const { created } = await store.add({ value: 'value1' });
83
87
  const retrieved = await store.find({ filter: { _id: { $eq: created[0]._id } } });
@@ -86,7 +90,7 @@ describe('MongoDB Store', () => {
86
90
  });
87
91
  });
88
92
  it('Should filter by _id.$in as array of strings', async () => {
89
- await usingAsync(new Injector(), async (injector) => {
93
+ await usingAsync(createInjector(), async (injector) => {
90
94
  const store = createTestClassWithIdStore(injector);
91
95
  const { created } = await store.add({ value: 'value1' }, { value: 'value2' });
92
96
  const ids = created.map((c) => c._id);
@@ -96,7 +100,7 @@ describe('MongoDB Store', () => {
96
100
  });
97
101
  });
98
102
  it('Should filter by _id.$nin as array of strings', async () => {
99
- await usingAsync(new Injector(), async (injector) => {
103
+ await usingAsync(createInjector(), async (injector) => {
100
104
  const store = createTestClassWithIdStore(injector);
101
105
  const { created } = await store.add({ value: 'value1' }, { value: 'value2' });
102
106
  const ids = created.map((c) => c._id);
@@ -106,7 +110,7 @@ describe('MongoDB Store', () => {
106
110
  });
107
111
  });
108
112
  it('Should filter by a non-_id field', async () => {
109
- await usingAsync(new Injector(), async (injector) => {
113
+ await usingAsync(createInjector(), async (injector) => {
110
114
  const store = createTestClassWithIdStore(injector);
111
115
  await store.add({ value: 'value1' }, { value: 'value2' });
112
116
  const retrieved = await store.find({ filter: { value: { $eq: 'value2' } } });
@@ -115,32 +119,4 @@ describe('MongoDB Store', () => {
115
119
  });
116
120
  });
117
121
  });
118
- describe('MongoClientFactory EventHub', () => {
119
- it('should emit onClientCreated when a new client is created', async () => {
120
- const factory = new MongoClientFactory();
121
- try {
122
- const handler = vi.fn();
123
- factory.addListener('onClientCreated', handler);
124
- factory.getClientFor('mongodb://localhost:27017/test-db');
125
- expect(handler).toHaveBeenCalledTimes(1);
126
- expect(handler).toHaveBeenCalledWith({ url: 'mongodb://localhost:27017/test-db' });
127
- }
128
- finally {
129
- await factory[Symbol.asyncDispose]();
130
- }
131
- });
132
- it('should not emit onClientCreated when returning an existing client', async () => {
133
- const factory = new MongoClientFactory();
134
- try {
135
- const handler = vi.fn();
136
- factory.getClientFor('mongodb://localhost:27017/test-db');
137
- factory.addListener('onClientCreated', handler);
138
- factory.getClientFor('mongodb://localhost:27017/test-db');
139
- expect(handler).not.toHaveBeenCalled();
140
- }
141
- finally {
142
- await factory[Symbol.asyncDispose]();
143
- }
144
- });
145
- });
146
122
  //# sourceMappingURL=mongodb-store.spec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mongodb-store.spec.js","sourceRoot":"","sources":["../src/mongodb-store.spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mongodb-store.spec.js","sourceRoot":"","sources":["../src/mongodb-store.spec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@furystack/mongodb-store",
3
- "version": "10.1.7",
3
+ "version": "11.0.0",
4
4
  "description": "MongoDB Store implementation for FuryStack",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -38,9 +38,9 @@
38
38
  },
39
39
  "homepage": "https://github.com/furystack/furystack",
40
40
  "dependencies": {
41
- "@furystack/core": "^16.0.4",
42
- "@furystack/inject": "^12.0.36",
43
- "@furystack/utils": "^8.2.5",
41
+ "@furystack/core": "^17.0.0",
42
+ "@furystack/inject": "^13.0.0",
43
+ "@furystack/utils": "^9.0.0",
44
44
  "mongodb": "7.1.1"
45
45
  },
46
46
  "devDependencies": {
@@ -0,0 +1,108 @@
1
+ import { createInjector } from '@furystack/inject'
2
+ import type { MongoClient } from 'mongodb'
3
+ import { describe, expect, it, vi } from 'vitest'
4
+ import { defineMongoDbStore } from './define-mongodb-store.js'
5
+ import { MongoClientFactory } from './mongo-client-factory.js'
6
+ import { MongodbStore } from './mongodb-store.js'
7
+
8
+ class Item {
9
+ declare id: string
10
+ declare value: string
11
+ }
12
+
13
+ describe('defineMongoDbStore (metadata)', () => {
14
+ it('propagates name, lifetime, model and primaryKey onto the returned token', () => {
15
+ const token = defineMongoDbStore<Item, 'id'>({
16
+ name: 'test/MongoItems',
17
+ model: Item,
18
+ primaryKey: 'id',
19
+ url: 'mongodb://localhost:27017',
20
+ db: 'unit-test',
21
+ collection: 'items',
22
+ })
23
+ expect(token.name).toBe('test/MongoItems')
24
+ expect(token.lifetime).toBe('singleton')
25
+ expect(token.model).toBe(Item)
26
+ expect(token.primaryKey).toBe('id')
27
+ })
28
+
29
+ it('mints a distinct token identity per call even when options match -- declare once at module scope', () => {
30
+ const options = {
31
+ name: 'test/MongoDupe',
32
+ model: Item,
33
+ primaryKey: 'id' as const,
34
+ url: 'mongodb://localhost:27017',
35
+ db: 'unit-test',
36
+ collection: 'items',
37
+ }
38
+ const a = defineMongoDbStore<Item, 'id'>(options)
39
+ const b = defineMongoDbStore<Item, 'id'>(options)
40
+ expect(a.id).not.toBe(b.id)
41
+ })
42
+ })
43
+
44
+ describe('defineMongoDbStore (factory + disposal)', () => {
45
+ const buildClientStub = () => {
46
+ const createIndex = vi.fn<() => Promise<string>>(() => Promise.resolve('id_1'))
47
+ const collection = vi.fn(() => ({ createIndex }))
48
+ const db = vi.fn(() => ({ collection }))
49
+ const client = { db } as unknown as MongoClient
50
+ return { client, db, collection, createIndex }
51
+ }
52
+
53
+ it('instantiates a MongodbStore whose lazy client pulls from the injected MongoClientFactory with the right url', async () => {
54
+ const { client, db, collection, createIndex } = buildClientStub()
55
+ const getClientFor = vi.fn(() => client)
56
+
57
+ const Token = defineMongoDbStore<Item, 'id'>({
58
+ name: 'test/MongoFactory',
59
+ model: Item,
60
+ primaryKey: 'id',
61
+ url: 'mongodb://stub-host:1234',
62
+ db: 'unit-db',
63
+ collection: 'unit-coll',
64
+ options: { appName: 'spec' },
65
+ })
66
+
67
+ const injector = createInjector()
68
+ injector.bind(MongoClientFactory, () => ({ getClientFor }))
69
+
70
+ try {
71
+ const store = injector.get(Token) as MongodbStore<Item, 'id'>
72
+ expect(store).toBeInstanceOf(MongodbStore)
73
+ expect(store.model).toBe(Item)
74
+ expect(store.primaryKey).toBe('id')
75
+ expect(getClientFor).not.toHaveBeenCalled()
76
+
77
+ await store.getCollection()
78
+
79
+ expect(getClientFor).toHaveBeenCalledTimes(1)
80
+ expect(getClientFor).toHaveBeenCalledWith('mongodb://stub-host:1234', { appName: 'spec' })
81
+ expect(db).toHaveBeenCalledWith('unit-db')
82
+ expect(collection).toHaveBeenCalledWith('unit-coll')
83
+ expect(createIndex).toHaveBeenCalledWith({ id: 1 }, { unique: true })
84
+ } finally {
85
+ await injector[Symbol.asyncDispose]()
86
+ }
87
+ })
88
+
89
+ it('disposing the injector tears down the bound MongoClientFactory', async () => {
90
+ const { client } = buildClientStub()
91
+ const getClientFor = vi.fn(() => client)
92
+
93
+ const Token = defineMongoDbStore<Item, 'id'>({
94
+ name: 'test/MongoDispose',
95
+ model: Item,
96
+ primaryKey: 'id',
97
+ url: 'mongodb://stub-host:1234',
98
+ db: 'unit-db',
99
+ collection: 'unit-coll',
100
+ })
101
+
102
+ const injector = createInjector()
103
+ injector.bind(MongoClientFactory, () => ({ getClientFor }))
104
+
105
+ injector.get(Token)
106
+ await expect(injector[Symbol.asyncDispose]()).resolves.toBeUndefined()
107
+ })
108
+ })
@@ -0,0 +1,64 @@
1
+ import type { Constructable, StoreToken } from '@furystack/core'
2
+ import { defineStore } from '@furystack/core'
3
+ import type { MongoClientOptions } from 'mongodb'
4
+ import { MongoClientFactory } from './mongo-client-factory.js'
5
+ import { MongodbStore } from './mongodb-store.js'
6
+
7
+ /**
8
+ * Options accepted by {@link defineMongoDbStore}.
9
+ */
10
+ export type DefineMongoDbStoreOptions<T extends object, TPrimaryKey extends keyof T> = {
11
+ /** Human-readable token name used for debug/readability. */
12
+ name: string
13
+ /** Entity model constructor. */
14
+ model: Constructable<T>
15
+ /** Primary key field name. */
16
+ primaryKey: TPrimaryKey
17
+ /** MongoDB deployment URL (e.g. `mongodb://localhost:27017`). */
18
+ url: string
19
+ /** MongoDB database name. */
20
+ db: string
21
+ /** MongoDB collection name. */
22
+ collection: string
23
+ /** Optional {@link MongoClientOptions}. */
24
+ options?: MongoClientOptions
25
+ }
26
+
27
+ /**
28
+ * Mints a singleton {@link StoreToken} backed by a {@link MongodbStore}.
29
+ *
30
+ * Internally resolves the singleton {@link MongoClientFactory} so every store
31
+ * declared against the same `url` shares one {@link MongoClient}. Declare the
32
+ * token once at module scope — inline calls mint new identities and defeat
33
+ * singleton caching.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * export const UserStore = defineMongoDbStore({
38
+ * name: 'my-app/UserStore',
39
+ * model: User,
40
+ * primaryKey: 'username',
41
+ * url: 'mongodb://localhost:27017',
42
+ * db: 'my-app',
43
+ * collection: 'users',
44
+ * })
45
+ * ```
46
+ */
47
+ export const defineMongoDbStore = <T extends object, const TPrimaryKey extends keyof T>(
48
+ options: DefineMongoDbStoreOptions<T, TPrimaryKey>,
49
+ ): StoreToken<T, TPrimaryKey> =>
50
+ defineStore<T, TPrimaryKey>({
51
+ name: options.name,
52
+ model: options.model,
53
+ primaryKey: options.primaryKey,
54
+ factory: ({ inject }) => {
55
+ const factory = inject(MongoClientFactory)
56
+ return new MongodbStore<T, TPrimaryKey>({
57
+ model: options.model,
58
+ primaryKey: options.primaryKey,
59
+ db: options.db,
60
+ collection: options.collection,
61
+ mongoClient: () => factory.getClientFor(options.url, options.options),
62
+ })
63
+ },
64
+ })
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
+ export * from './define-mongodb-store.js'
2
+ export * from './mongo-client-factory.js'
1
3
  export * from './mongodb-store.js'
2
- export * from './store-manager-helpers.js'
@@ -1,40 +1,43 @@
1
- import { Injectable } from '@furystack/inject'
2
- import { EventHub, type ListenerErrorPayload } from '@furystack/utils'
1
+ import type { Token } from '@furystack/inject'
2
+ import { defineService } from '@furystack/inject'
3
3
  import type { MongoClientOptions } from 'mongodb'
4
4
  import { MongoClient } from 'mongodb'
5
5
 
6
6
  /**
7
- * Events emitted by the {@link MongoClientFactory}
7
+ * Pools {@link MongoClient} instances by URL so every store that targets the
8
+ * same MongoDB deployment shares a single connection.
9
+ *
10
+ * Application code rarely calls this directly — {@link defineMongoDbStore}
11
+ * resolves the factory token internally. Exported for tests and advanced
12
+ * integrations that need raw client access (e.g. dropping a test database).
8
13
  */
9
- export type MongoClientFactoryEvents = {
10
- onClientCreated: { url: string }
11
- onDisposed: undefined
12
- onListenerError: ListenerErrorPayload
14
+ export interface MongoClientFactory {
15
+ getClientFor(url: string, options?: MongoClientOptions): MongoClient
13
16
  }
14
17
 
15
18
  /**
16
- * Factory for instantiating MongoDb clients
19
+ * Singleton token for the {@link MongoClientFactory}. Closes every pooled
20
+ * client when the owning injector is disposed.
17
21
  */
18
- @Injectable({ lifetime: 'singleton' })
19
- export class MongoClientFactory extends EventHub<MongoClientFactoryEvents> implements AsyncDisposable {
20
- private connections: Map<string, MongoClient> = new Map()
21
-
22
- public async [Symbol.asyncDispose]() {
23
- await Promise.all([...this.connections.values()].map((c) => c.close(true)))
24
- this.connections.clear()
25
- this.emit('onDisposed', undefined)
26
- super[Symbol.dispose]()
27
- }
28
-
29
- public getClientFor(url: string, options?: MongoClientOptions) {
30
- const existing = this.connections.get(url)
31
- if (existing) {
32
- return existing
22
+ export const MongoClientFactory: Token<MongoClientFactory, 'singleton'> = defineService({
23
+ name: '@furystack/mongodb-store/MongoClientFactory',
24
+ lifetime: 'singleton',
25
+ factory: ({ onDispose }) => {
26
+ const connections = new Map<string, MongoClient>()
27
+ onDispose(async () => {
28
+ await Promise.all([...connections.values()].map((c) => c.close(true)))
29
+ connections.clear()
30
+ })
31
+ return {
32
+ getClientFor(url: string, options?: MongoClientOptions): MongoClient {
33
+ const existing = connections.get(url)
34
+ if (existing) {
35
+ return existing
36
+ }
37
+ const client = new MongoClient(url, options)
38
+ connections.set(url, client)
39
+ return client
40
+ },
33
41
  }
34
-
35
- const client = new MongoClient(url, options)
36
- this.connections.set(url, client)
37
- this.emit('onClientCreated', { url })
38
- return client
39
- }
40
- }
42
+ },
43
+ })
@@ -1,10 +1,10 @@
1
- import { StoreManager } from '@furystack/core'
2
1
  import { TestClass, createStoreTest } from '@furystack/core/create-physical-store-tests'
3
- import { Injector } from '@furystack/inject'
2
+ import type { Injector } from '@furystack/inject'
3
+ import { createInjector } from '@furystack/inject'
4
4
  import { usingAsync } from '@furystack/utils'
5
- import { describe, expect, it, vi } from 'vitest'
5
+ import { describe, expect, it } from 'vitest'
6
+ import { defineMongoDbStore } from './define-mongodb-store.js'
6
7
  import { MongoClientFactory } from './mongo-client-factory.js'
7
- import { useMongoDb } from './store-manager-helpers.js'
8
8
 
9
9
  class TestClassWithId {
10
10
  declare _id: string
@@ -18,8 +18,7 @@ const mongoDbUrl = process?.env?.MONGODB_URL || 'mongodb://localhost:27017'
18
18
  describe('MongoDB Store', () => {
19
19
  let dbIdx = 0
20
20
 
21
- const getMongoOptions = (injector: Injector) => ({
22
- injector,
21
+ const getMongoOptions = () => ({
23
22
  model: TestClass,
24
23
  primaryKey: 'id' as const,
25
24
  collection: `furystack-mongo-store-tests-${storeCount++}`,
@@ -27,16 +26,20 @@ describe('MongoDB Store', () => {
27
26
  url: mongoDbUrl,
28
27
  options: {},
29
28
  })
29
+
30
30
  createStoreTest({
31
31
  typeName: 'mongodb-store',
32
32
  skipStringTests: true,
33
33
  createStore: (i) => {
34
- const mongoOptions = getMongoOptions(i)
35
- useMongoDb(mongoOptions)
36
- const store = i.getInstance(StoreManager).getStoreFor(TestClass, 'id')
34
+ const mongoOptions = getMongoOptions()
35
+ const token = defineMongoDbStore<TestClass, 'id'>({
36
+ name: `mongodb-store/${mongoOptions.collection}`,
37
+ ...mongoOptions,
38
+ })
39
+ const store = i.get(token)
37
40
  const oldDispose = i[Symbol.asyncDispose]
38
41
  i[Symbol.asyncDispose] = async () => {
39
- const client = i.getInstance(MongoClientFactory).getClientFor(mongoOptions.url, mongoOptions.options)
42
+ const client = i.get(MongoClientFactory).getClientFor(mongoOptions.url, mongoOptions.options)
40
43
  const db = client.db(mongoOptions.db)
41
44
  await db.dropDatabase()
42
45
  await oldDispose.call(i)
@@ -46,20 +49,23 @@ describe('MongoDB Store', () => {
46
49
  })
47
50
 
48
51
  const createTestClassWithIdStore = (injector: Injector) => {
49
- const mongoOptions = { ...getMongoOptions(injector), model: TestClassWithId, primaryKey: '_id' as const }
50
- useMongoDb(mongoOptions)
52
+ const mongoOptions = { ...getMongoOptions(), model: TestClassWithId, primaryKey: '_id' as const }
53
+ const token = defineMongoDbStore<TestClassWithId, '_id'>({
54
+ name: `mongodb-store/${mongoOptions.collection}`,
55
+ ...mongoOptions,
56
+ })
51
57
  const oldDispose = injector[Symbol.asyncDispose]
52
58
  injector[Symbol.asyncDispose] = async () => {
53
- const client = injector.getInstance(MongoClientFactory).getClientFor(mongoOptions.url, mongoOptions.options)
59
+ const client = injector.get(MongoClientFactory).getClientFor(mongoOptions.url, mongoOptions.options)
54
60
  const db = client.db(mongoOptions.db)
55
61
  await db.dropDatabase()
56
62
  await oldDispose.call(injector)
57
63
  }
58
- return injector.getInstance(StoreManager).getStoreFor(TestClassWithId, '_id')
64
+ return injector.get(token)
59
65
  }
60
66
 
61
67
  it('Should retrieve an entity with its id', async () => {
62
- await usingAsync(new Injector(), async (injector) => {
68
+ await usingAsync(createInjector(), async (injector) => {
63
69
  const store = createTestClassWithIdStore(injector)
64
70
  const { created } = await store.add({ value: 'value1' })
65
71
 
@@ -71,7 +77,7 @@ describe('MongoDB Store', () => {
71
77
  })
72
78
 
73
79
  it('Should retrieve more entities with theis ids', async () => {
74
- await usingAsync(new Injector(), async (injector) => {
80
+ await usingAsync(createInjector(), async (injector) => {
75
81
  const store = createTestClassWithIdStore(injector)
76
82
  const { created } = await store.add({ value: 'value1' }, { value: 'value2' }, { value: 'value3' })
77
83
  const retrieved = await store.find({ filter: { _id: { $in: created.map((c) => c._id) } } })
@@ -81,7 +87,7 @@ describe('MongoDB Store', () => {
81
87
  })
82
88
 
83
89
  it('Should filter by _id as a string', async () => {
84
- await usingAsync(new Injector(), async (injector) => {
90
+ await usingAsync(createInjector(), async (injector) => {
85
91
  const store = createTestClassWithIdStore(injector)
86
92
  const { created } = await store.add({ value: 'value1' })
87
93
  const retrieved = await store.find({ filter: { _id: { $eq: created[0]._id } } })
@@ -91,7 +97,7 @@ describe('MongoDB Store', () => {
91
97
  })
92
98
 
93
99
  it('Should filter by _id.$eq as a string', async () => {
94
- await usingAsync(new Injector(), async (injector) => {
100
+ await usingAsync(createInjector(), async (injector) => {
95
101
  const store = createTestClassWithIdStore(injector)
96
102
  const { created } = await store.add({ value: 'value1' })
97
103
  const retrieved = await store.find({ filter: { _id: { $eq: created[0]._id } } })
@@ -101,7 +107,7 @@ describe('MongoDB Store', () => {
101
107
  })
102
108
 
103
109
  it('Should filter by _id.$in as array of strings', async () => {
104
- await usingAsync(new Injector(), async (injector) => {
110
+ await usingAsync(createInjector(), async (injector) => {
105
111
  const store = createTestClassWithIdStore(injector)
106
112
  const { created } = await store.add({ value: 'value1' }, { value: 'value2' })
107
113
  const ids = created.map((c) => c._id)
@@ -112,7 +118,7 @@ describe('MongoDB Store', () => {
112
118
  })
113
119
 
114
120
  it('Should filter by _id.$nin as array of strings', async () => {
115
- await usingAsync(new Injector(), async (injector) => {
121
+ await usingAsync(createInjector(), async (injector) => {
116
122
  const store = createTestClassWithIdStore(injector)
117
123
  const { created } = await store.add({ value: 'value1' }, { value: 'value2' })
118
124
  const ids = created.map((c) => c._id)
@@ -123,7 +129,7 @@ describe('MongoDB Store', () => {
123
129
  })
124
130
 
125
131
  it('Should filter by a non-_id field', async () => {
126
- await usingAsync(new Injector(), async (injector) => {
132
+ await usingAsync(createInjector(), async (injector) => {
127
133
  const store = createTestClassWithIdStore(injector)
128
134
  await store.add({ value: 'value1' }, { value: 'value2' })
129
135
  const retrieved = await store.find({ filter: { value: { $eq: 'value2' } } })
@@ -132,35 +138,3 @@ describe('MongoDB Store', () => {
132
138
  })
133
139
  })
134
140
  })
135
-
136
- describe('MongoClientFactory EventHub', () => {
137
- it('should emit onClientCreated when a new client is created', async () => {
138
- const factory = new MongoClientFactory()
139
- try {
140
- const handler = vi.fn()
141
- factory.addListener('onClientCreated', handler)
142
-
143
- factory.getClientFor('mongodb://localhost:27017/test-db')
144
-
145
- expect(handler).toHaveBeenCalledTimes(1)
146
- expect(handler).toHaveBeenCalledWith({ url: 'mongodb://localhost:27017/test-db' })
147
- } finally {
148
- await factory[Symbol.asyncDispose]()
149
- }
150
- })
151
-
152
- it('should not emit onClientCreated when returning an existing client', async () => {
153
- const factory = new MongoClientFactory()
154
- try {
155
- const handler = vi.fn()
156
-
157
- factory.getClientFor('mongodb://localhost:27017/test-db')
158
- factory.addListener('onClientCreated', handler)
159
- factory.getClientFor('mongodb://localhost:27017/test-db')
160
-
161
- expect(handler).not.toHaveBeenCalled()
162
- } finally {
163
- await factory[Symbol.asyncDispose]()
164
- }
165
- })
166
- })
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ Constructable,
2
3
  CreateResult,
3
4
  FilterType,
4
5
  FindOptions,
@@ -6,7 +7,6 @@ import type {
6
7
  PhysicalStore,
7
8
  WithOptionalId,
8
9
  } from '@furystack/core'
9
- import type { Constructable } from '@furystack/inject'
10
10
  import { EventHub } from '@furystack/utils'
11
11
  import type { Collection, Filter, MongoClient, OptionalUnlessRequiredId, Sort, UpdateFilter } from 'mongodb'
12
12
  import { ObjectId } from 'mongodb'
@@ -1,33 +0,0 @@
1
- import type { Constructable, Injector } from '@furystack/inject';
2
- import type { MongoClientOptions } from 'mongodb';
3
- export declare const useMongoDb: <T extends object>({ injector, model, primaryKey, db, collection, url, options, }: {
4
- /**
5
- * The Injector instance to use
6
- */
7
- injector: Injector;
8
- /**
9
- * The constructable model class
10
- */
11
- model: Constructable<T>;
12
- /**
13
- * The name of the Primary Key property
14
- */
15
- primaryKey: keyof T;
16
- /**
17
- * Url of the MongoDb repository
18
- */
19
- url: string;
20
- /**
21
- * MongoDb Database name
22
- */
23
- db: string;
24
- /**
25
- * MongoDb Collection name
26
- */
27
- collection: string;
28
- /**
29
- * Optional options for the MongoDb Client
30
- */
31
- options?: MongoClientOptions;
32
- }) => void;
33
- //# sourceMappingURL=store-manager-helpers.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"store-manager-helpers.d.ts","sourceRoot":"","sources":["../src/store-manager-helpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAIjD,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,EAAE,gEAQ1C;IACD;;OAEG;IACH,QAAQ,EAAE,QAAQ,CAAA;IAClB;;OAEG;IACH,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;IACvB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC,CAAA;IACnB;;OAEG;IACH,GAAG,EAAE,MAAM,CAAA;IACX;;OAEG;IACH,EAAE,EAAE,MAAM,CAAA;IACV;;OAEG;IACH,UAAU,EAAE,MAAM,CAAA;IAElB;;OAEG;IACH,OAAO,CAAC,EAAE,kBAAkB,CAAA;CAC7B,SAUA,CAAA"}
@@ -1,15 +0,0 @@
1
- import { addStore } from '@furystack/core';
2
- import { MongoClientFactory } from './mongo-client-factory.js';
3
- import { MongodbStore } from './mongodb-store.js';
4
- export const useMongoDb = ({ injector, model, primaryKey, db, collection, url, options, }) => {
5
- const clientFactory = injector.getInstance(MongoClientFactory);
6
- const store = new MongodbStore({
7
- model,
8
- primaryKey,
9
- db,
10
- collection,
11
- mongoClient: () => clientFactory.getClientFor(url, options),
12
- });
13
- addStore(injector, store);
14
- };
15
- //# sourceMappingURL=store-manager-helpers.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"store-manager-helpers.js","sourceRoot":"","sources":["../src/store-manager-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAG1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,MAAM,CAAC,MAAM,UAAU,GAAG,CAAmB,EAC3C,QAAQ,EACR,KAAK,EACL,UAAU,EACV,EAAE,EACF,UAAU,EACV,GAAG,EACH,OAAO,GA+BR,EAAE,EAAE;IACH,MAAM,aAAa,GAAG,QAAQ,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAA;IAC9D,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC;QAC7B,KAAK;QACL,UAAU;QACV,EAAE;QACF,UAAU;QACV,WAAW,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC;KAC5D,CAAC,CAAA;IACF,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;AAC3B,CAAC,CAAA"}
@@ -1,55 +0,0 @@
1
- import { addStore } from '@furystack/core'
2
- import type { Constructable, Injector } from '@furystack/inject'
3
- import type { MongoClientOptions } from 'mongodb'
4
- import { MongoClientFactory } from './mongo-client-factory.js'
5
- import { MongodbStore } from './mongodb-store.js'
6
-
7
- export const useMongoDb = <T extends object>({
8
- injector,
9
- model,
10
- primaryKey,
11
- db,
12
- collection,
13
- url,
14
- options,
15
- }: {
16
- /**
17
- * The Injector instance to use
18
- */
19
- injector: Injector
20
- /**
21
- * The constructable model class
22
- */
23
- model: Constructable<T>
24
- /**
25
- * The name of the Primary Key property
26
- */
27
- primaryKey: keyof T
28
- /**
29
- * Url of the MongoDb repository
30
- */
31
- url: string
32
- /**
33
- * MongoDb Database name
34
- */
35
- db: string
36
- /**
37
- * MongoDb Collection name
38
- */
39
- collection: string
40
-
41
- /**
42
- * Optional options for the MongoDb Client
43
- */
44
- options?: MongoClientOptions
45
- }) => {
46
- const clientFactory = injector.getInstance(MongoClientFactory)
47
- const store = new MongodbStore({
48
- model,
49
- primaryKey,
50
- db,
51
- collection,
52
- mongoClient: () => clientFactory.getClientFor(url, options),
53
- })
54
- addStore(injector, store)
55
- }