@technomoron/api-server-base 2.0.0-beta.15 → 2.0.0-beta.17

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 (35) hide show
  1. package/README.txt +17 -0
  2. package/dist/cjs/api-server-base.cjs +17 -0
  3. package/dist/cjs/api-server-base.d.ts +2 -0
  4. package/dist/cjs/auth-api/sql-auth-store.d.ts +11 -1
  5. package/dist/cjs/auth-api/sql-auth-store.js +35 -3
  6. package/dist/cjs/oauth/models.d.ts +6 -2
  7. package/dist/cjs/oauth/models.js +17 -6
  8. package/dist/cjs/oauth/sequelize.d.ts +13 -4
  9. package/dist/cjs/oauth/sequelize.js +27 -8
  10. package/dist/cjs/passkey/models.d.ts +6 -2
  11. package/dist/cjs/passkey/models.js +15 -4
  12. package/dist/cjs/passkey/sequelize.d.ts +7 -2
  13. package/dist/cjs/passkey/sequelize.js +22 -6
  14. package/dist/cjs/token/sequelize.d.ts +4 -1
  15. package/dist/cjs/token/sequelize.js +26 -7
  16. package/dist/cjs/user/sequelize.d.ts +7 -2
  17. package/dist/cjs/user/sequelize.js +18 -5
  18. package/dist/esm/api-server-base.d.ts +2 -0
  19. package/dist/esm/api-server-base.js +17 -0
  20. package/dist/esm/auth-api/sql-auth-store.d.ts +11 -1
  21. package/dist/esm/auth-api/sql-auth-store.js +35 -3
  22. package/dist/esm/oauth/models.d.ts +6 -2
  23. package/dist/esm/oauth/models.js +17 -6
  24. package/dist/esm/oauth/sequelize.d.ts +13 -4
  25. package/dist/esm/oauth/sequelize.js +27 -8
  26. package/dist/esm/passkey/models.d.ts +6 -2
  27. package/dist/esm/passkey/models.js +15 -4
  28. package/dist/esm/passkey/sequelize.d.ts +7 -2
  29. package/dist/esm/passkey/sequelize.js +22 -6
  30. package/dist/esm/token/sequelize.d.ts +4 -1
  31. package/dist/esm/token/sequelize.js +26 -7
  32. package/dist/esm/user/sequelize.d.ts +7 -2
  33. package/dist/esm/user/sequelize.js +18 -5
  34. package/docs/swagger/openapi.json +1 -1
  35. package/package.json +6 -6
package/README.txt CHANGED
@@ -102,6 +102,7 @@ apiBasePath (string, default '/api') Prefix applied to every module namespace.
102
102
  origins (string array, default empty array) CORS allowlist; empty allows all origins.
103
103
  uploadPath (string, default empty string) Enables multer.any() when provided.
104
104
  uploadMax (number, default 30 * 1024 * 1024) Maximum upload size in bytes.
105
+ staticDirs (record, default empty object) Map of mount path => disk path for serving static files as-is (ex: { '/assets': './public' }).
105
106
  accessSecret (string, default empty string) Required for JWT signing and verification.
106
107
  refreshSecret (string, default empty string) Used for refresh tokens if you implement them.
107
108
  cookieDomain (string, default '.somewhere-over-the-rainbow.com') Domain applied to auth cookies.
@@ -141,6 +142,22 @@ Use your storage adapter's filterUser helper to trim sensitive data before retur
141
142
  Provide your own authorize method to enforce role based access control using the ApiAuthClass enum.
142
143
  Create feature modules by extending ApiModule. Use the optional checkConfig hook to validate prerequisites before routes mount.
143
144
 
145
+ Sequelize Table Prefixes
146
+ ------------------------
147
+ Sequelize-backed stores accept `tablePrefix` to prepend to the built-in table names (`users`, `jwttokens`, `passkey_credentials`, `passkey_challenges`, `oauth_clients`, `oauth_codes`).
148
+
149
+ SqlAuthStore supports both a global prefix (`tablePrefix`) and per-module overrides (`tablePrefixes.user|token|passkey|oauth`). When present, `tokenStoreOptions.tablePrefix` and `oauthStoreOptions.tablePrefix` take precedence.
150
+
151
+ Example:
152
+
153
+ const store = new SqlAuthStore({
154
+ sequelize,
155
+ tablePrefix: 'myapp_'
156
+ });
157
+ // Creates tables like myapp_users, myapp_jwttokens, myapp_oauth_clients, ...
158
+
159
+ If you need a different base name (for example `myapp_tokens` instead of `myapp_jwttokens`), pass a custom model or model factory to the store and set the `tableName` yourself.
160
+
144
161
  Custom Express Endpoints
145
162
  ------------------------
146
163
  ApiModule routes run inside the tuple wrapper (always responding with a standardized JSON envelope). For endpoints that need raw Express control (streaming, webhooks, tus uploads, etc.), mount your own handlers directly.
@@ -343,6 +343,7 @@ function fillConfig(config) {
343
343
  apiHost: config.apiHost ?? 'localhost',
344
344
  uploadPath: config.uploadPath ?? '',
345
345
  uploadMax: config.uploadMax ?? 30 * 1024 * 1024,
346
+ staticDirs: config.staticDirs,
346
347
  origins: config.origins ?? [],
347
348
  debug: config.debug ?? false,
348
349
  apiBasePath: config.apiBasePath ?? '/api',
@@ -399,6 +400,7 @@ class ApiServer {
399
400
  this.app.use(upload.any());
400
401
  }
401
402
  this.middlewares();
403
+ this.installStaticDirs();
402
404
  this.installPingHandler();
403
405
  this.installSwaggerHandler();
404
406
  // addSwaggerUi(this.app);
@@ -656,6 +658,21 @@ class ApiServer {
656
658
  };
657
659
  this.app.use((0, cors_1.default)(corsOptions));
658
660
  }
661
+ installStaticDirs() {
662
+ const staticDirs = this.config.staticDirs;
663
+ if (!staticDirs || !isPlainObject(staticDirs)) {
664
+ return;
665
+ }
666
+ for (const [mountRaw, dirRaw] of Object.entries(staticDirs)) {
667
+ const mount = typeof mountRaw === 'string' ? mountRaw.trim() : '';
668
+ const dir = typeof dirRaw === 'string' ? dirRaw.trim() : '';
669
+ if (!mount || !dir) {
670
+ continue;
671
+ }
672
+ const resolvedMount = mount.startsWith('/') ? mount : `/${mount}`;
673
+ this.app.use(resolvedMount, express_1.default.static(dir));
674
+ }
675
+ }
659
676
  installPingHandler() {
660
677
  const path = `${this.apiBasePath}/v1/ping`;
661
678
  this.app.get(path, (_req, res) => {
@@ -92,6 +92,7 @@ export interface ApiServerConf {
92
92
  apiHost: string;
93
93
  uploadPath: string;
94
94
  uploadMax: number;
95
+ staticDirs?: Record<string, string>;
95
96
  origins: string[];
96
97
  debug: boolean;
97
98
  apiBasePath: string;
@@ -193,6 +194,7 @@ export declare class ApiServer {
193
194
  guessExceptionText(error: unknown, defMsg?: string): string;
194
195
  protected authorize(apiReq: ApiRequest, requiredClass: ApiAuthClass): Promise<void>;
195
196
  private middlewares;
197
+ private installStaticDirs;
196
198
  private installPingHandler;
197
199
  private loadSwaggerSpec;
198
200
  private installSwaggerHandler;
@@ -11,13 +11,23 @@ import type { Token } from '../token/types.js';
11
11
  interface PasskeyOptions extends Partial<PasskeyServiceConfig> {
12
12
  enabled?: boolean;
13
13
  }
14
+ export interface SqlAuthStoreTablePrefixes {
15
+ user?: string;
16
+ token?: string;
17
+ passkey?: string;
18
+ oauth?: string;
19
+ }
14
20
  export interface SqlAuthStoreParams<UserAttributes extends AuthUserAttributes = AuthUserAttributes, PublicUserShape extends Omit<UserAttributes, 'password'> = Omit<UserAttributes, 'password'>> {
15
21
  sequelize: Sequelize;
16
22
  syncOptions?: SyncOptions;
17
23
  bcryptRounds?: number;
18
24
  passwordPepper?: string;
25
+ tablePrefix?: string;
26
+ tablePrefixes?: SqlAuthStoreTablePrefixes;
19
27
  userModel?: GenericUserModelStatic;
20
- userModelFactory?: (sequelize: Sequelize) => GenericUserModelStatic;
28
+ userModelFactory?: (sequelize: Sequelize, options?: {
29
+ tablePrefix?: string;
30
+ }) => GenericUserModelStatic;
21
31
  userRecordMapper?: (model: GenericUserModel) => UserAttributes;
22
32
  publicUserMapper?: (user: UserAttributes) => PublicUserShape;
23
33
  passkeyUserMapper?: (user: UserAttributes) => PasskeyUserDescriptor;
@@ -13,6 +13,22 @@ const DEFAULT_PASSKEY_CONFIG = {
13
13
  timeoutMs: 5 * 60 * 1000,
14
14
  userVerification: 'preferred'
15
15
  };
16
+ function normalizeTablePrefix(prefix) {
17
+ if (!prefix) {
18
+ return undefined;
19
+ }
20
+ const trimmed = prefix.trim();
21
+ return trimmed.length > 0 ? trimmed : undefined;
22
+ }
23
+ function resolveTablePrefix(...prefixes) {
24
+ for (const prefix of prefixes) {
25
+ const normalized = normalizeTablePrefix(prefix);
26
+ if (normalized) {
27
+ return normalized;
28
+ }
29
+ }
30
+ return undefined;
31
+ }
16
32
  function isOriginString(origin) {
17
33
  return typeof origin === 'string' && origin.trim().length > 0;
18
34
  }
@@ -36,6 +52,8 @@ class SqlAuthStore {
36
52
  }
37
53
  this.sequelize = params.sequelize;
38
54
  this.syncOptions = params.syncOptions;
55
+ const moduleTablePrefixes = params.tablePrefixes ?? {};
56
+ const userTablePrefix = resolveTablePrefix(moduleTablePrefixes.user, params.tablePrefix);
39
57
  this.userStore = new sequelize_js_4.SequelizeUserStore({
40
58
  sequelize: this.sequelize,
41
59
  userModel: params.userModel,
@@ -43,18 +61,28 @@ class SqlAuthStore {
43
61
  recordMapper: params.userRecordMapper,
44
62
  toPublic: params.publicUserMapper,
45
63
  bcryptRounds: params.bcryptRounds,
46
- bcryptPepper: params.passwordPepper
64
+ bcryptPepper: params.passwordPepper,
65
+ tablePrefix: userTablePrefix
47
66
  });
67
+ const tokenTablePrefix = resolveTablePrefix(params.tokenStoreOptions?.tablePrefix, moduleTablePrefixes.token, params.tablePrefix);
48
68
  this.tokenStore =
49
- params.tokenStore ?? new sequelize_js_3.SequelizeTokenStore({ sequelize: this.sequelize, ...params.tokenStoreOptions });
69
+ params.tokenStore ??
70
+ new sequelize_js_3.SequelizeTokenStore({
71
+ sequelize: this.sequelize,
72
+ ...params.tokenStoreOptions,
73
+ tablePrefix: tokenTablePrefix
74
+ });
75
+ const oauthTablePrefix = resolveTablePrefix(params.oauthStoreOptions?.tablePrefix, moduleTablePrefixes.oauth, params.tablePrefix);
50
76
  this.oauthStore = new sequelize_js_1.SequelizeOAuthStore({
51
77
  sequelize: this.sequelize,
52
78
  ...params.oauthStoreOptions,
79
+ tablePrefix: oauthTablePrefix,
53
80
  bcryptRounds: params.bcryptRounds
54
81
  });
55
82
  let passkeyStore;
56
83
  let passkeyConfig;
57
84
  if (params.passkeys !== false) {
85
+ const passkeyTablePrefix = resolveTablePrefix(moduleTablePrefixes.passkey, params.tablePrefix);
58
86
  passkeyConfig = normalizePasskeyConfig(params.passkeys ?? {});
59
87
  const resolveUser = async (lookup) => {
60
88
  const found = await this.userStore.findUser(lookup.userId ?? lookup.login ?? '');
@@ -69,7 +97,11 @@ class SqlAuthStore {
69
97
  }));
70
98
  return mapper(found);
71
99
  };
72
- passkeyStore = new sequelize_js_2.SequelizePasskeyStore({ sequelize: this.sequelize, resolveUser });
100
+ passkeyStore = new sequelize_js_2.SequelizePasskeyStore({
101
+ sequelize: this.sequelize,
102
+ resolveUser,
103
+ tablePrefix: passkeyTablePrefix
104
+ });
73
105
  this.passkeyStore = passkeyStore;
74
106
  }
75
107
  this.adapter = new compat_auth_storage_js_1.CompositeAuthAdapter({
@@ -18,7 +18,9 @@ export declare class OAuthClientModel extends Model<OAuthClientAttributes, OAuth
18
18
  metadata: string | null;
19
19
  first_party: boolean;
20
20
  }
21
- export declare function initOAuthClientModel(sequelize: Sequelize): typeof OAuthClientModel;
21
+ export declare function initOAuthClientModel(sequelize: Sequelize, options?: {
22
+ tablePrefix?: string;
23
+ }): typeof OAuthClientModel;
22
24
  export interface OAuthCodeAttributes {
23
25
  code: string;
24
26
  client_id: string;
@@ -42,4 +44,6 @@ export declare class OAuthCodeModel extends Model<OAuthCodeAttributes, OAuthCode
42
44
  expires: Date;
43
45
  metadata: string | null;
44
46
  }
45
- export declare function initOAuthCodeModel(sequelize: Sequelize): typeof OAuthCodeModel;
47
+ export declare function initOAuthCodeModel(sequelize: Sequelize, options?: {
48
+ tablePrefix?: string;
49
+ }): typeof OAuthCodeModel;
@@ -5,11 +5,22 @@ exports.initOAuthClientModel = initOAuthClientModel;
5
5
  exports.initOAuthCodeModel = initOAuthCodeModel;
6
6
  const sequelize_1 = require("sequelize");
7
7
  const DIALECTS_SUPPORTING_UNSIGNED = new Set(['mysql', 'mariadb']);
8
+ function normalizeTablePrefix(prefix) {
9
+ if (!prefix) {
10
+ return undefined;
11
+ }
12
+ const trimmed = prefix.trim();
13
+ return trimmed.length > 0 ? trimmed : undefined;
14
+ }
15
+ function applyTablePrefix(prefix, tableName) {
16
+ const normalized = normalizeTablePrefix(prefix);
17
+ return normalized ? `${normalized}${tableName}` : tableName;
18
+ }
8
19
  function integerIdType(sequelize) {
9
20
  return DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect()) ? sequelize_1.DataTypes.INTEGER.UNSIGNED : sequelize_1.DataTypes.INTEGER;
10
21
  }
11
- function tableOptions(sequelize, tableName, extra) {
12
- const opts = { sequelize, tableName };
22
+ function tableOptions(sequelize, tableName, tablePrefix, extra) {
23
+ const opts = { sequelize, tableName: applyTablePrefix(tablePrefix, tableName) };
13
24
  if (extra) {
14
25
  Object.assign(opts, extra);
15
26
  }
@@ -22,7 +33,7 @@ function tableOptions(sequelize, tableName, extra) {
22
33
  class OAuthClientModel extends sequelize_1.Model {
23
34
  }
24
35
  exports.OAuthClientModel = OAuthClientModel;
25
- function initOAuthClientModel(sequelize) {
36
+ function initOAuthClientModel(sequelize, options = {}) {
26
37
  OAuthClientModel.init({
27
38
  client_id: { type: sequelize_1.DataTypes.STRING(128), allowNull: false, primaryKey: true },
28
39
  client_secret: { type: sequelize_1.DataTypes.STRING(255), allowNull: false, defaultValue: '' },
@@ -32,14 +43,14 @@ function initOAuthClientModel(sequelize) {
32
43
  metadata: { type: sequelize_1.DataTypes.TEXT, allowNull: true, defaultValue: null },
33
44
  first_party: { type: sequelize_1.DataTypes.BOOLEAN, allowNull: false, defaultValue: false }
34
45
  }, {
35
- ...tableOptions(sequelize, 'oauth_clients', { timestamps: false })
46
+ ...tableOptions(sequelize, 'oauth_clients', options.tablePrefix, { timestamps: false })
36
47
  });
37
48
  return OAuthClientModel;
38
49
  }
39
50
  class OAuthCodeModel extends sequelize_1.Model {
40
51
  }
41
52
  exports.OAuthCodeModel = OAuthCodeModel;
42
- function initOAuthCodeModel(sequelize) {
53
+ function initOAuthCodeModel(sequelize, options = {}) {
43
54
  const idType = integerIdType(sequelize);
44
55
  OAuthCodeModel.init({
45
56
  code: { type: sequelize_1.DataTypes.STRING(128), allowNull: false, primaryKey: true },
@@ -52,7 +63,7 @@ function initOAuthCodeModel(sequelize) {
52
63
  expires: { type: sequelize_1.DataTypes.DATE, allowNull: false },
53
64
  metadata: { type: sequelize_1.DataTypes.TEXT, allowNull: true, defaultValue: null }
54
65
  }, {
55
- ...tableOptions(sequelize, 'oauth_codes', { timestamps: false })
66
+ ...tableOptions(sequelize, 'oauth_codes', options.tablePrefix, { timestamps: false })
56
67
  });
57
68
  return OAuthCodeModel;
58
69
  }
@@ -19,7 +19,9 @@ export declare class OAuthClientModel extends Model<OAuthClientAttributes, OAuth
19
19
  metadata: string | null;
20
20
  first_party: boolean;
21
21
  }
22
- export declare function initOAuthClientModel(sequelize: Sequelize): typeof OAuthClientModel;
22
+ export declare function initOAuthClientModel(sequelize: Sequelize, options?: {
23
+ tablePrefix?: string;
24
+ }): typeof OAuthClientModel;
23
25
  export interface OAuthCodeAttributes {
24
26
  code: string;
25
27
  client_id: string;
@@ -43,13 +45,20 @@ export declare class OAuthCodeModel extends Model<OAuthCodeAttributes, OAuthCode
43
45
  expires: Date;
44
46
  metadata: string | null;
45
47
  }
46
- export declare function initOAuthCodeModel(sequelize: Sequelize): typeof OAuthCodeModel;
48
+ export declare function initOAuthCodeModel(sequelize: Sequelize, options?: {
49
+ tablePrefix?: string;
50
+ }): typeof OAuthCodeModel;
47
51
  export interface SequelizeOAuthStoreOptions {
48
52
  sequelize: Sequelize;
53
+ tablePrefix?: string;
49
54
  clientModel?: typeof OAuthClientModel;
50
55
  codeModel?: typeof OAuthCodeModel;
51
- clientModelFactory?: (sequelize: Sequelize) => typeof OAuthClientModel;
52
- codeModelFactory?: (sequelize: Sequelize) => typeof OAuthCodeModel;
56
+ clientModelFactory?: (sequelize: Sequelize, options?: {
57
+ tablePrefix?: string;
58
+ }) => typeof OAuthClientModel;
59
+ codeModelFactory?: (sequelize: Sequelize, options?: {
60
+ tablePrefix?: string;
61
+ }) => typeof OAuthCodeModel;
53
62
  bcryptRounds?: number;
54
63
  }
55
64
  export declare class SequelizeOAuthStore extends OAuthStore {
@@ -10,11 +10,22 @@ const bcryptjs_1 = __importDefault(require("bcryptjs"));
10
10
  const sequelize_1 = require("sequelize");
11
11
  const base_js_1 = require("./base.js");
12
12
  const DIALECTS_SUPPORTING_UNSIGNED = new Set(['mysql', 'mariadb']);
13
+ function normalizeTablePrefix(prefix) {
14
+ if (!prefix) {
15
+ return undefined;
16
+ }
17
+ const trimmed = prefix.trim();
18
+ return trimmed.length > 0 ? trimmed : undefined;
19
+ }
20
+ function applyTablePrefix(prefix, tableName) {
21
+ const normalized = normalizeTablePrefix(prefix);
22
+ return normalized ? `${normalized}${tableName}` : tableName;
23
+ }
13
24
  function integerIdType(sequelize) {
14
25
  return DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect()) ? sequelize_1.DataTypes.INTEGER.UNSIGNED : sequelize_1.DataTypes.INTEGER;
15
26
  }
16
- function tableOptions(sequelize, tableName, extra) {
17
- const opts = { sequelize, tableName };
27
+ function tableOptions(sequelize, tableName, tablePrefix, extra) {
28
+ const opts = { sequelize, tableName: applyTablePrefix(tablePrefix, tableName) };
18
29
  if (extra) {
19
30
  Object.assign(opts, extra);
20
31
  }
@@ -27,7 +38,7 @@ function tableOptions(sequelize, tableName, extra) {
27
38
  class OAuthClientModel extends sequelize_1.Model {
28
39
  }
29
40
  exports.OAuthClientModel = OAuthClientModel;
30
- function initOAuthClientModel(sequelize) {
41
+ function initOAuthClientModel(sequelize, options = {}) {
31
42
  OAuthClientModel.init({
32
43
  client_id: { type: sequelize_1.DataTypes.STRING(128), allowNull: false, primaryKey: true },
33
44
  client_secret: { type: sequelize_1.DataTypes.STRING(255), allowNull: false, defaultValue: '' },
@@ -36,13 +47,13 @@ function initOAuthClientModel(sequelize) {
36
47
  scope: { type: sequelize_1.DataTypes.TEXT, allowNull: false, defaultValue: '[]' },
37
48
  metadata: { type: sequelize_1.DataTypes.TEXT, allowNull: true, defaultValue: null },
38
49
  first_party: { type: sequelize_1.DataTypes.BOOLEAN, allowNull: false, defaultValue: false }
39
- }, tableOptions(sequelize, 'oauth_clients', { timestamps: false }));
50
+ }, tableOptions(sequelize, 'oauth_clients', options.tablePrefix, { timestamps: false }));
40
51
  return OAuthClientModel;
41
52
  }
42
53
  class OAuthCodeModel extends sequelize_1.Model {
43
54
  }
44
55
  exports.OAuthCodeModel = OAuthCodeModel;
45
- function initOAuthCodeModel(sequelize) {
56
+ function initOAuthCodeModel(sequelize, options = {}) {
46
57
  const idType = integerIdType(sequelize);
47
58
  OAuthCodeModel.init({
48
59
  code: { type: sequelize_1.DataTypes.STRING(128), allowNull: false, primaryKey: true },
@@ -54,7 +65,7 @@ function initOAuthCodeModel(sequelize) {
54
65
  code_challenge_method: { type: sequelize_1.DataTypes.STRING(10), allowNull: true, defaultValue: null },
55
66
  expires: { type: sequelize_1.DataTypes.DATE, allowNull: false },
56
67
  metadata: { type: sequelize_1.DataTypes.TEXT, allowNull: true, defaultValue: null }
57
- }, tableOptions(sequelize, 'oauth_codes', { timestamps: false }));
68
+ }, tableOptions(sequelize, 'oauth_codes', options.tablePrefix, { timestamps: false }));
58
69
  return OAuthCodeModel;
59
70
  }
60
71
  function encodeStringArray(values) {
@@ -114,8 +125,16 @@ class SequelizeOAuthStore extends base_js_1.OAuthStore {
114
125
  if (!options?.sequelize) {
115
126
  throw new Error('SequelizeOAuthStore requires an initialised Sequelize instance');
116
127
  }
117
- this.clients = options.clientModel ?? (options.clientModelFactory ?? initOAuthClientModel)(options.sequelize);
118
- this.codes = options.codeModel ?? (options.codeModelFactory ?? initOAuthCodeModel)(options.sequelize);
128
+ this.clients =
129
+ options.clientModel ??
130
+ (options.clientModelFactory ?? initOAuthClientModel)(options.sequelize, {
131
+ tablePrefix: options.tablePrefix
132
+ });
133
+ this.codes =
134
+ options.codeModel ??
135
+ (options.codeModelFactory ?? initOAuthCodeModel)(options.sequelize, {
136
+ tablePrefix: options.tablePrefix
137
+ });
119
138
  this.bcryptRounds = options.bcryptRounds ?? 12;
120
139
  }
121
140
  async getClient(clientId) {
@@ -26,5 +26,9 @@ export declare class PasskeyChallengeModel extends Model<InferAttributes<Passkey
26
26
  createdAt?: Date;
27
27
  updatedAt?: Date;
28
28
  }
29
- export declare function initPasskeyCredentialModel(sequelize: Sequelize): ModelStatic<PasskeyCredentialModel>;
30
- export declare function initPasskeyChallengeModel(sequelize: Sequelize): ModelStatic<PasskeyChallengeModel>;
29
+ export declare function initPasskeyCredentialModel(sequelize: Sequelize, options?: {
30
+ tablePrefix?: string;
31
+ }): ModelStatic<PasskeyCredentialModel>;
32
+ export declare function initPasskeyChallengeModel(sequelize: Sequelize, options?: {
33
+ tablePrefix?: string;
34
+ }): ModelStatic<PasskeyChallengeModel>;
@@ -5,6 +5,17 @@ exports.initPasskeyCredentialModel = initPasskeyCredentialModel;
5
5
  exports.initPasskeyChallengeModel = initPasskeyChallengeModel;
6
6
  const sequelize_1 = require("sequelize");
7
7
  const DIALECTS_SUPPORTING_UNSIGNED = new Set(['mysql', 'mariadb']);
8
+ function normalizeTablePrefix(prefix) {
9
+ if (!prefix) {
10
+ return undefined;
11
+ }
12
+ const trimmed = prefix.trim();
13
+ return trimmed.length > 0 ? trimmed : undefined;
14
+ }
15
+ function applyTablePrefix(prefix, tableName) {
16
+ const normalized = normalizeTablePrefix(prefix);
17
+ return normalized ? `${normalized}${tableName}` : tableName;
18
+ }
8
19
  function integerIdType(sequelize) {
9
20
  return DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect()) ? sequelize_1.DataTypes.INTEGER.UNSIGNED : sequelize_1.DataTypes.INTEGER;
10
21
  }
@@ -14,7 +25,7 @@ exports.PasskeyCredentialModel = PasskeyCredentialModel;
14
25
  class PasskeyChallengeModel extends sequelize_1.Model {
15
26
  }
16
27
  exports.PasskeyChallengeModel = PasskeyChallengeModel;
17
- function initPasskeyCredentialModel(sequelize) {
28
+ function initPasskeyCredentialModel(sequelize, options = {}) {
18
29
  const idType = integerIdType(sequelize);
19
30
  return PasskeyCredentialModel.init({
20
31
  credentialId: {
@@ -104,12 +115,12 @@ function initPasskeyCredentialModel(sequelize) {
104
115
  }
105
116
  }, {
106
117
  sequelize,
107
- tableName: 'passkey_credentials',
118
+ tableName: applyTablePrefix(options.tablePrefix, 'passkey_credentials'),
108
119
  timestamps: true,
109
120
  underscored: true
110
121
  });
111
122
  }
112
- function initPasskeyChallengeModel(sequelize) {
123
+ function initPasskeyChallengeModel(sequelize, options = {}) {
113
124
  const idType = integerIdType(sequelize);
114
125
  return PasskeyChallengeModel.init({
115
126
  challenge: {
@@ -137,7 +148,7 @@ function initPasskeyChallengeModel(sequelize) {
137
148
  }
138
149
  }, {
139
150
  sequelize,
140
- tableName: 'passkey_challenges',
151
+ tableName: applyTablePrefix(options.tablePrefix, 'passkey_challenges'),
141
152
  timestamps: true,
142
153
  underscored: true,
143
154
  indexes: [{ fields: ['expires_at'] }, { fields: ['user_id'] }]
@@ -31,10 +31,15 @@ declare class PasskeyChallengeModel extends Model<InferAttributes<PasskeyChallen
31
31
  }
32
32
  export interface SequelizePasskeyStoreOptions {
33
33
  sequelize: Sequelize;
34
+ tablePrefix?: string;
34
35
  credentialModel?: ModelStatic<PasskeyCredentialModel>;
35
36
  challengeModel?: ModelStatic<PasskeyChallengeModel>;
36
- credentialModelFactory?: (sequelize: Sequelize) => ModelStatic<PasskeyCredentialModel>;
37
- challengeModelFactory?: (sequelize: Sequelize) => ModelStatic<PasskeyChallengeModel>;
37
+ credentialModelFactory?: (sequelize: Sequelize, options?: {
38
+ tablePrefix?: string;
39
+ }) => ModelStatic<PasskeyCredentialModel>;
40
+ challengeModelFactory?: (sequelize: Sequelize, options?: {
41
+ tablePrefix?: string;
42
+ }) => ModelStatic<PasskeyChallengeModel>;
38
43
  resolveUser: (params: {
39
44
  userId?: AuthIdentifier;
40
45
  login?: string;
@@ -4,6 +4,17 @@ exports.SequelizePasskeyStore = void 0;
4
4
  const sequelize_1 = require("sequelize");
5
5
  const base_js_1 = require("./base.js");
6
6
  const DIALECTS_SUPPORTING_UNSIGNED = new Set(['mysql', 'mariadb']);
7
+ function normalizeTablePrefix(prefix) {
8
+ if (!prefix) {
9
+ return undefined;
10
+ }
11
+ const trimmed = prefix.trim();
12
+ return trimmed.length > 0 ? trimmed : undefined;
13
+ }
14
+ function applyTablePrefix(prefix, tableName) {
15
+ const normalized = normalizeTablePrefix(prefix);
16
+ return normalized ? `${normalized}${tableName}` : tableName;
17
+ }
7
18
  function integerIdType(sequelize) {
8
19
  return DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect()) ? sequelize_1.DataTypes.INTEGER.UNSIGNED : sequelize_1.DataTypes.INTEGER;
9
20
  }
@@ -23,7 +34,7 @@ class PasskeyCredentialModel extends sequelize_1.Model {
23
34
  }
24
35
  class PasskeyChallengeModel extends sequelize_1.Model {
25
36
  }
26
- function initPasskeyCredentialModel(sequelize) {
37
+ function initPasskeyCredentialModel(sequelize, options = {}) {
27
38
  const idType = integerIdType(sequelize);
28
39
  return PasskeyCredentialModel.init({
29
40
  credentialId: {
@@ -113,12 +124,12 @@ function initPasskeyCredentialModel(sequelize) {
113
124
  }
114
125
  }, {
115
126
  sequelize,
116
- tableName: 'passkey_credentials',
127
+ tableName: applyTablePrefix(options.tablePrefix, 'passkey_credentials'),
117
128
  timestamps: true,
118
129
  underscored: true
119
130
  });
120
131
  }
121
- function initPasskeyChallengeModel(sequelize) {
132
+ function initPasskeyChallengeModel(sequelize, options = {}) {
122
133
  const idType = integerIdType(sequelize);
123
134
  return PasskeyChallengeModel.init({
124
135
  challenge: {
@@ -146,7 +157,7 @@ function initPasskeyChallengeModel(sequelize) {
146
157
  }
147
158
  }, {
148
159
  sequelize,
149
- tableName: 'passkey_challenges',
160
+ tableName: applyTablePrefix(options.tablePrefix, 'passkey_challenges'),
150
161
  timestamps: true,
151
162
  underscored: true,
152
163
  indexes: [{ fields: ['expires_at'] }, { fields: ['user_id'] }]
@@ -161,9 +172,14 @@ class SequelizePasskeyStore extends base_js_1.PasskeyStore {
161
172
  this.resolveUserFn = options.resolveUser;
162
173
  this.credentials =
163
174
  options.credentialModel ??
164
- (options.credentialModelFactory ?? initPasskeyCredentialModel)(options.sequelize);
175
+ (options.credentialModelFactory ?? initPasskeyCredentialModel)(options.sequelize, {
176
+ tablePrefix: options.tablePrefix
177
+ });
165
178
  this.challenges =
166
- options.challengeModel ?? (options.challengeModelFactory ?? initPasskeyChallengeModel)(options.sequelize);
179
+ options.challengeModel ??
180
+ (options.challengeModelFactory ?? initPasskeyChallengeModel)(options.sequelize, {
181
+ tablePrefix: options.tablePrefix
182
+ });
167
183
  }
168
184
  async resolveUser(params) {
169
185
  return this.resolveUserFn(params);
@@ -27,8 +27,11 @@ export type TokenAttributes = InferAttributes<TokenModel>;
27
27
  export type TokenCreationAttributes = InferCreationAttributes<TokenModel>;
28
28
  export interface SequelizeTokenStoreOptions {
29
29
  sequelize: Sequelize;
30
+ tablePrefix?: string;
30
31
  tokenModel?: ModelStatic<TokenModel>;
31
- tokenModelFactory?: (sequelize: Sequelize) => ModelStatic<TokenModel>;
32
+ tokenModelFactory?: (sequelize: Sequelize, options?: {
33
+ tablePrefix?: string;
34
+ }) => ModelStatic<TokenModel>;
32
35
  }
33
36
  export declare class SequelizeTokenStore extends TokenStore {
34
37
  readonly Tokens: ModelStatic<TokenModel>;
@@ -4,12 +4,23 @@ exports.SequelizeTokenStore = void 0;
4
4
  const sequelize_1 = require("sequelize");
5
5
  const base_js_1 = require("./base.js");
6
6
  const DIALECTS_SUPPORTING_UNSIGNED = new Set(['mysql', 'mariadb']);
7
+ function normalizeTablePrefix(prefix) {
8
+ if (!prefix) {
9
+ return undefined;
10
+ }
11
+ const trimmed = prefix.trim();
12
+ return trimmed.length > 0 ? trimmed : undefined;
13
+ }
14
+ function applyTablePrefix(prefix, tableName) {
15
+ const normalized = normalizeTablePrefix(prefix);
16
+ return normalized ? `${normalized}${tableName}` : tableName;
17
+ }
7
18
  class TokenModel extends sequelize_1.Model {
8
19
  }
9
- function tokenTableOptions(sequelize) {
20
+ function tokenTableOptions(sequelize, tablePrefix) {
10
21
  const opts = {
11
22
  sequelize,
12
- tableName: 'jwttokens',
23
+ tableName: applyTablePrefix(tablePrefix, 'jwttokens'),
13
24
  timestamps: false
14
25
  };
15
26
  if (DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect())) {
@@ -18,7 +29,11 @@ function tokenTableOptions(sequelize) {
18
29
  }
19
30
  return opts;
20
31
  }
21
- function initTokenModel(sequelize) {
32
+ function initTokenModel(sequelize, options = {}) {
33
+ const tableName = applyTablePrefix(options.tablePrefix, 'jwttokens');
34
+ const usePrefixedIndexNames = tableName !== 'jwttokens';
35
+ const accessIndexName = usePrefixedIndexNames ? `${tableName}_access_unique` : 'jwt_access_unique';
36
+ const refreshIndexName = usePrefixedIndexNames ? `${tableName}_refresh_unique` : 'jwt_refresh_unique';
22
37
  TokenModel.init({
23
38
  token_id: {
24
39
  type: DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect())
@@ -120,10 +135,10 @@ function initTokenModel(sequelize) {
120
135
  defaultValue: '[]'
121
136
  }
122
137
  }, {
123
- ...tokenTableOptions(sequelize),
138
+ ...tokenTableOptions(sequelize, options.tablePrefix),
124
139
  indexes: [
125
- { name: 'jwt_access_unique', unique: true, fields: ['access'] },
126
- { name: 'jwt_refresh_unique', unique: true, fields: ['refresh'] }
140
+ { name: accessIndexName, unique: true, fields: ['access'] },
141
+ { name: refreshIndexName, unique: true, fields: ['refresh'] }
127
142
  ]
128
143
  });
129
144
  return TokenModel;
@@ -134,7 +149,11 @@ class SequelizeTokenStore extends base_js_1.TokenStore {
134
149
  if (!options?.sequelize) {
135
150
  throw new Error('SequelizeTokenStore requires an initialised Sequelize instance');
136
151
  }
137
- this.Tokens = options.tokenModel ?? (options.tokenModelFactory ?? initTokenModel)(options.sequelize);
152
+ this.Tokens =
153
+ options.tokenModel ??
154
+ (options.tokenModelFactory ?? initTokenModel)(options.sequelize, {
155
+ tablePrefix: options.tablePrefix
156
+ });
138
157
  }
139
158
  async save(record) {
140
159
  const normalized = this.normalizeToken(record);
@@ -10,15 +10,20 @@ export declare class AuthUserModel extends Model<InferAttributes<AuthUserModel>,
10
10
  }
11
11
  export type AuthUserAttributes = InferAttributes<AuthUserModel>;
12
12
  export type AuthUserCreationAttributes = InferCreationAttributes<AuthUserModel>;
13
- export declare function initAuthUserModel(sequelize: Sequelize): typeof AuthUserModel;
13
+ export declare function initAuthUserModel(sequelize: Sequelize, options?: {
14
+ tablePrefix?: string;
15
+ }): typeof AuthUserModel;
14
16
  export type GenericUserModel = Model<Record<string, unknown>, Record<string, unknown>>;
15
17
  export type GenericUserModelStatic = ModelStatic<GenericUserModel>;
16
18
  export interface SequelizeUserStoreOptions<UserAttributes extends AuthUserAttributes = AuthUserAttributes, PublicUserShape extends Omit<UserAttributes, 'password'> = Omit<UserAttributes, 'password'>> {
17
19
  bcryptRounds?: number;
18
20
  bcryptPepper?: string;
19
21
  sequelize: Sequelize;
22
+ tablePrefix?: string;
20
23
  userModel?: GenericUserModelStatic;
21
- userModelFactory?: (sequelize: Sequelize) => GenericUserModelStatic;
24
+ userModelFactory?: (sequelize: Sequelize, options?: {
25
+ tablePrefix?: string;
26
+ }) => GenericUserModelStatic;
22
27
  recordMapper?: (model: GenericUserModel) => UserAttributes;
23
28
  toPublic?: PublicUserMapper<UserAttributes, PublicUserShape>;
24
29
  }