@furystack/sequelize-store 6.0.49 → 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +23 -10
  3. package/esm/define-sequelize-store.d.ts +37 -0
  4. package/esm/define-sequelize-store.d.ts.map +1 -0
  5. package/esm/define-sequelize-store.js +39 -0
  6. package/esm/define-sequelize-store.js.map +1 -0
  7. package/esm/define-sequelize-store.spec.d.ts +2 -0
  8. package/esm/define-sequelize-store.spec.d.ts.map +1 -0
  9. package/esm/define-sequelize-store.spec.js +40 -0
  10. package/esm/define-sequelize-store.spec.js.map +1 -0
  11. package/esm/index.d.ts +2 -1
  12. package/esm/index.d.ts.map +1 -1
  13. package/esm/index.js +2 -1
  14. package/esm/index.js.map +1 -1
  15. package/esm/sequelize-client-factory.d.ts +9 -4
  16. package/esm/sequelize-client-factory.d.ts.map +1 -1
  17. package/esm/sequelize-client-factory.js +26 -29
  18. package/esm/sequelize-client-factory.js.map +1 -1
  19. package/esm/sequelize-store.d.ts +12 -18
  20. package/esm/sequelize-store.d.ts.map +1 -1
  21. package/esm/sequelize-store.js +11 -2
  22. package/esm/sequelize-store.js.map +1 -1
  23. package/esm/sequelize-store.spec.js +31 -76
  24. package/esm/sequelize-store.spec.js.map +1 -1
  25. package/package.json +6 -6
  26. package/src/define-sequelize-store.spec.ts +47 -0
  27. package/src/define-sequelize-store.ts +58 -0
  28. package/src/index.ts +2 -1
  29. package/src/sequelize-client-factory.ts +33 -21
  30. package/src/sequelize-store.spec.ts +36 -82
  31. package/src/sequelize-store.ts +15 -19
  32. package/esm/store-manager-helpers.d.ts +0 -26
  33. package/esm/store-manager-helpers.d.ts.map +0 -1
  34. package/esm/store-manager-helpers.js +0 -15
  35. package/esm/store-manager-helpers.js.map +0 -1
  36. package/src/store-manager-helpers.ts +0 -49
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [7.0.1] - 2026-05-21
4
+
5
+ ### ♻️ Refactoring
6
+
7
+ ### `update` throws `NotFoundError` for missing entities
8
+
9
+ `SequelizeStore.update` now throws `NotFoundError` from `@furystack/core` (instead of a plain `Error('Entity not found')`) when zero rows match the primary key. The new message also includes the offending id (`Entity not found with id 'X', cannot update`) so logs are actionable. Callers can branch on `instanceof NotFoundError` without parsing messages — string matches on the bare `Entity not found` keep working as a substring.
10
+
11
+ ### ⬆️ Dependencies
12
+
13
+ - Bump dev `vitest` to `^4.1.5`.
14
+ - Bumped `@types/node` to `^25.9.1` and `vitest` to `^4.1.7`. No source changes — dev-tooling bump only.
15
+
16
+ ### 📚 Documentation
17
+
18
+ - Rewrote JSDoc on `SequelizeStore`, `SequelizeStoreSettings`, and `defineSequelizeStore` to follow the new value-test guidance: dropped restate-the-type narration, called out the lazy-init + memoized `initModel` contract, and the fact that the store has no `[Symbol.asyncDispose]` because client lifetime belongs to `SequelizeClientFactory`.
19
+
20
+ ## [7.0.0] - 2026-04-25
21
+
22
+ ### 💥 Breaking Changes
23
+
24
+ Stores are now first-class DI tokens. See the [v7 migration guide](../../docs/migrations/v7-functional-di.md) for rationale, recipes, and pitfalls.
25
+
26
+ - Removed `useSequelize(...)`. Declare the store at module scope with `defineSequelizeStore<T, M, PK>({ name, model, sequelizeModel, primaryKey, options, initModel? })`.
27
+ - `SequelizeClientFactory` is now an exported interface + `defineService({ lifetime: 'singleton' })` token. Clients are pooled by `JSON.stringify(options)` and closed on injector teardown.
28
+
3
29
  ## [6.0.49] - 2026-04-17
4
30
 
5
31
  ### ⬆️ 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 { Injector } from '@furystack/inject'
17
- import { StoreManager, type PhysicalStore } from '@furystack/core'
18
- import { useSequelize } from '@furystack/sequelize-store'
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 myInjector = new Injector()
27
- useSequelize({
28
- injector: myInjector,
31
+ export const TestEntryStore = defineSequelizeStore<TestEntry, TestEntry, '_id'>({
32
+ name: 'my-app/TestEntryStore',
29
33
  model: TestEntry,
30
- sequelizeModel: TestEntry, // Your Sequelize Model class
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 myStore: PhysicalStore<TestEntry, '_id'> = myInjector.getInstance(StoreManager).getStoreFor(TestEntry, '_id')
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, 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.
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,37 @@
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
+ name: string;
8
+ model: Constructable<T>;
9
+ sequelizeModel: ModelStatic<M>;
10
+ primaryKey: TPrimaryKey;
11
+ /** Sequelize client options (dialect, storage, etc.) — also acts as the pool key. */
12
+ options: Options;
13
+ /** Runs once per store before the first DB call (e.g. `Model.init(...)` + `sequelize.sync()`). */
14
+ initModel?: (sequelize: Sequelize) => Promise<void>;
15
+ };
16
+ /**
17
+ * Mints a singleton {@link StoreToken} backed by a {@link SequelizeStore}.
18
+ *
19
+ * Internally resolves the singleton {@link SequelizeClientFactory} so every
20
+ * store declared against the same `options` shares one {@link Sequelize}
21
+ * client. Declare the token once at module scope — inline calls mint new
22
+ * identities and defeat singleton caching.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * export const UserStore = defineSequelizeStore({
27
+ * name: 'my-app/UserStore',
28
+ * model: User,
29
+ * sequelizeModel: UserSequelizeModel,
30
+ * primaryKey: 'username',
31
+ * options: { dialect: 'sqlite', storage: './data.sqlite' },
32
+ * initModel: async (sequelize) => { ... },
33
+ * })
34
+ * ```
35
+ */
36
+ export declare const defineSequelizeStore: <T extends object, M extends Model<T>, const TPrimaryKey extends keyof T>(opts: DefineSequelizeStoreOptions<T, M, TPrimaryKey>) => StoreToken<T, TPrimaryKey>;
37
+ //# 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,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;IACvB,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;IAC9B,UAAU,EAAE,WAAW,CAAA;IACvB,qFAAqF;IACrF,OAAO,EAAE,OAAO,CAAA;IAChB,kGAAkG;IAClG,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;AAgBrD;;;;;;;;;;;;;;;;;;;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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=define-sequelize-store.spec.d.ts.map
@@ -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
@@ -1,3 +1,4 @@
1
+ export * from './define-sequelize-store.js';
2
+ export * from './sequelize-client-factory.js';
1
3
  export * from './sequelize-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,sBAAsB,CAAA;AACpC,cAAc,4BAA4B,CAAA"}
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
@@ -1,3 +1,4 @@
1
+ export * from './define-sequelize-store.js';
2
+ export * from './sequelize-client-factory.js';
1
3
  export * from './sequelize-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,sBAAsB,CAAA;AACpC,cAAc,4BAA4B,CAAA"}
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
- * Factory for instantiating Sequelize clients
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 declare class SequelizeClientFactory implements AsyncDisposable {
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":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAM,SAAS,EAAE,MAAM,WAAW,CAAA;AA2CzC;;GAEG;AACH,qBACa,sBAAuB,YAAW,eAAe;IAC5D,OAAO,CAAC,WAAW,CAAoC;IAE1C,CAAC,MAAM,CAAC,YAAY,CAAC;IAK3B,kBAAkB,CAAC,OAAO,EAAE,OAAO;CAW3C"}
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
- 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';
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
- * Factory for instantiating Sequelize clients
44
+ * Singleton token for the {@link SequelizeClientFactory}. Closes every pooled
45
+ * client when the owning injector is disposed.
51
46
  */
52
- let SequelizeClientFactory = class SequelizeClientFactory {
53
- connections = new Map();
54
- async [Symbol.asyncDispose]() {
55
- await Promise.all([...this.connections.values()].map((c) => c.close()));
56
- this.connections.clear();
57
- }
58
- getSequelizeClient(options) {
59
- const key = JSON.stringify(options);
60
- const existing = this.connections.get(key);
61
- if (existing) {
62
- return existing;
63
- }
64
- const client = new Sequelize({ ...options, operatorsAliases });
65
- this.connections.set(key, client);
66
- return client;
67
- }
68
- };
69
- SequelizeClientFactory = __decorate([
70
- Injectable({ lifetime: 'singleton' })
71
- ], SequelizeClientFactory);
72
- export { SequelizeClientFactory };
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":"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"}
1
+ {"version":3,"file":"sequelize-client-factory.js","sourceRoot":"","sources":["../src/sequelize-client-factory.ts"],"names":[],"mappings":"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"}
@@ -1,31 +1,25 @@
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> {
6
- /**
7
- * The Model to use
8
- */
9
5
  model: Constructable<T>;
10
- /**
11
- * The Sequelize Model class
12
- */
13
6
  sequelizeModel: ModelStatic<M>;
14
- /**
15
- * The Primary key field
16
- */
17
7
  primaryKey: TPrimaryKey;
18
- /**
19
- * Callback that returns the Sequelize client (provided by the client factory)
20
- */
8
+ /** Resolves the pooled Sequelize client (typically delegated to {@link SequelizeClientFactory}). */
21
9
  getSequelizeClient: () => Sequelize;
22
- /**
23
- * Optional callback that will initialize the Model for the Sequelize instance
24
- */
10
+ /** Runs once before the first DB call (e.g. `Model.init(...)` + `sequelize.sync()`). */
25
11
  initModel?: (sequelize: Sequelize) => Promise<void>;
26
12
  }
27
13
  /**
28
- * Sequelize Store implementation for FuryStack
14
+ * {@link PhysicalStore} backed by a Sequelize Model.
15
+ *
16
+ * The Sequelize client is resolved lazily on the first DB call via
17
+ * {@link getModel}. {@link SequelizeStoreSettings.initModel} runs once and is
18
+ * memoized; if it throws the cached promise is cleared so the next call retries.
19
+ *
20
+ * The store has no `[Symbol.asyncDispose]` — client lifetime belongs to
21
+ * {@link SequelizeClientFactory}, which closes pooled clients on injector
22
+ * teardown.
29
23
  */
30
24
  export declare class SequelizeStore<T extends object, M extends Model<T>, TPrimaryKey extends keyof T, TWriteableData = WithOptionalId<T, TPrimaryKey>> extends EventHub<{
31
25
  onEntityAdded: {
@@ -1 +1 @@
1
- {"version":3,"file":"sequelize-store.d.ts","sourceRoot":"","sources":["../src/sequelize-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sequelize-store.d.ts","sourceRoot":"","sources":["../src/sequelize-store.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,15 @@
1
+ import { NotFoundError } from '@furystack/core';
1
2
  import { EventHub } from '@furystack/utils';
2
3
  /**
3
- * Sequelize Store implementation for FuryStack
4
+ * {@link PhysicalStore} backed by a Sequelize Model.
5
+ *
6
+ * The Sequelize client is resolved lazily on the first DB call via
7
+ * {@link getModel}. {@link SequelizeStoreSettings.initModel} runs once and is
8
+ * memoized; if it throws the cached promise is cleared so the next call retries.
9
+ *
10
+ * The store has no `[Symbol.asyncDispose]` — client lifetime belongs to
11
+ * {@link SequelizeClientFactory}, which closes pooled clients on injector
12
+ * teardown.
4
13
  */
5
14
  export class SequelizeStore extends EventHub {
6
15
  options;
@@ -56,7 +65,7 @@ export class SequelizeStore extends EventHub {
56
65
  const model = await this.getModel();
57
66
  const result = await model.update(data, { where: { [this.primaryKey]: id } });
58
67
  if (result[0] < 1) {
59
- throw Error('Entity not found');
68
+ throw new NotFoundError(`Entity not found with id '${String(id)}', cannot update`);
60
69
  }
61
70
  this.emit('onEntityUpdated', { id, change: data });
62
71
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sequelize-store.js","sourceRoot":"","sources":["../src/sequelize-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sequelize-store.js","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 { StoreManager } from '@furystack/core';
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 { Injector } from '@furystack/inject';
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
- useSequelize({
17
- injector: i,
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: async (sequelize) => {
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
- const store = i.getInstance(StoreManager).getStoreFor(TestClass, 'id');
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(new Injector(), async (i) => {
60
- const factory = i.getInstance(SequelizeClientFactory);
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(new Injector(), async (i) => {
73
- useSequelize({
74
- injector: i,
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
- TestSequelizeClass.init({
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(new Injector(), async (i) => {
125
- useSequelize({
126
- injector: i,
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'); // will be thrown by sequelize
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":"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"}
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": "6.0.49",
3
+ "version": "7.0.1",
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": "^16.0.4",
42
- "@furystack/inject": "^12.0.36",
43
- "@furystack/utils": "^8.2.5",
41
+ "@furystack/core": "^17.1.0",
42
+ "@furystack/inject": "^13.0.1",
43
+ "@furystack/utils": "^9.0.1",
44
44
  "sequelize": "^6.37.8"
45
45
  },
46
46
  "devDependencies": {
47
- "@types/node": "^25.6.0",
47
+ "@types/node": "^25.9.1",
48
48
  "sqlite3": "6.0.1",
49
49
  "typescript": "^6.0.3",
50
- "vitest": "^4.1.4"
50
+ "vitest": "^4.1.7"
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,58 @@
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
+ name: string
12
+ model: Constructable<T>
13
+ sequelizeModel: ModelStatic<M>
14
+ primaryKey: TPrimaryKey
15
+ /** Sequelize client options (dialect, storage, etc.) — also acts as the pool key. */
16
+ options: Options
17
+ /** Runs once per store before the first DB call (e.g. `Model.init(...)` + `sequelize.sync()`). */
18
+ initModel?: (sequelize: Sequelize) => Promise<void>
19
+ }
20
+
21
+ /**
22
+ * Mints a singleton {@link StoreToken} backed by a {@link SequelizeStore}.
23
+ *
24
+ * Internally resolves the singleton {@link SequelizeClientFactory} so every
25
+ * store declared against the same `options` shares one {@link Sequelize}
26
+ * client. Declare the token once at module scope — inline calls mint new
27
+ * identities and defeat singleton caching.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * export const UserStore = defineSequelizeStore({
32
+ * name: 'my-app/UserStore',
33
+ * model: User,
34
+ * sequelizeModel: UserSequelizeModel,
35
+ * primaryKey: 'username',
36
+ * options: { dialect: 'sqlite', storage: './data.sqlite' },
37
+ * initModel: async (sequelize) => { ... },
38
+ * })
39
+ * ```
40
+ */
41
+ export const defineSequelizeStore = <T extends object, M extends Model<T>, const TPrimaryKey extends keyof T>(
42
+ opts: DefineSequelizeStoreOptions<T, M, TPrimaryKey>,
43
+ ): StoreToken<T, TPrimaryKey> =>
44
+ defineStore<T, TPrimaryKey>({
45
+ name: opts.name,
46
+ model: opts.model,
47
+ primaryKey: opts.primaryKey,
48
+ factory: ({ inject }) => {
49
+ const factory = inject(SequelizeClientFactory)
50
+ return new SequelizeStore<T, M, TPrimaryKey>({
51
+ model: opts.model,
52
+ primaryKey: opts.primaryKey,
53
+ sequelizeModel: opts.sequelizeModel,
54
+ getSequelizeClient: () => factory.getSequelizeClient(opts.options),
55
+ initModel: opts.initModel,
56
+ })
57
+ },
58
+ })
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
+ export * from './define-sequelize-store.js'
2
+ export * from './sequelize-client-factory.js'
1
3
  export * from './sequelize-store.js'
2
- export * from './store-manager-helpers.js'
@@ -1,4 +1,5 @@
1
- import { Injectable } from '@furystack/inject'
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
- * Factory for instantiating Sequelize clients
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
- @Injectable({ lifetime: 'singleton' })
50
- export class SequelizeClientFactory implements AsyncDisposable {
51
- private connections: Map<string, Sequelize> = new Map()
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
- public getSequelizeClient(options: Options) {
59
- const key = JSON.stringify(options)
60
- const existing = this.connections.get(key)
61
- if (existing) {
62
- return existing
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
- const client = new Sequelize({ ...options, operatorsAliases })
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 { StoreManager } from '@furystack/core'
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 { Injector } from '@furystack/inject'
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
- useSequelize({
27
- injector: i,
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: async (sequelize) => {
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
- const store = i.getInstance(StoreManager).getStoreFor(TestClass, 'id')
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(new Injector(), async (i) => {
74
- const factory = i.getInstance(SequelizeClientFactory)
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(new Injector(), async (i) => {
88
- useSequelize({
89
- injector: i,
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
- TestSequelizeClass.init(
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: SequelizeStore<TestClass, TestSequelizeClass, 'id'> = i
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(new Injector(), async (i) => {
147
- useSequelize({
148
- injector: i,
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: SequelizeStore<TestClass, TestSequelizeClass, 'id'> = i
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') // will be thrown by sequelize
123
+ expect((error as Error).message).toBe('TestSequelizeClass has not been defined')
170
124
  }
171
125
  })
172
126
  })
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ Constructable,
2
3
  CreateResult,
3
4
  FilterType,
4
5
  FindOptions,
@@ -6,36 +7,31 @@ import type {
6
7
  PhysicalStore,
7
8
  WithOptionalId,
8
9
  } from '@furystack/core'
9
- import type { Constructable } from '@furystack/inject'
10
+ import { NotFoundError } from '@furystack/core'
10
11
  import { EventHub } from '@furystack/utils'
11
12
  import type { FindAttributeOptions, Identifier, Model, ModelStatic, Sequelize, WhereOptions } from 'sequelize'
13
+ import type { SequelizeClientFactory } from './sequelize-client-factory.js'
12
14
 
13
15
  export interface SequelizeStoreSettings<T extends object, M extends Model<T>, TPrimaryKey extends keyof T> {
14
- /**
15
- * The Model to use
16
- */
17
16
  model: Constructable<T>
18
-
19
- /**
20
- * The Sequelize Model class
21
- */
22
17
  sequelizeModel: ModelStatic<M>
23
- /**
24
- * The Primary key field
25
- */
26
18
  primaryKey: TPrimaryKey
27
- /**
28
- * Callback that returns the Sequelize client (provided by the client factory)
29
- */
19
+ /** Resolves the pooled Sequelize client (typically delegated to {@link SequelizeClientFactory}). */
30
20
  getSequelizeClient: () => Sequelize
31
- /**
32
- * Optional callback that will initialize the Model for the Sequelize instance
33
- */
21
+ /** Runs once before the first DB call (e.g. `Model.init(...)` + `sequelize.sync()`). */
34
22
  initModel?: (sequelize: Sequelize) => Promise<void>
35
23
  }
36
24
 
37
25
  /**
38
- * Sequelize Store implementation for FuryStack
26
+ * {@link PhysicalStore} backed by a Sequelize Model.
27
+ *
28
+ * The Sequelize client is resolved lazily on the first DB call via
29
+ * {@link getModel}. {@link SequelizeStoreSettings.initModel} runs once and is
30
+ * memoized; if it throws the cached promise is cleared so the next call retries.
31
+ *
32
+ * The store has no `[Symbol.asyncDispose]` — client lifetime belongs to
33
+ * {@link SequelizeClientFactory}, which closes pooled clients on injector
34
+ * teardown.
39
35
  */
40
36
  export class SequelizeStore<
41
37
  T extends object,
@@ -113,7 +109,7 @@ export class SequelizeStore<
113
109
 
114
110
  const result = await model.update(data, { where: { [this.primaryKey]: id } as WhereOptions<T> })
115
111
  if (result[0] < 1) {
116
- throw Error('Entity not found')
112
+ throw new NotFoundError(`Entity not found with id '${String(id)}', cannot update`)
117
113
  }
118
114
  this.emit('onEntityUpdated', { id, change: data })
119
115
  }
@@ -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
- }