@furystack/sequelize-store 6.0.48 → 7.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 +15 -0
- package/README.md +23 -10
- package/esm/define-sequelize-store.d.ts +41 -0
- package/esm/define-sequelize-store.d.ts.map +1 -0
- package/esm/define-sequelize-store.js +39 -0
- package/esm/define-sequelize-store.js.map +1 -0
- package/esm/define-sequelize-store.spec.d.ts +2 -0
- package/esm/define-sequelize-store.spec.d.ts.map +1 -0
- package/esm/define-sequelize-store.spec.js +40 -0
- package/esm/define-sequelize-store.spec.js.map +1 -0
- package/esm/index.d.ts +2 -1
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +2 -1
- package/esm/index.js.map +1 -1
- package/esm/sequelize-client-factory.d.ts +9 -4
- package/esm/sequelize-client-factory.d.ts.map +1 -1
- package/esm/sequelize-client-factory.js +26 -29
- package/esm/sequelize-client-factory.js.map +1 -1
- package/esm/sequelize-store.d.ts +1 -2
- package/esm/sequelize-store.d.ts.map +1 -1
- package/esm/sequelize-store.spec.js +31 -76
- package/esm/sequelize-store.spec.js.map +1 -1
- package/package.json +7 -7
- package/src/define-sequelize-store.spec.ts +47 -0
- package/src/define-sequelize-store.ts +62 -0
- package/src/index.ts +2 -1
- package/src/sequelize-client-factory.ts +33 -21
- package/src/sequelize-store.spec.ts +36 -82
- package/src/sequelize-store.ts +1 -1
- package/esm/store-manager-helpers.d.ts +0 -26
- package/esm/store-manager-helpers.d.ts.map +0 -1
- package/esm/store-manager-helpers.js +0 -15
- package/esm/store-manager-helpers.js.map +0 -1
- package/src/store-manager-helpers.ts +0 -49
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [7.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 `useSequelize(...)`. Declare the store at module scope with `defineSequelizeStore<T, M, PK>({ name, model, sequelizeModel, primaryKey, options, initModel? })`.
|
|
10
|
+
- `SequelizeClientFactory` is now an exported interface + `defineService({ lifetime: 'singleton' })` token. Clients are pooled by `JSON.stringify(options)` and closed on injector teardown.
|
|
11
|
+
|
|
12
|
+
## [6.0.49] - 2026-04-17
|
|
13
|
+
|
|
14
|
+
### ⬆️ Dependencies
|
|
15
|
+
|
|
16
|
+
- Raised `@types/node` to ^25.6.0, `typescript` to ^6.0.3, and `vitest` to ^4.1.4 so package development matches the workspace toolchain.
|
|
17
|
+
|
|
3
18
|
## [6.0.48] - 2026-03-27
|
|
4
19
|
|
|
5
20
|
### ⬆️ Dependencies
|
package/README.md
CHANGED
|
@@ -12,10 +12,15 @@ yarn add @furystack/sequelize-store
|
|
|
12
12
|
|
|
13
13
|
## Initialization
|
|
14
14
|
|
|
15
|
+
`defineSequelizeStore` mints a `StoreToken` backed by a Sequelize model. The
|
|
16
|
+
shared `SequelizeClientFactory` singleton pools `Sequelize` clients keyed by
|
|
17
|
+
`JSON.stringify(options)` and closes them when the owning injector is
|
|
18
|
+
disposed.
|
|
19
|
+
|
|
15
20
|
```ts
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
21
|
+
import { createInjector } from '@furystack/inject'
|
|
22
|
+
import { defineSequelizeStore } from '@furystack/sequelize-store'
|
|
23
|
+
import { defineDataSet } from '@furystack/repository'
|
|
19
24
|
import { Model, type Options } from 'sequelize'
|
|
20
25
|
|
|
21
26
|
export class TestEntry extends Model {
|
|
@@ -23,20 +28,28 @@ export class TestEntry extends Model {
|
|
|
23
28
|
declare public value: string
|
|
24
29
|
}
|
|
25
30
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
injector: myInjector,
|
|
31
|
+
export const TestEntryStore = defineSequelizeStore<TestEntry, TestEntry, '_id'>({
|
|
32
|
+
name: 'my-app/TestEntryStore',
|
|
29
33
|
model: TestEntry,
|
|
30
|
-
sequelizeModel: TestEntry,
|
|
34
|
+
sequelizeModel: TestEntry,
|
|
31
35
|
primaryKey: '_id',
|
|
32
36
|
options: {
|
|
33
|
-
// Sequelize connection options
|
|
34
37
|
dialect: 'sqlite',
|
|
35
38
|
storage: ':memory:',
|
|
36
39
|
} as Options,
|
|
37
40
|
})
|
|
38
41
|
|
|
39
|
-
const
|
|
42
|
+
export const TestEntryDataSet = defineDataSet({
|
|
43
|
+
name: 'my-app/TestEntryDataSet',
|
|
44
|
+
store: TestEntryStore,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const myInjector = createInjector()
|
|
48
|
+
const dataSet = myInjector.get(TestEntryDataSet)
|
|
40
49
|
```
|
|
41
50
|
|
|
42
|
-
> **Tip:** For application-level data access,
|
|
51
|
+
> **Tip:** For application-level data access, always go through a
|
|
52
|
+
> `DataSetToken` rather than resolving the `StoreToken` directly. The
|
|
53
|
+
> DataSet layer runs authorization, modification hooks, and entity-sync
|
|
54
|
+
> events; a direct store access skips all of them. The
|
|
55
|
+
> `furystack/no-direct-store-token` lint rule guards against this.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Constructable, StoreToken } from '@furystack/core';
|
|
2
|
+
import type { Model, ModelStatic, Options, Sequelize } from 'sequelize';
|
|
3
|
+
/**
|
|
4
|
+
* Options accepted by {@link defineSequelizeStore}.
|
|
5
|
+
*/
|
|
6
|
+
export type DefineSequelizeStoreOptions<T extends object, M extends Model<T>, 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
|
+
/** The Sequelize Model class. */
|
|
12
|
+
sequelizeModel: ModelStatic<M>;
|
|
13
|
+
/** Primary key field name. */
|
|
14
|
+
primaryKey: TPrimaryKey;
|
|
15
|
+
/** Sequelize client options (dialect, storage, etc.). */
|
|
16
|
+
options: Options;
|
|
17
|
+
/** Optional callback that initializes the Sequelize model. Runs once per store. */
|
|
18
|
+
initModel?: (sequelize: Sequelize) => Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Mints a singleton {@link StoreToken} backed by a {@link SequelizeStore}.
|
|
22
|
+
*
|
|
23
|
+
* Internally resolves the singleton {@link SequelizeClientFactory} so every
|
|
24
|
+
* store declared against the same `options` shares one {@link Sequelize}
|
|
25
|
+
* client. Declare the token once at module scope — inline calls mint new
|
|
26
|
+
* identities and defeat singleton caching.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* export const UserStore = defineSequelizeStore({
|
|
31
|
+
* name: 'my-app/UserStore',
|
|
32
|
+
* model: User,
|
|
33
|
+
* sequelizeModel: UserSequelizeModel,
|
|
34
|
+
* primaryKey: 'username',
|
|
35
|
+
* options: { dialect: 'sqlite', storage: './data.sqlite' },
|
|
36
|
+
* initModel: async (sequelize) => { ... },
|
|
37
|
+
* })
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare const defineSequelizeStore: <T extends object, M extends Model<T>, const TPrimaryKey extends keyof T>(opts: DefineSequelizeStoreOptions<T, M, TPrimaryKey>) => StoreToken<T, TPrimaryKey>;
|
|
41
|
+
//# sourceMappingURL=define-sequelize-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-sequelize-store.d.ts","sourceRoot":"","sources":["../src/define-sequelize-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEhE,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAIvE;;GAEG;AACH,MAAM,MAAM,2BAA2B,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,SAAS,MAAM,CAAC,IAAI;IAC3G,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAA;IACZ,gCAAgC;IAChC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;IACvB,iCAAiC;IACjC,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;IAC9B,8BAA8B;IAC9B,UAAU,EAAE,WAAW,CAAA;IACvB,yDAAyD;IACzD,OAAO,EAAE,OAAO,CAAA;IAChB,mFAAmF;IACnF,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACpD,CAAA;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,WAAW,SAAS,MAAM,CAAC,EAC1G,MAAM,2BAA2B,CAAC,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,KACnD,UAAU,CAAC,CAAC,EAAE,WAAW,CAexB,CAAA"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { defineStore } from '@furystack/core';
|
|
2
|
+
import { SequelizeClientFactory } from './sequelize-client-factory.js';
|
|
3
|
+
import { SequelizeStore } from './sequelize-store.js';
|
|
4
|
+
/**
|
|
5
|
+
* Mints a singleton {@link StoreToken} backed by a {@link SequelizeStore}.
|
|
6
|
+
*
|
|
7
|
+
* Internally resolves the singleton {@link SequelizeClientFactory} so every
|
|
8
|
+
* store declared against the same `options` shares one {@link Sequelize}
|
|
9
|
+
* client. Declare the token once at module scope — inline calls mint new
|
|
10
|
+
* identities and defeat singleton caching.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* export const UserStore = defineSequelizeStore({
|
|
15
|
+
* name: 'my-app/UserStore',
|
|
16
|
+
* model: User,
|
|
17
|
+
* sequelizeModel: UserSequelizeModel,
|
|
18
|
+
* primaryKey: 'username',
|
|
19
|
+
* options: { dialect: 'sqlite', storage: './data.sqlite' },
|
|
20
|
+
* initModel: async (sequelize) => { ... },
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export const defineSequelizeStore = (opts) => defineStore({
|
|
25
|
+
name: opts.name,
|
|
26
|
+
model: opts.model,
|
|
27
|
+
primaryKey: opts.primaryKey,
|
|
28
|
+
factory: ({ inject }) => {
|
|
29
|
+
const factory = inject(SequelizeClientFactory);
|
|
30
|
+
return new SequelizeStore({
|
|
31
|
+
model: opts.model,
|
|
32
|
+
primaryKey: opts.primaryKey,
|
|
33
|
+
sequelizeModel: opts.sequelizeModel,
|
|
34
|
+
getSequelizeClient: () => factory.getSequelizeClient(opts.options),
|
|
35
|
+
initModel: opts.initModel,
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
//# sourceMappingURL=define-sequelize-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-sequelize-store.js","sourceRoot":"","sources":["../src/define-sequelize-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAE7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAoBrD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,IAAoD,EACxB,EAAE,CAC9B,WAAW,CAAiB;IAC1B,IAAI,EAAE,IAAI,CAAC,IAAI;IACf,KAAK,EAAE,IAAI,CAAC,KAAK;IACjB,UAAU,EAAE,IAAI,CAAC,UAAU;IAC3B,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;QACtB,MAAM,OAAO,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAA;QAC9C,OAAO,IAAI,cAAc,CAAoB;YAC3C,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,kBAAkB,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC;YAClE,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC,CAAA;IACJ,CAAC;CACF,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-sequelize-store.spec.d.ts","sourceRoot":"","sources":["../src/define-sequelize-store.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Model } from 'sequelize';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { defineSequelizeStore } from './define-sequelize-store.js';
|
|
4
|
+
class Item {
|
|
5
|
+
}
|
|
6
|
+
class ItemSequelizeClass extends Model {
|
|
7
|
+
}
|
|
8
|
+
describe('defineSequelizeStore (metadata)', () => {
|
|
9
|
+
it('propagates name, lifetime, model and primaryKey onto the returned token', () => {
|
|
10
|
+
const token = defineSequelizeStore({
|
|
11
|
+
name: 'test/SequelizeItems',
|
|
12
|
+
model: Item,
|
|
13
|
+
sequelizeModel: ItemSequelizeClass,
|
|
14
|
+
primaryKey: 'id',
|
|
15
|
+
options: { dialect: 'sqlite', storage: ':memory:', logging: false },
|
|
16
|
+
});
|
|
17
|
+
expect(token.name).toBe('test/SequelizeItems');
|
|
18
|
+
expect(token.lifetime).toBe('singleton');
|
|
19
|
+
expect(token.model).toBe(Item);
|
|
20
|
+
expect(token.primaryKey).toBe('id');
|
|
21
|
+
});
|
|
22
|
+
it('mints a distinct token identity per call even when options match -- declare once at module scope', () => {
|
|
23
|
+
const a = defineSequelizeStore({
|
|
24
|
+
name: 'test/SequelizeDupe',
|
|
25
|
+
model: Item,
|
|
26
|
+
sequelizeModel: ItemSequelizeClass,
|
|
27
|
+
primaryKey: 'id',
|
|
28
|
+
options: { dialect: 'sqlite', storage: ':memory:', logging: false },
|
|
29
|
+
});
|
|
30
|
+
const b = defineSequelizeStore({
|
|
31
|
+
name: 'test/SequelizeDupe',
|
|
32
|
+
model: Item,
|
|
33
|
+
sequelizeModel: ItemSequelizeClass,
|
|
34
|
+
primaryKey: 'id',
|
|
35
|
+
options: { dialect: 'sqlite', storage: ':memory:', logging: false },
|
|
36
|
+
});
|
|
37
|
+
expect(a.id).not.toBe(b.id);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=define-sequelize-store.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-sequelize-store.spec.js","sourceRoot":"","sources":["../src/define-sequelize-store.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAElE,MAAM,IAAI;CAGT;AAED,MAAM,kBAAmB,SAAQ,KAAiB;CAGjD;AAED,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC/C,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,MAAM,KAAK,GAAG,oBAAoB,CAAiC;YACjE,IAAI,EAAE,qBAAqB;YAC3B,KAAK,EAAE,IAAI;YACX,cAAc,EAAE,kBAAkB;YAClC,UAAU,EAAE,IAAI;YAChB,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE;SACpE,CAAC,CAAA;QACF,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAA;QAC9C,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QACxC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC9B,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACrC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kGAAkG,EAAE,GAAG,EAAE;QAC1G,MAAM,CAAC,GAAG,oBAAoB,CAAiC;YAC7D,IAAI,EAAE,oBAAoB;YAC1B,KAAK,EAAE,IAAI;YACX,cAAc,EAAE,kBAAkB;YAClC,UAAU,EAAE,IAAI;YAChB,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE;SACpE,CAAC,CAAA;QACF,MAAM,CAAC,GAAG,oBAAoB,CAAiC;YAC7D,IAAI,EAAE,oBAAoB;YAC1B,KAAK,EAAE,IAAI;YACX,cAAc,EAAE,kBAAkB;YAClC,UAAU,EAAE,IAAI;YAChB,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE;SACpE,CAAC,CAAA;QACF,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
package/esm/index.d.ts
CHANGED
package/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAA;AAC3C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,sBAAsB,CAAA"}
|
package/esm/index.js
CHANGED
package/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAA;AAC3C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,sBAAsB,CAAA"}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import type { Token } from '@furystack/inject';
|
|
1
2
|
import type { Options } from 'sequelize';
|
|
2
3
|
import { Sequelize } from 'sequelize';
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
+
* Pools {@link Sequelize} client instances keyed by the serialized options so
|
|
6
|
+
* multiple stores against the same connection string share one client.
|
|
5
7
|
*/
|
|
6
|
-
export
|
|
7
|
-
private connections;
|
|
8
|
-
[Symbol.asyncDispose](): Promise<void>;
|
|
8
|
+
export interface SequelizeClientFactory {
|
|
9
9
|
getSequelizeClient(options: Options): Sequelize;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Singleton token for the {@link SequelizeClientFactory}. Closes every pooled
|
|
13
|
+
* client when the owning injector is disposed.
|
|
14
|
+
*/
|
|
15
|
+
export declare const SequelizeClientFactory: Token<SequelizeClientFactory, 'singleton'>;
|
|
11
16
|
//# sourceMappingURL=sequelize-client-factory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sequelize-client-factory.d.ts","sourceRoot":"","sources":["../src/sequelize-client-factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sequelize-client-factory.d.ts","sourceRoot":"","sources":["../src/sequelize-client-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAE9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAM,SAAS,EAAE,MAAM,WAAW,CAAA;AA2CzC;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAA;CAChD;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,sBAAsB,EAAE,WAAW,CAsB5E,CAAA"}
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
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';
|
|
1
|
+
import { defineService } from '@furystack/inject';
|
|
8
2
|
import { Op, Sequelize } from 'sequelize';
|
|
9
3
|
const operatorsAliases = {
|
|
10
4
|
$eq: Op.eq,
|
|
@@ -47,27 +41,30 @@ const operatorsAliases = {
|
|
|
47
41
|
$col: Op.col,
|
|
48
42
|
};
|
|
49
43
|
/**
|
|
50
|
-
*
|
|
44
|
+
* Singleton token for the {@link SequelizeClientFactory}. Closes every pooled
|
|
45
|
+
* client when the owning injector is disposed.
|
|
51
46
|
*/
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
47
|
+
export const SequelizeClientFactory = defineService({
|
|
48
|
+
name: '@furystack/sequelize-store/SequelizeClientFactory',
|
|
49
|
+
lifetime: 'singleton',
|
|
50
|
+
factory: ({ onDispose }) => {
|
|
51
|
+
const connections = new Map();
|
|
52
|
+
onDispose(async () => {
|
|
53
|
+
await Promise.all([...connections.values()].map((c) => c.close()));
|
|
54
|
+
connections.clear();
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
getSequelizeClient(options) {
|
|
58
|
+
const key = JSON.stringify(options);
|
|
59
|
+
const existing = connections.get(key);
|
|
60
|
+
if (existing) {
|
|
61
|
+
return existing;
|
|
62
|
+
}
|
|
63
|
+
const client = new Sequelize({ ...options, operatorsAliases });
|
|
64
|
+
connections.set(key, client);
|
|
65
|
+
return client;
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
});
|
|
73
70
|
//# sourceMappingURL=sequelize-client-factory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sequelize-client-factory.js","sourceRoot":"","sources":["../src/sequelize-client-factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sequelize-client-factory.js","sourceRoot":"","sources":["../src/sequelize-client-factory.ts"],"names":[],"mappings":"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"}
|
package/esm/sequelize-store.d.ts
CHANGED
|
@@ -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 { Model, ModelStatic, Sequelize } from 'sequelize';
|
|
5
4
|
export interface SequelizeStoreSettings<T extends object, M extends Model<T>, TPrimaryKey extends keyof T> {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sequelize-store.d.ts","sourceRoot":"","sources":["../src/sequelize-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,
|
|
1
|
+
{"version":3,"file":"sequelize-store.d.ts","sourceRoot":"","sources":["../src/sequelize-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
import { TestClass, createStoreTest } from '@furystack/core/create-physical-store-tests';
|
|
2
|
-
import {
|
|
3
|
-
import { useSequelize } from './store-manager-helpers.js';
|
|
4
|
-
import { DataTypes, Model } from 'sequelize';
|
|
2
|
+
import { createInjector } from '@furystack/inject';
|
|
5
3
|
import { sleepAsync, usingAsync } from '@furystack/utils';
|
|
6
|
-
import {
|
|
4
|
+
import { DataTypes, Model } from 'sequelize';
|
|
5
|
+
import { describe, expect, it } from 'vitest';
|
|
6
|
+
import { defineSequelizeStore } from './define-sequelize-store.js';
|
|
7
7
|
import { SequelizeClientFactory } from './sequelize-client-factory.js';
|
|
8
|
-
import { describe, it, expect } from 'vitest';
|
|
9
8
|
class TestSequelizeClass extends Model {
|
|
10
9
|
}
|
|
10
|
+
const initTestModel = async (sequelize) => {
|
|
11
|
+
TestSequelizeClass.init({
|
|
12
|
+
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
|
|
13
|
+
stringValue1: { type: DataTypes.STRING },
|
|
14
|
+
stringValue2: { type: DataTypes.STRING },
|
|
15
|
+
numberValue1: { type: DataTypes.INTEGER },
|
|
16
|
+
numberValue2: { type: DataTypes.INTEGER },
|
|
17
|
+
booleanValue: { type: DataTypes.BOOLEAN },
|
|
18
|
+
dateValue: { type: DataTypes.DATE },
|
|
19
|
+
}, { sequelize, timestamps: false });
|
|
20
|
+
};
|
|
11
21
|
describe('Sequelize Store', () => {
|
|
12
22
|
createStoreTest({
|
|
13
23
|
typeName: 'sequelize-store',
|
|
14
24
|
skipRegexTests: true,
|
|
15
25
|
createStore: (i) => {
|
|
16
|
-
|
|
17
|
-
|
|
26
|
+
const token = defineSequelizeStore({
|
|
27
|
+
name: 'sequelize-store/TestClass',
|
|
18
28
|
model: TestClass,
|
|
19
29
|
sequelizeModel: TestSequelizeClass,
|
|
20
30
|
primaryKey: 'id',
|
|
@@ -23,41 +33,14 @@ describe('Sequelize Store', () => {
|
|
|
23
33
|
storage: ':memory:',
|
|
24
34
|
logging: false,
|
|
25
35
|
},
|
|
26
|
-
initModel:
|
|
27
|
-
TestSequelizeClass.init({
|
|
28
|
-
id: {
|
|
29
|
-
type: DataTypes.INTEGER,
|
|
30
|
-
autoIncrement: true,
|
|
31
|
-
primaryKey: true,
|
|
32
|
-
},
|
|
33
|
-
stringValue1: {
|
|
34
|
-
type: DataTypes.STRING,
|
|
35
|
-
},
|
|
36
|
-
stringValue2: {
|
|
37
|
-
type: DataTypes.STRING,
|
|
38
|
-
},
|
|
39
|
-
numberValue1: {
|
|
40
|
-
type: DataTypes.INTEGER,
|
|
41
|
-
},
|
|
42
|
-
numberValue2: {
|
|
43
|
-
type: DataTypes.INTEGER,
|
|
44
|
-
},
|
|
45
|
-
booleanValue: {
|
|
46
|
-
type: DataTypes.BOOLEAN,
|
|
47
|
-
},
|
|
48
|
-
dateValue: {
|
|
49
|
-
type: DataTypes.DATE,
|
|
50
|
-
},
|
|
51
|
-
}, { sequelize, timestamps: false });
|
|
52
|
-
},
|
|
36
|
+
initModel: initTestModel,
|
|
53
37
|
});
|
|
54
|
-
|
|
55
|
-
return store;
|
|
38
|
+
return i.get(token);
|
|
56
39
|
},
|
|
57
40
|
});
|
|
58
41
|
it('should return the cached sequelize client instance from the factory', async () => {
|
|
59
|
-
await usingAsync(
|
|
60
|
-
const factory = i.
|
|
42
|
+
await usingAsync(createInjector(), async (i) => {
|
|
43
|
+
const factory = i.get(SequelizeClientFactory);
|
|
61
44
|
const settings = {
|
|
62
45
|
dialect: 'sqlite',
|
|
63
46
|
storage: ':memory:',
|
|
@@ -69,9 +52,9 @@ describe('Sequelize Store', () => {
|
|
|
69
52
|
});
|
|
70
53
|
});
|
|
71
54
|
it('should return the cached model from the store class', async () => {
|
|
72
|
-
await usingAsync(
|
|
73
|
-
|
|
74
|
-
|
|
55
|
+
await usingAsync(createInjector(), async (i) => {
|
|
56
|
+
const token = defineSequelizeStore({
|
|
57
|
+
name: 'sequelize-store/TestClass/cached-model',
|
|
75
58
|
model: TestClass,
|
|
76
59
|
sequelizeModel: TestSequelizeClass,
|
|
77
60
|
primaryKey: 'id',
|
|
@@ -82,36 +65,10 @@ describe('Sequelize Store', () => {
|
|
|
82
65
|
},
|
|
83
66
|
initModel: async (sequelize) => {
|
|
84
67
|
await sleepAsync(100);
|
|
85
|
-
|
|
86
|
-
id: {
|
|
87
|
-
type: DataTypes.INTEGER,
|
|
88
|
-
autoIncrement: true,
|
|
89
|
-
primaryKey: true,
|
|
90
|
-
},
|
|
91
|
-
stringValue1: {
|
|
92
|
-
type: DataTypes.STRING,
|
|
93
|
-
},
|
|
94
|
-
stringValue2: {
|
|
95
|
-
type: DataTypes.STRING,
|
|
96
|
-
},
|
|
97
|
-
numberValue1: {
|
|
98
|
-
type: DataTypes.INTEGER,
|
|
99
|
-
},
|
|
100
|
-
numberValue2: {
|
|
101
|
-
type: DataTypes.INTEGER,
|
|
102
|
-
},
|
|
103
|
-
booleanValue: {
|
|
104
|
-
type: DataTypes.BOOLEAN,
|
|
105
|
-
},
|
|
106
|
-
dateValue: {
|
|
107
|
-
type: DataTypes.DATE,
|
|
108
|
-
},
|
|
109
|
-
}, { sequelize, timestamps: false });
|
|
68
|
+
await initTestModel(sequelize);
|
|
110
69
|
},
|
|
111
70
|
});
|
|
112
|
-
const store = i
|
|
113
|
-
.getInstance(StoreManager)
|
|
114
|
-
.getStoreFor(TestClass, 'id');
|
|
71
|
+
const store = i.get(token);
|
|
115
72
|
const model1Promise = store.getModel();
|
|
116
73
|
const model2 = await store.getModel();
|
|
117
74
|
const model1 = await model1Promise;
|
|
@@ -121,9 +78,9 @@ describe('Sequelize Store', () => {
|
|
|
121
78
|
});
|
|
122
79
|
});
|
|
123
80
|
it('should throw if the sequelize model is not initialized', async () => {
|
|
124
|
-
await usingAsync(
|
|
125
|
-
|
|
126
|
-
|
|
81
|
+
await usingAsync(createInjector(), async (i) => {
|
|
82
|
+
const token = defineSequelizeStore({
|
|
83
|
+
name: 'sequelize-store/TestClass/no-init',
|
|
127
84
|
model: TestClass,
|
|
128
85
|
sequelizeModel: TestSequelizeClass,
|
|
129
86
|
primaryKey: 'id',
|
|
@@ -136,15 +93,13 @@ describe('Sequelize Store', () => {
|
|
|
136
93
|
// won't init the model here
|
|
137
94
|
},
|
|
138
95
|
});
|
|
139
|
-
const store = i
|
|
140
|
-
.getInstance(StoreManager)
|
|
141
|
-
.getStoreFor(TestClass, 'id');
|
|
96
|
+
const store = i.get(token);
|
|
142
97
|
expect.assertions(1);
|
|
143
98
|
try {
|
|
144
99
|
await store.getModel();
|
|
145
100
|
}
|
|
146
101
|
catch (error) {
|
|
147
|
-
expect(error.message).toBe('TestSequelizeClass has not been defined');
|
|
102
|
+
expect(error.message).toBe('TestSequelizeClass has not been defined');
|
|
148
103
|
}
|
|
149
104
|
});
|
|
150
105
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sequelize-store.spec.js","sourceRoot":"","sources":["../src/sequelize-store.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAA;AACxF,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"sequelize-store.spec.js","sourceRoot":"","sources":["../src/sequelize-store.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@furystack/sequelize-store",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "Sequelize-based Store implementation for FuryStack",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -38,16 +38,16 @@
|
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://github.com/furystack/furystack",
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@furystack/core": "^
|
|
42
|
-
"@furystack/inject": "^
|
|
43
|
-
"@furystack/utils": "^
|
|
41
|
+
"@furystack/core": "^17.0.0",
|
|
42
|
+
"@furystack/inject": "^13.0.0",
|
|
43
|
+
"@furystack/utils": "^9.0.0",
|
|
44
44
|
"sequelize": "^6.37.8"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@types/node": "^25.
|
|
47
|
+
"@types/node": "^25.6.0",
|
|
48
48
|
"sqlite3": "6.0.1",
|
|
49
|
-
"typescript": "^6.0.
|
|
50
|
-
"vitest": "^4.1.
|
|
49
|
+
"typescript": "^6.0.3",
|
|
50
|
+
"vitest": "^4.1.4"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": ">=22.0.0"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Model } from 'sequelize'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import { defineSequelizeStore } from './define-sequelize-store.js'
|
|
4
|
+
|
|
5
|
+
class Item {
|
|
6
|
+
declare id: number
|
|
7
|
+
declare name: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class ItemSequelizeClass extends Model<Item, Item> implements Item {
|
|
11
|
+
declare id: number
|
|
12
|
+
declare name: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('defineSequelizeStore (metadata)', () => {
|
|
16
|
+
it('propagates name, lifetime, model and primaryKey onto the returned token', () => {
|
|
17
|
+
const token = defineSequelizeStore<Item, ItemSequelizeClass, 'id'>({
|
|
18
|
+
name: 'test/SequelizeItems',
|
|
19
|
+
model: Item,
|
|
20
|
+
sequelizeModel: ItemSequelizeClass,
|
|
21
|
+
primaryKey: 'id',
|
|
22
|
+
options: { dialect: 'sqlite', storage: ':memory:', logging: false },
|
|
23
|
+
})
|
|
24
|
+
expect(token.name).toBe('test/SequelizeItems')
|
|
25
|
+
expect(token.lifetime).toBe('singleton')
|
|
26
|
+
expect(token.model).toBe(Item)
|
|
27
|
+
expect(token.primaryKey).toBe('id')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('mints a distinct token identity per call even when options match -- declare once at module scope', () => {
|
|
31
|
+
const a = defineSequelizeStore<Item, ItemSequelizeClass, 'id'>({
|
|
32
|
+
name: 'test/SequelizeDupe',
|
|
33
|
+
model: Item,
|
|
34
|
+
sequelizeModel: ItemSequelizeClass,
|
|
35
|
+
primaryKey: 'id',
|
|
36
|
+
options: { dialect: 'sqlite', storage: ':memory:', logging: false },
|
|
37
|
+
})
|
|
38
|
+
const b = defineSequelizeStore<Item, ItemSequelizeClass, 'id'>({
|
|
39
|
+
name: 'test/SequelizeDupe',
|
|
40
|
+
model: Item,
|
|
41
|
+
sequelizeModel: ItemSequelizeClass,
|
|
42
|
+
primaryKey: 'id',
|
|
43
|
+
options: { dialect: 'sqlite', storage: ':memory:', logging: false },
|
|
44
|
+
})
|
|
45
|
+
expect(a.id).not.toBe(b.id)
|
|
46
|
+
})
|
|
47
|
+
})
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Constructable, StoreToken } from '@furystack/core'
|
|
2
|
+
import { defineStore } from '@furystack/core'
|
|
3
|
+
import type { Model, ModelStatic, Options, Sequelize } from 'sequelize'
|
|
4
|
+
import { SequelizeClientFactory } from './sequelize-client-factory.js'
|
|
5
|
+
import { SequelizeStore } from './sequelize-store.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Options accepted by {@link defineSequelizeStore}.
|
|
9
|
+
*/
|
|
10
|
+
export type DefineSequelizeStoreOptions<T extends object, M extends Model<T>, 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
|
+
/** The Sequelize Model class. */
|
|
16
|
+
sequelizeModel: ModelStatic<M>
|
|
17
|
+
/** Primary key field name. */
|
|
18
|
+
primaryKey: TPrimaryKey
|
|
19
|
+
/** Sequelize client options (dialect, storage, etc.). */
|
|
20
|
+
options: Options
|
|
21
|
+
/** Optional callback that initializes the Sequelize model. Runs once per store. */
|
|
22
|
+
initModel?: (sequelize: Sequelize) => Promise<void>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Mints a singleton {@link StoreToken} backed by a {@link SequelizeStore}.
|
|
27
|
+
*
|
|
28
|
+
* Internally resolves the singleton {@link SequelizeClientFactory} so every
|
|
29
|
+
* store declared against the same `options` shares one {@link Sequelize}
|
|
30
|
+
* client. Declare the token once at module scope — inline calls mint new
|
|
31
|
+
* identities and defeat singleton caching.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* export const UserStore = defineSequelizeStore({
|
|
36
|
+
* name: 'my-app/UserStore',
|
|
37
|
+
* model: User,
|
|
38
|
+
* sequelizeModel: UserSequelizeModel,
|
|
39
|
+
* primaryKey: 'username',
|
|
40
|
+
* options: { dialect: 'sqlite', storage: './data.sqlite' },
|
|
41
|
+
* initModel: async (sequelize) => { ... },
|
|
42
|
+
* })
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export const defineSequelizeStore = <T extends object, M extends Model<T>, const TPrimaryKey extends keyof T>(
|
|
46
|
+
opts: DefineSequelizeStoreOptions<T, M, TPrimaryKey>,
|
|
47
|
+
): StoreToken<T, TPrimaryKey> =>
|
|
48
|
+
defineStore<T, TPrimaryKey>({
|
|
49
|
+
name: opts.name,
|
|
50
|
+
model: opts.model,
|
|
51
|
+
primaryKey: opts.primaryKey,
|
|
52
|
+
factory: ({ inject }) => {
|
|
53
|
+
const factory = inject(SequelizeClientFactory)
|
|
54
|
+
return new SequelizeStore<T, M, TPrimaryKey>({
|
|
55
|
+
model: opts.model,
|
|
56
|
+
primaryKey: opts.primaryKey,
|
|
57
|
+
sequelizeModel: opts.sequelizeModel,
|
|
58
|
+
getSequelizeClient: () => factory.getSequelizeClient(opts.options),
|
|
59
|
+
initModel: opts.initModel,
|
|
60
|
+
})
|
|
61
|
+
},
|
|
62
|
+
})
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Token } from '@furystack/inject'
|
|
2
|
+
import { defineService } from '@furystack/inject'
|
|
2
3
|
import type { Options } from 'sequelize'
|
|
3
4
|
import { Op, Sequelize } from 'sequelize'
|
|
4
5
|
|
|
@@ -44,26 +45,37 @@ const operatorsAliases = {
|
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
/**
|
|
47
|
-
*
|
|
48
|
+
* Pools {@link Sequelize} client instances keyed by the serialized options so
|
|
49
|
+
* multiple stores against the same connection string share one client.
|
|
48
50
|
*/
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
public async [Symbol.asyncDispose]() {
|
|
54
|
-
await Promise.all([...this.connections.values()].map((c) => c.close()))
|
|
55
|
-
this.connections.clear()
|
|
56
|
-
}
|
|
51
|
+
export interface SequelizeClientFactory {
|
|
52
|
+
getSequelizeClient(options: Options): Sequelize
|
|
53
|
+
}
|
|
57
54
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Singleton token for the {@link SequelizeClientFactory}. Closes every pooled
|
|
57
|
+
* client when the owning injector is disposed.
|
|
58
|
+
*/
|
|
59
|
+
export const SequelizeClientFactory: Token<SequelizeClientFactory, 'singleton'> = defineService({
|
|
60
|
+
name: '@furystack/sequelize-store/SequelizeClientFactory',
|
|
61
|
+
lifetime: 'singleton',
|
|
62
|
+
factory: ({ onDispose }) => {
|
|
63
|
+
const connections = new Map<string, Sequelize>()
|
|
64
|
+
onDispose(async () => {
|
|
65
|
+
await Promise.all([...connections.values()].map((c) => c.close()))
|
|
66
|
+
connections.clear()
|
|
67
|
+
})
|
|
68
|
+
return {
|
|
69
|
+
getSequelizeClient(options: Options): Sequelize {
|
|
70
|
+
const key = JSON.stringify(options)
|
|
71
|
+
const existing = connections.get(key)
|
|
72
|
+
if (existing) {
|
|
73
|
+
return existing
|
|
74
|
+
}
|
|
75
|
+
const client = new Sequelize({ ...options, operatorsAliases })
|
|
76
|
+
connections.set(key, client)
|
|
77
|
+
return client
|
|
78
|
+
},
|
|
63
79
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
this.connections.set(key, client)
|
|
67
|
-
return client
|
|
68
|
-
}
|
|
69
|
-
}
|
|
80
|
+
},
|
|
81
|
+
})
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { TestClass, createStoreTest } from '@furystack/core/create-physical-store-tests'
|
|
2
|
-
import {
|
|
3
|
-
import { useSequelize } from './store-manager-helpers.js'
|
|
4
|
-
import { DataTypes, Model } from 'sequelize'
|
|
2
|
+
import { createInjector } from '@furystack/inject'
|
|
5
3
|
import { sleepAsync, usingAsync } from '@furystack/utils'
|
|
6
|
-
import {
|
|
4
|
+
import type { Sequelize } from 'sequelize'
|
|
5
|
+
import { DataTypes, Model } from 'sequelize'
|
|
6
|
+
import { describe, expect, it } from 'vitest'
|
|
7
|
+
import { defineSequelizeStore } from './define-sequelize-store.js'
|
|
7
8
|
import { SequelizeClientFactory } from './sequelize-client-factory.js'
|
|
8
9
|
import type { SequelizeStore } from './sequelize-store.js'
|
|
9
|
-
import { describe, it, expect } from 'vitest'
|
|
10
10
|
|
|
11
11
|
class TestSequelizeClass extends Model<TestClass, TestClass> implements TestClass {
|
|
12
12
|
declare id: number
|
|
@@ -18,13 +18,28 @@ class TestSequelizeClass extends Model<TestClass, TestClass> implements TestClas
|
|
|
18
18
|
declare dateValue: Date
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
const initTestModel = async (sequelize: Sequelize) => {
|
|
22
|
+
TestSequelizeClass.init(
|
|
23
|
+
{
|
|
24
|
+
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
|
|
25
|
+
stringValue1: { type: DataTypes.STRING },
|
|
26
|
+
stringValue2: { type: DataTypes.STRING },
|
|
27
|
+
numberValue1: { type: DataTypes.INTEGER },
|
|
28
|
+
numberValue2: { type: DataTypes.INTEGER },
|
|
29
|
+
booleanValue: { type: DataTypes.BOOLEAN },
|
|
30
|
+
dateValue: { type: DataTypes.DATE },
|
|
31
|
+
},
|
|
32
|
+
{ sequelize, timestamps: false },
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
21
36
|
describe('Sequelize Store', () => {
|
|
22
37
|
createStoreTest({
|
|
23
38
|
typeName: 'sequelize-store',
|
|
24
39
|
skipRegexTests: true,
|
|
25
40
|
createStore: (i) => {
|
|
26
|
-
|
|
27
|
-
|
|
41
|
+
const token = defineSequelizeStore<TestClass, TestSequelizeClass, 'id'>({
|
|
42
|
+
name: 'sequelize-store/TestClass',
|
|
28
43
|
model: TestClass,
|
|
29
44
|
sequelizeModel: TestSequelizeClass,
|
|
30
45
|
primaryKey: 'id',
|
|
@@ -33,45 +48,15 @@ describe('Sequelize Store', () => {
|
|
|
33
48
|
storage: ':memory:',
|
|
34
49
|
logging: false,
|
|
35
50
|
},
|
|
36
|
-
initModel:
|
|
37
|
-
TestSequelizeClass.init(
|
|
38
|
-
{
|
|
39
|
-
id: {
|
|
40
|
-
type: DataTypes.INTEGER,
|
|
41
|
-
autoIncrement: true,
|
|
42
|
-
primaryKey: true,
|
|
43
|
-
},
|
|
44
|
-
stringValue1: {
|
|
45
|
-
type: DataTypes.STRING,
|
|
46
|
-
},
|
|
47
|
-
stringValue2: {
|
|
48
|
-
type: DataTypes.STRING,
|
|
49
|
-
},
|
|
50
|
-
numberValue1: {
|
|
51
|
-
type: DataTypes.INTEGER,
|
|
52
|
-
},
|
|
53
|
-
numberValue2: {
|
|
54
|
-
type: DataTypes.INTEGER,
|
|
55
|
-
},
|
|
56
|
-
booleanValue: {
|
|
57
|
-
type: DataTypes.BOOLEAN,
|
|
58
|
-
},
|
|
59
|
-
dateValue: {
|
|
60
|
-
type: DataTypes.DATE,
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
{ sequelize, timestamps: false },
|
|
64
|
-
)
|
|
65
|
-
},
|
|
51
|
+
initModel: initTestModel,
|
|
66
52
|
})
|
|
67
|
-
|
|
68
|
-
return store
|
|
53
|
+
return i.get(token)
|
|
69
54
|
},
|
|
70
55
|
})
|
|
71
56
|
|
|
72
57
|
it('should return the cached sequelize client instance from the factory', async () => {
|
|
73
|
-
await usingAsync(
|
|
74
|
-
const factory = i.
|
|
58
|
+
await usingAsync(createInjector(), async (i) => {
|
|
59
|
+
const factory = i.get(SequelizeClientFactory)
|
|
75
60
|
const settings = {
|
|
76
61
|
dialect: 'sqlite',
|
|
77
62
|
storage: ':memory:',
|
|
@@ -84,9 +69,9 @@ describe('Sequelize Store', () => {
|
|
|
84
69
|
})
|
|
85
70
|
|
|
86
71
|
it('should return the cached model from the store class', async () => {
|
|
87
|
-
await usingAsync(
|
|
88
|
-
|
|
89
|
-
|
|
72
|
+
await usingAsync(createInjector(), async (i) => {
|
|
73
|
+
const token = defineSequelizeStore<TestClass, TestSequelizeClass, 'id'>({
|
|
74
|
+
name: 'sequelize-store/TestClass/cached-model',
|
|
90
75
|
model: TestClass,
|
|
91
76
|
sequelizeModel: TestSequelizeClass,
|
|
92
77
|
primaryKey: 'id',
|
|
@@ -97,39 +82,10 @@ describe('Sequelize Store', () => {
|
|
|
97
82
|
},
|
|
98
83
|
initModel: async (sequelize) => {
|
|
99
84
|
await sleepAsync(100)
|
|
100
|
-
|
|
101
|
-
{
|
|
102
|
-
id: {
|
|
103
|
-
type: DataTypes.INTEGER,
|
|
104
|
-
autoIncrement: true,
|
|
105
|
-
primaryKey: true,
|
|
106
|
-
},
|
|
107
|
-
stringValue1: {
|
|
108
|
-
type: DataTypes.STRING,
|
|
109
|
-
},
|
|
110
|
-
stringValue2: {
|
|
111
|
-
type: DataTypes.STRING,
|
|
112
|
-
},
|
|
113
|
-
numberValue1: {
|
|
114
|
-
type: DataTypes.INTEGER,
|
|
115
|
-
},
|
|
116
|
-
numberValue2: {
|
|
117
|
-
type: DataTypes.INTEGER,
|
|
118
|
-
},
|
|
119
|
-
booleanValue: {
|
|
120
|
-
type: DataTypes.BOOLEAN,
|
|
121
|
-
},
|
|
122
|
-
dateValue: {
|
|
123
|
-
type: DataTypes.DATE,
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
{ sequelize, timestamps: false },
|
|
127
|
-
)
|
|
85
|
+
await initTestModel(sequelize)
|
|
128
86
|
},
|
|
129
87
|
})
|
|
130
|
-
const store
|
|
131
|
-
.getInstance(StoreManager)
|
|
132
|
-
.getStoreFor(TestClass, 'id')
|
|
88
|
+
const store = i.get(token) as SequelizeStore<TestClass, TestSequelizeClass, 'id'>
|
|
133
89
|
|
|
134
90
|
const model1Promise = store.getModel()
|
|
135
91
|
const model2 = await store.getModel()
|
|
@@ -143,9 +99,9 @@ describe('Sequelize Store', () => {
|
|
|
143
99
|
})
|
|
144
100
|
|
|
145
101
|
it('should throw if the sequelize model is not initialized', async () => {
|
|
146
|
-
await usingAsync(
|
|
147
|
-
|
|
148
|
-
|
|
102
|
+
await usingAsync(createInjector(), async (i) => {
|
|
103
|
+
const token = defineSequelizeStore<TestClass, TestSequelizeClass, 'id'>({
|
|
104
|
+
name: 'sequelize-store/TestClass/no-init',
|
|
149
105
|
model: TestClass,
|
|
150
106
|
sequelizeModel: TestSequelizeClass,
|
|
151
107
|
primaryKey: 'id',
|
|
@@ -158,15 +114,13 @@ describe('Sequelize Store', () => {
|
|
|
158
114
|
// won't init the model here
|
|
159
115
|
},
|
|
160
116
|
})
|
|
161
|
-
const store
|
|
162
|
-
.getInstance(StoreManager)
|
|
163
|
-
.getStoreFor(TestClass, 'id')
|
|
117
|
+
const store = i.get(token) as SequelizeStore<TestClass, TestSequelizeClass, 'id'>
|
|
164
118
|
|
|
165
119
|
expect.assertions(1)
|
|
166
120
|
try {
|
|
167
121
|
await store.getModel()
|
|
168
122
|
} catch (error) {
|
|
169
|
-
expect((error as Error).message).toBe('TestSequelizeClass has not been defined')
|
|
123
|
+
expect((error as Error).message).toBe('TestSequelizeClass has not been defined')
|
|
170
124
|
}
|
|
171
125
|
})
|
|
172
126
|
})
|
package/src/sequelize-store.ts
CHANGED
|
@@ -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 { FindAttributeOptions, Identifier, Model, ModelStatic, Sequelize, WhereOptions } from 'sequelize'
|
|
12
12
|
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { Constructable, Injector } from '@furystack/inject';
|
|
2
|
-
import type { Options, Sequelize, ModelStatic, Model } from 'sequelize';
|
|
3
|
-
export declare const useSequelize: <T extends object, M extends ModelStatic<Model<T>>, Key extends keyof T>({ injector, model, sequelizeModel, primaryKey, options, initModel, }: {
|
|
4
|
-
injector: Injector;
|
|
5
|
-
/**
|
|
6
|
-
* The constructable model class
|
|
7
|
-
*/
|
|
8
|
-
model: Constructable<T>;
|
|
9
|
-
/**
|
|
10
|
-
* The Sequelize Model class
|
|
11
|
-
*/
|
|
12
|
-
sequelizeModel: M;
|
|
13
|
-
/**
|
|
14
|
-
* The name of the Primary Key property
|
|
15
|
-
*/
|
|
16
|
-
primaryKey: Key;
|
|
17
|
-
/**
|
|
18
|
-
* Optional options for the MongoDb Client
|
|
19
|
-
*/
|
|
20
|
-
options: Options;
|
|
21
|
-
/**
|
|
22
|
-
* A callback for model initialization
|
|
23
|
-
*/
|
|
24
|
-
initModel?: (sequelize: Sequelize) => Promise<void>;
|
|
25
|
-
}) => void;
|
|
26
|
-
//# 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,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AAIvE,eAAO,MAAM,YAAY,GAAa,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,SAAS,MAAM,CAAC,EAAE,sEAO3G;IACD,QAAQ,EAAE,QAAQ,CAAA;IAClB;;OAEG;IACH,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;IAEvB;;OAEG;IACH,cAAc,EAAE,CAAC,CAAA;IAEjB;;OAEG;IACH,UAAU,EAAE,GAAG,CAAA;IACf;;OAEG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB;;OAEG;IAEH,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACpD,SAUA,CAAA"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { addStore } from '@furystack/core';
|
|
2
|
-
import { SequelizeClientFactory } from './sequelize-client-factory.js';
|
|
3
|
-
import { SequelizeStore } from './sequelize-store.js';
|
|
4
|
-
export const useSequelize = function ({ injector, model, sequelizeModel, primaryKey, options, initModel, }) {
|
|
5
|
-
const clientFactory = injector.getInstance(SequelizeClientFactory);
|
|
6
|
-
const store = new SequelizeStore({
|
|
7
|
-
model,
|
|
8
|
-
primaryKey,
|
|
9
|
-
getSequelizeClient: () => clientFactory.getSequelizeClient(options),
|
|
10
|
-
initModel,
|
|
11
|
-
sequelizeModel,
|
|
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,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAErD,MAAM,CAAC,MAAM,YAAY,GAAG,UAAkF,EAC5G,QAAQ,EACR,KAAK,EACL,cAAc,EACd,UAAU,EACV,OAAO,EACP,SAAS,GA0BV;IACC,MAAM,aAAa,GAAG,QAAQ,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAA;IAClE,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC;QAC/B,KAAK;QACL,UAAU;QACV,kBAAkB,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,kBAAkB,CAAC,OAAO,CAAC;QACnE,SAAS;QACT,cAAc;KACf,CAAC,CAAA;IACF,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;AAC3B,CAAC,CAAA"}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { addStore } from '@furystack/core'
|
|
2
|
-
import type { Constructable, Injector } from '@furystack/inject'
|
|
3
|
-
import type { Options, Sequelize, ModelStatic, Model } from 'sequelize'
|
|
4
|
-
import { SequelizeClientFactory } from './sequelize-client-factory.js'
|
|
5
|
-
import { SequelizeStore } from './sequelize-store.js'
|
|
6
|
-
|
|
7
|
-
export const useSequelize = function <T extends object, M extends ModelStatic<Model<T>>, Key extends keyof T>({
|
|
8
|
-
injector,
|
|
9
|
-
model,
|
|
10
|
-
sequelizeModel,
|
|
11
|
-
primaryKey,
|
|
12
|
-
options,
|
|
13
|
-
initModel,
|
|
14
|
-
}: {
|
|
15
|
-
injector: Injector
|
|
16
|
-
/**
|
|
17
|
-
* The constructable model class
|
|
18
|
-
*/
|
|
19
|
-
model: Constructable<T>
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* The Sequelize Model class
|
|
23
|
-
*/
|
|
24
|
-
sequelizeModel: M
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* The name of the Primary Key property
|
|
28
|
-
*/
|
|
29
|
-
primaryKey: Key
|
|
30
|
-
/**
|
|
31
|
-
* Optional options for the MongoDb Client
|
|
32
|
-
*/
|
|
33
|
-
options: Options
|
|
34
|
-
/**
|
|
35
|
-
* A callback for model initialization
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
initModel?: (sequelize: Sequelize) => Promise<void>
|
|
39
|
-
}) {
|
|
40
|
-
const clientFactory = injector.getInstance(SequelizeClientFactory)
|
|
41
|
-
const store = new SequelizeStore({
|
|
42
|
-
model,
|
|
43
|
-
primaryKey,
|
|
44
|
-
getSequelizeClient: () => clientFactory.getSequelizeClient(options),
|
|
45
|
-
initModel,
|
|
46
|
-
sequelizeModel,
|
|
47
|
-
})
|
|
48
|
-
addStore(injector, store)
|
|
49
|
-
}
|