@technomoron/apicore-server 1.0.0-beta.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 (171) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/api-module.cjs +34 -0
  3. package/dist/cjs/api-module.d.ts +45 -0
  4. package/dist/cjs/apicore-server.cjs +1561 -0
  5. package/dist/cjs/apicore-server.d.ts +288 -0
  6. package/dist/cjs/auth-api/auth-module.cjs +1248 -0
  7. package/dist/cjs/auth-api/auth-module.d.ts +116 -0
  8. package/dist/cjs/auth-api/compat-auth-storage.cjs +128 -0
  9. package/dist/cjs/auth-api/compat-auth-storage.d.ts +57 -0
  10. package/dist/cjs/auth-api/mem-auth-store.cjs +121 -0
  11. package/dist/cjs/auth-api/mem-auth-store.d.ts +68 -0
  12. package/dist/cjs/auth-api/module.cjs +25 -0
  13. package/dist/cjs/auth-api/module.d.ts +20 -0
  14. package/dist/cjs/auth-api/schemas.cjs +171 -0
  15. package/dist/cjs/auth-api/schemas.d.ts +21 -0
  16. package/dist/cjs/auth-api/sql-auth-store.cjs +179 -0
  17. package/dist/cjs/auth-api/sql-auth-store.d.ts +87 -0
  18. package/dist/cjs/auth-api/storage.cjs +102 -0
  19. package/dist/cjs/auth-api/storage.d.ts +38 -0
  20. package/dist/cjs/auth-api/types.cjs +2 -0
  21. package/dist/cjs/auth-api/types.d.ts +34 -0
  22. package/dist/cjs/auth-api/user-id.cjs +47 -0
  23. package/dist/cjs/auth-api/user-id.d.ts +5 -0
  24. package/dist/cjs/auth-cookie-options.cjs +66 -0
  25. package/dist/cjs/auth-cookie-options.d.ts +13 -0
  26. package/dist/cjs/base/client-info.cjs +285 -0
  27. package/dist/cjs/base/client-info.d.ts +27 -0
  28. package/dist/cjs/base/error-utils.cjs +50 -0
  29. package/dist/cjs/base/error-utils.d.ts +16 -0
  30. package/dist/cjs/base/request-utils.cjs +27 -0
  31. package/dist/cjs/base/request-utils.d.ts +8 -0
  32. package/dist/cjs/index.cjs +51 -0
  33. package/dist/cjs/index.d.ts +34 -0
  34. package/dist/cjs/limiter/auth-rate-limiter.cjs +35 -0
  35. package/dist/cjs/limiter/auth-rate-limiter.d.ts +12 -0
  36. package/dist/cjs/limiter/fixed-window.cjs +41 -0
  37. package/dist/cjs/limiter/fixed-window.d.ts +11 -0
  38. package/dist/cjs/oauth/base.cjs +7 -0
  39. package/dist/cjs/oauth/base.d.ts +17 -0
  40. package/dist/cjs/oauth/memory.cjs +135 -0
  41. package/dist/cjs/oauth/memory.d.ts +22 -0
  42. package/dist/cjs/oauth/models.cjs +47 -0
  43. package/dist/cjs/oauth/models.d.ts +50 -0
  44. package/dist/cjs/oauth/sequelize.cjs +159 -0
  45. package/dist/cjs/oauth/sequelize.d.ts +30 -0
  46. package/dist/cjs/oauth/types.cjs +3 -0
  47. package/dist/cjs/oauth/types.d.ts +51 -0
  48. package/dist/cjs/passkey/base.cjs +7 -0
  49. package/dist/cjs/passkey/base.d.ts +28 -0
  50. package/dist/cjs/passkey/config.cjs +26 -0
  51. package/dist/cjs/passkey/config.d.ts +2 -0
  52. package/dist/cjs/passkey/memory.cjs +123 -0
  53. package/dist/cjs/passkey/memory.d.ts +34 -0
  54. package/dist/cjs/passkey/models.cjs +142 -0
  55. package/dist/cjs/passkey/models.d.ts +34 -0
  56. package/dist/cjs/passkey/sequelize.cjs +126 -0
  57. package/dist/cjs/passkey/sequelize.d.ts +42 -0
  58. package/dist/cjs/passkey/service.cjs +413 -0
  59. package/dist/cjs/passkey/service.d.ts +21 -0
  60. package/dist/cjs/passkey/types.cjs +2 -0
  61. package/dist/cjs/passkey/types.d.ts +84 -0
  62. package/dist/cjs/sequelize-utils.cjs +56 -0
  63. package/dist/cjs/sequelize-utils.d.ts +8 -0
  64. package/dist/cjs/token/base.cjs +120 -0
  65. package/dist/cjs/token/base.d.ts +46 -0
  66. package/dist/cjs/token/memory.cjs +234 -0
  67. package/dist/cjs/token/memory.d.ts +29 -0
  68. package/dist/cjs/token/sequelize.cjs +400 -0
  69. package/dist/cjs/token/sequelize.d.ts +58 -0
  70. package/dist/cjs/token/types.cjs +2 -0
  71. package/dist/cjs/token/types.d.ts +34 -0
  72. package/dist/cjs/upload/memory.cjs +92 -0
  73. package/dist/cjs/upload/memory.d.ts +17 -0
  74. package/dist/cjs/upload/tus-module.cjs +270 -0
  75. package/dist/cjs/upload/tus-module.d.ts +38 -0
  76. package/dist/cjs/upload/types.cjs +2 -0
  77. package/dist/cjs/upload/types.d.ts +28 -0
  78. package/dist/cjs/user/base.cjs +53 -0
  79. package/dist/cjs/user/base.d.ts +36 -0
  80. package/dist/cjs/user/memory.cjs +194 -0
  81. package/dist/cjs/user/memory.d.ts +37 -0
  82. package/dist/cjs/user/sequelize.cjs +194 -0
  83. package/dist/cjs/user/sequelize.d.ts +46 -0
  84. package/dist/cjs/user/types.cjs +2 -0
  85. package/dist/cjs/user/types.d.ts +11 -0
  86. package/dist/esm/api-module.d.ts +45 -0
  87. package/dist/esm/api-module.js +30 -0
  88. package/dist/esm/apicore-server.d.ts +288 -0
  89. package/dist/esm/apicore-server.js +1552 -0
  90. package/dist/esm/auth-api/auth-module.d.ts +116 -0
  91. package/dist/esm/auth-api/auth-module.js +1246 -0
  92. package/dist/esm/auth-api/compat-auth-storage.d.ts +57 -0
  93. package/dist/esm/auth-api/compat-auth-storage.js +124 -0
  94. package/dist/esm/auth-api/mem-auth-store.d.ts +68 -0
  95. package/dist/esm/auth-api/mem-auth-store.js +117 -0
  96. package/dist/esm/auth-api/module.d.ts +20 -0
  97. package/dist/esm/auth-api/module.js +21 -0
  98. package/dist/esm/auth-api/schemas.d.ts +21 -0
  99. package/dist/esm/auth-api/schemas.js +168 -0
  100. package/dist/esm/auth-api/sql-auth-store.d.ts +87 -0
  101. package/dist/esm/auth-api/sql-auth-store.js +175 -0
  102. package/dist/esm/auth-api/storage.d.ts +38 -0
  103. package/dist/esm/auth-api/storage.js +98 -0
  104. package/dist/esm/auth-api/types.d.ts +34 -0
  105. package/dist/esm/auth-api/types.js +1 -0
  106. package/dist/esm/auth-api/user-id.d.ts +5 -0
  107. package/dist/esm/auth-api/user-id.js +41 -0
  108. package/dist/esm/auth-cookie-options.d.ts +13 -0
  109. package/dist/esm/auth-cookie-options.js +63 -0
  110. package/dist/esm/base/client-info.d.ts +27 -0
  111. package/dist/esm/base/client-info.js +282 -0
  112. package/dist/esm/base/error-utils.d.ts +16 -0
  113. package/dist/esm/base/error-utils.js +44 -0
  114. package/dist/esm/base/request-utils.d.ts +8 -0
  115. package/dist/esm/base/request-utils.js +23 -0
  116. package/dist/esm/index.d.ts +34 -0
  117. package/dist/esm/index.js +21 -0
  118. package/dist/esm/limiter/auth-rate-limiter.d.ts +12 -0
  119. package/dist/esm/limiter/auth-rate-limiter.js +32 -0
  120. package/dist/esm/limiter/fixed-window.d.ts +11 -0
  121. package/dist/esm/limiter/fixed-window.js +37 -0
  122. package/dist/esm/oauth/base.d.ts +17 -0
  123. package/dist/esm/oauth/base.js +3 -0
  124. package/dist/esm/oauth/memory.d.ts +22 -0
  125. package/dist/esm/oauth/memory.js +128 -0
  126. package/dist/esm/oauth/models.d.ts +50 -0
  127. package/dist/esm/oauth/models.js +38 -0
  128. package/dist/esm/oauth/sequelize.d.ts +30 -0
  129. package/dist/esm/oauth/sequelize.js +148 -0
  130. package/dist/esm/oauth/types.d.ts +51 -0
  131. package/dist/esm/oauth/types.js +2 -0
  132. package/dist/esm/passkey/base.d.ts +28 -0
  133. package/dist/esm/passkey/base.js +3 -0
  134. package/dist/esm/passkey/config.d.ts +2 -0
  135. package/dist/esm/passkey/config.js +23 -0
  136. package/dist/esm/passkey/memory.d.ts +34 -0
  137. package/dist/esm/passkey/memory.js +119 -0
  138. package/dist/esm/passkey/models.d.ts +34 -0
  139. package/dist/esm/passkey/models.js +135 -0
  140. package/dist/esm/passkey/sequelize.d.ts +42 -0
  141. package/dist/esm/passkey/sequelize.js +122 -0
  142. package/dist/esm/passkey/service.d.ts +21 -0
  143. package/dist/esm/passkey/service.js +376 -0
  144. package/dist/esm/passkey/types.d.ts +84 -0
  145. package/dist/esm/passkey/types.js +1 -0
  146. package/dist/esm/sequelize-utils.d.ts +8 -0
  147. package/dist/esm/sequelize-utils.js +47 -0
  148. package/dist/esm/token/base.d.ts +46 -0
  149. package/dist/esm/token/base.js +113 -0
  150. package/dist/esm/token/memory.d.ts +29 -0
  151. package/dist/esm/token/memory.js +230 -0
  152. package/dist/esm/token/sequelize.d.ts +58 -0
  153. package/dist/esm/token/sequelize.js +396 -0
  154. package/dist/esm/token/types.d.ts +34 -0
  155. package/dist/esm/token/types.js +1 -0
  156. package/dist/esm/upload/memory.d.ts +17 -0
  157. package/dist/esm/upload/memory.js +86 -0
  158. package/dist/esm/upload/tus-module.d.ts +38 -0
  159. package/dist/esm/upload/tus-module.js +266 -0
  160. package/dist/esm/upload/types.d.ts +28 -0
  161. package/dist/esm/upload/types.js +1 -0
  162. package/dist/esm/user/base.d.ts +36 -0
  163. package/dist/esm/user/base.js +46 -0
  164. package/dist/esm/user/memory.d.ts +37 -0
  165. package/dist/esm/user/memory.js +190 -0
  166. package/dist/esm/user/sequelize.d.ts +46 -0
  167. package/dist/esm/user/sequelize.js +188 -0
  168. package/dist/esm/user/types.d.ts +11 -0
  169. package/dist/esm/user/types.js +1 -0
  170. package/docs/swagger/openapi.json +2162 -0
  171. package/package.json +131 -0
@@ -0,0 +1,400 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SequelizeTokenStore = void 0;
4
+ const sequelize_1 = require("sequelize");
5
+ const user_id_js_1 = require("../auth-api/user-id.cjs");
6
+ const sequelize_utils_js_1 = require("../sequelize-utils.cjs");
7
+ const base_js_1 = require("./base.cjs");
8
+ class TokenModel extends sequelize_1.Model {
9
+ }
10
+ function initTokenModel(sequelize, options = {}) {
11
+ const tableName = (0, sequelize_utils_js_1.applyTablePrefix)(options.tablePrefix, 'jwttokens');
12
+ const usePrefixedIndexNames = tableName !== 'jwttokens';
13
+ const accessIndexName = usePrefixedIndexNames ? `${tableName}_access_unique` : 'jwt_access_unique';
14
+ const refreshIndexName = usePrefixedIndexNames ? `${tableName}_refresh_unique` : 'jwt_refresh_unique';
15
+ TokenModel.init({
16
+ token_id: {
17
+ type: (0, sequelize_utils_js_1.integerIdType)(sequelize),
18
+ autoIncrement: true,
19
+ allowNull: false,
20
+ primaryKey: true
21
+ },
22
+ user_id: {
23
+ type: sequelize_1.DataTypes.STRING(191),
24
+ allowNull: false
25
+ },
26
+ real_user_id: {
27
+ type: sequelize_1.DataTypes.STRING(191),
28
+ allowNull: true,
29
+ defaultValue: null
30
+ },
31
+ expires: {
32
+ type: sequelize_1.DataTypes.DATE,
33
+ allowNull: false
34
+ },
35
+ issued_at: {
36
+ type: sequelize_1.DataTypes.DATE,
37
+ allowNull: false,
38
+ defaultValue: sequelize_1.DataTypes.NOW
39
+ },
40
+ last_seen_at: {
41
+ type: sequelize_1.DataTypes.DATE,
42
+ allowNull: false,
43
+ defaultValue: sequelize_1.DataTypes.NOW
44
+ },
45
+ access: {
46
+ type: sequelize_1.DataTypes.STRING(768),
47
+ allowNull: false
48
+ },
49
+ refresh: {
50
+ type: sequelize_1.DataTypes.STRING(768),
51
+ allowNull: false
52
+ },
53
+ domain: {
54
+ type: sequelize_1.DataTypes.STRING(64),
55
+ allowNull: false,
56
+ defaultValue: ''
57
+ },
58
+ fingerprint: {
59
+ type: sequelize_1.DataTypes.STRING(64),
60
+ allowNull: false,
61
+ defaultValue: ''
62
+ },
63
+ label: {
64
+ type: sequelize_1.DataTypes.STRING(128),
65
+ allowNull: false,
66
+ defaultValue: ''
67
+ },
68
+ browser: {
69
+ type: sequelize_1.DataTypes.STRING(64),
70
+ allowNull: false,
71
+ defaultValue: ''
72
+ },
73
+ device: {
74
+ type: sequelize_1.DataTypes.STRING(64),
75
+ allowNull: false,
76
+ defaultValue: ''
77
+ },
78
+ ip: {
79
+ type: sequelize_1.DataTypes.STRING(45),
80
+ allowNull: false,
81
+ defaultValue: ''
82
+ },
83
+ os: {
84
+ type: sequelize_1.DataTypes.STRING(64),
85
+ allowNull: false,
86
+ defaultValue: ''
87
+ },
88
+ login_type: {
89
+ type: sequelize_1.DataTypes.STRING(64),
90
+ allowNull: false,
91
+ defaultValue: ''
92
+ },
93
+ refresh_ttl_seconds: {
94
+ type: sequelize_1.DataTypes.INTEGER,
95
+ allowNull: true,
96
+ defaultValue: null
97
+ },
98
+ session_cookie: {
99
+ type: sequelize_1.DataTypes.BOOLEAN,
100
+ allowNull: false,
101
+ defaultValue: false
102
+ },
103
+ client_id: {
104
+ type: sequelize_1.DataTypes.STRING(128),
105
+ allowNull: true,
106
+ defaultValue: null
107
+ },
108
+ scope: {
109
+ type: sequelize_1.DataTypes.TEXT,
110
+ allowNull: false,
111
+ defaultValue: '[]'
112
+ }
113
+ }, {
114
+ ...(0, sequelize_utils_js_1.tableOptions)(sequelize, 'jwttokens', options.tablePrefix, { timestamps: false }),
115
+ indexes: [
116
+ { name: accessIndexName, unique: true, fields: ['access'] },
117
+ { name: refreshIndexName, unique: true, fields: ['refresh'] }
118
+ ]
119
+ });
120
+ return TokenModel;
121
+ }
122
+ class SequelizeTokenStore extends base_js_1.TokenStore {
123
+ constructor(options) {
124
+ super();
125
+ if (!options?.sequelize) {
126
+ throw new Error('SequelizeTokenStore requires an initialised Sequelize instance');
127
+ }
128
+ this.Tokens =
129
+ options.tokenModel ??
130
+ (options.tokenModelFactory ?? initTokenModel)(options.sequelize, {
131
+ tablePrefix: options.tablePrefix
132
+ });
133
+ }
134
+ async save(record) {
135
+ const normalized = this.normalizeToken(record);
136
+ const resolvedUserId = this.normalizeUserId(normalized.userId);
137
+ const resolvedRealUserId = this.resolveRealUserId(normalized.ruid);
138
+ const domain = normalized.domain;
139
+ const fingerprint = normalized.fingerprint;
140
+ const browser = normalized.browser;
141
+ const device = normalized.device;
142
+ const ip = normalized.ip;
143
+ const os = normalized.os;
144
+ const label = normalized.label;
145
+ const loginType = normalized.loginType ?? '';
146
+ const refreshTtlSeconds = typeof normalized.refreshTtlSeconds === 'number' && normalized.refreshTtlSeconds > 0
147
+ ? Math.floor(normalized.refreshTtlSeconds)
148
+ : null;
149
+ const issuedAt = normalized.issuedAt ?? new Date();
150
+ const lastSeenAt = normalized.lastSeenAt ?? issuedAt;
151
+ const sessionCookie = normalized.sessionCookie ?? false;
152
+ const removalWhere = { user_id: resolvedUserId };
153
+ if (record.domain !== undefined) {
154
+ removalWhere.domain = domain;
155
+ }
156
+ if (record.fingerprint !== undefined) {
157
+ removalWhere.fingerprint = fingerprint;
158
+ }
159
+ if (normalized.clientId) {
160
+ removalWhere.client_id = normalized.clientId;
161
+ }
162
+ const sequelize = this.Tokens.sequelize;
163
+ if (!sequelize) {
164
+ throw new Error('Token model is not bound to a Sequelize instance');
165
+ }
166
+ await sequelize.transaction(async (transaction) => {
167
+ await this.Tokens.destroy({ where: removalWhere, transaction });
168
+ // Access/refresh columns are unique. Remove stale collisions before insert to avoid
169
+ // transient uniqueness failures during retries/rotation edge-cases.
170
+ // Only include non-empty token values to prevent matching unrelated rows.
171
+ const collisionConditions = [];
172
+ if (normalized.accessToken) {
173
+ collisionConditions.push({ access: normalized.accessToken });
174
+ }
175
+ if (normalized.refreshToken) {
176
+ collisionConditions.push({ refresh: normalized.refreshToken });
177
+ }
178
+ if (collisionConditions.length > 0) {
179
+ await this.Tokens.destroy({
180
+ where: { [sequelize_1.Op.or]: collisionConditions },
181
+ transaction
182
+ });
183
+ }
184
+ await this.Tokens.create({
185
+ user_id: resolvedUserId,
186
+ real_user_id: resolvedRealUserId,
187
+ access: normalized.accessToken ?? '',
188
+ refresh: normalized.refreshToken,
189
+ expires: normalized.expires,
190
+ issued_at: issuedAt,
191
+ last_seen_at: lastSeenAt,
192
+ domain,
193
+ fingerprint,
194
+ label,
195
+ browser,
196
+ device,
197
+ ip,
198
+ os,
199
+ client_id: normalized.clientId ?? null,
200
+ scope: this.encodeScope(normalized.scope),
201
+ login_type: loginType,
202
+ refresh_ttl_seconds: refreshTtlSeconds,
203
+ session_cookie: sessionCookie
204
+ }, { transaction });
205
+ });
206
+ }
207
+ async get(query, opts) {
208
+ if (!query.refreshToken && !query.accessToken && query.userId === undefined) {
209
+ throw new Error('At least one token lookup field must be provided');
210
+ }
211
+ const where = {};
212
+ if (query.refreshToken) {
213
+ where.refresh = query.refreshToken;
214
+ }
215
+ if (query.accessToken) {
216
+ where.access = query.accessToken;
217
+ }
218
+ if (query.userId !== undefined) {
219
+ where.user_id = this.normalizeUserId(query.userId);
220
+ }
221
+ if (query.clientId) {
222
+ where.client_id = query.clientId;
223
+ }
224
+ if (query.domain !== undefined && query.domain !== null) {
225
+ where.domain = query.domain;
226
+ }
227
+ if (query.fingerprint !== undefined && query.fingerprint !== null) {
228
+ where.fingerprint = query.fingerprint;
229
+ }
230
+ if (query.label) {
231
+ where.label = query.label;
232
+ }
233
+ if (!(opts?.includeExpired ?? false)) {
234
+ where.expires = { [sequelize_1.Op.gt]: new Date() };
235
+ }
236
+ const model = await this.Tokens.findOne({ where });
237
+ return model ? this.toTokenRecord(model) : null;
238
+ }
239
+ async delete(query) {
240
+ if (!query.refreshToken && !query.accessToken && query.userId === undefined && !query.clientId) {
241
+ return 0;
242
+ }
243
+ const where = {};
244
+ if (query.refreshToken) {
245
+ where.refresh = query.refreshToken;
246
+ }
247
+ if (query.accessToken) {
248
+ where.access = query.accessToken;
249
+ }
250
+ if (query.userId !== undefined) {
251
+ where.user_id = this.normalizeUserId(query.userId);
252
+ }
253
+ if (query.clientId) {
254
+ where.client_id = query.clientId;
255
+ }
256
+ if (query.domain !== undefined && query.domain !== null) {
257
+ where.domain = query.domain;
258
+ }
259
+ if (query.fingerprint !== undefined && query.fingerprint !== null) {
260
+ where.fingerprint = query.fingerprint;
261
+ }
262
+ if (query.label) {
263
+ where.label = query.label;
264
+ }
265
+ return this.Tokens.destroy({ where });
266
+ }
267
+ async update(params) {
268
+ const where = { refresh: params.refreshToken };
269
+ if (params.clientId) {
270
+ where.client_id = params.clientId;
271
+ }
272
+ const updates = {};
273
+ if (params.accessToken !== undefined && params.accessToken !== null) {
274
+ updates.access = params.accessToken;
275
+ }
276
+ if (params.expires !== undefined && params.expires !== null) {
277
+ updates.expires = params.expires;
278
+ }
279
+ if (params.scope !== undefined) {
280
+ updates.scope = this.encodeScope(params.scope);
281
+ }
282
+ if (params.label !== undefined) {
283
+ updates.label = params.label ?? '';
284
+ }
285
+ if (params.domain !== undefined) {
286
+ updates.domain = params.domain ?? '';
287
+ }
288
+ if (params.fingerprint !== undefined) {
289
+ updates.fingerprint = params.fingerprint ?? '';
290
+ }
291
+ if (params.browser !== undefined) {
292
+ updates.browser = params.browser ?? '';
293
+ }
294
+ if (params.device !== undefined) {
295
+ updates.device = params.device ?? '';
296
+ }
297
+ if (params.ip !== undefined) {
298
+ updates.ip = params.ip ?? '';
299
+ }
300
+ if (params.os !== undefined) {
301
+ updates.os = params.os ?? '';
302
+ }
303
+ if (params.refreshTtlSeconds !== undefined) {
304
+ updates.refresh_ttl_seconds =
305
+ typeof params.refreshTtlSeconds === 'number' && params.refreshTtlSeconds > 0
306
+ ? Math.floor(params.refreshTtlSeconds)
307
+ : null;
308
+ }
309
+ if (params.loginType !== undefined) {
310
+ updates.login_type = params.loginType ?? '';
311
+ }
312
+ if (params.sessionCookie !== undefined) {
313
+ updates.session_cookie = params.sessionCookie;
314
+ }
315
+ if (params.issuedAt !== undefined && params.issuedAt !== null) {
316
+ updates.issued_at = params.issuedAt;
317
+ }
318
+ if (params.lastSeenAt !== undefined && params.lastSeenAt !== null) {
319
+ updates.last_seen_at = params.lastSeenAt;
320
+ }
321
+ if (Object.keys(updates).length === 0) {
322
+ return false;
323
+ }
324
+ const sequelize = this.Tokens.sequelize;
325
+ if (!sequelize) {
326
+ throw new Error('Token model is not bound to a Sequelize instance');
327
+ }
328
+ return sequelize.transaction(async (transaction) => {
329
+ const [updated] = await this.Tokens.update(updates, { where, transaction });
330
+ return updated > 0;
331
+ });
332
+ }
333
+ async list(userId, opts = {}) {
334
+ const where = { user_id: this.normalizeUserId(userId) };
335
+ if (!(opts.includeExpired ?? false)) {
336
+ where.expires = { [sequelize_1.Op.gt]: new Date() };
337
+ }
338
+ const models = await this.Tokens.findAll({
339
+ where,
340
+ order: [['issued_at', 'DESC']],
341
+ limit: opts.limit,
342
+ offset: opts.offset
343
+ });
344
+ return models.map((model) => this.toTokenRecord(model));
345
+ }
346
+ async close() {
347
+ return;
348
+ }
349
+ normalizeUserId(identifier) {
350
+ return (0, user_id_js_1.normalizeStringUserId)(identifier);
351
+ }
352
+ resolveRealUserId(ruid) {
353
+ if (ruid === undefined || ruid === null) {
354
+ return null;
355
+ }
356
+ const value = String(ruid);
357
+ if (value.length === 0 || value === '0') {
358
+ return null;
359
+ }
360
+ return value;
361
+ }
362
+ encodeScope(scope) {
363
+ if (!scope || (Array.isArray(scope) && scope.length === 0)) {
364
+ return '[]';
365
+ }
366
+ if (Array.isArray(scope)) {
367
+ return (0, sequelize_utils_js_1.encodeStringArray)(scope);
368
+ }
369
+ return (0, sequelize_utils_js_1.encodeStringArray)(scope.split(/\s+/).filter((entry) => entry.length > 0));
370
+ }
371
+ toTokenRecord(model) {
372
+ const scope = (0, sequelize_utils_js_1.decodeStringArray)(model.scope);
373
+ const normalized = this.normalizeToken({
374
+ userId: model.user_id,
375
+ refreshToken: model.refresh,
376
+ accessToken: model.access,
377
+ expires: model.expires,
378
+ issuedAt: model.issued_at,
379
+ lastSeenAt: model.last_seen_at,
380
+ domain: model.domain,
381
+ fingerprint: model.fingerprint,
382
+ label: model.label,
383
+ browser: model.browser,
384
+ device: model.device,
385
+ ip: model.ip,
386
+ os: model.os,
387
+ clientId: model.client_id ?? undefined,
388
+ scope,
389
+ loginType: model.login_type || undefined,
390
+ refreshTtlSeconds: model.refresh_ttl_seconds ?? undefined,
391
+ ruid: model.real_user_id ?? undefined,
392
+ sessionCookie: model.session_cookie
393
+ });
394
+ return {
395
+ ...normalized,
396
+ scope: normalized.scope ? [...normalized.scope] : undefined
397
+ };
398
+ }
399
+ }
400
+ exports.SequelizeTokenStore = SequelizeTokenStore;
@@ -0,0 +1,58 @@
1
+ import { CreationOptional, Model, type InferAttributes, type InferCreationAttributes, type ModelStatic, type Sequelize } from 'sequelize';
2
+ import { TokenStore } from './base.js';
3
+ import type { Token } from './types.js';
4
+ declare class TokenModel extends Model<InferAttributes<TokenModel>, InferCreationAttributes<TokenModel>> implements InferAttributes<TokenModel> {
5
+ token_id: CreationOptional<number>;
6
+ user_id: string;
7
+ real_user_id: CreationOptional<string | null>;
8
+ expires: Date;
9
+ issued_at: CreationOptional<Date>;
10
+ last_seen_at: CreationOptional<Date>;
11
+ access: string;
12
+ refresh: string;
13
+ domain: CreationOptional<string>;
14
+ fingerprint: CreationOptional<string>;
15
+ label: CreationOptional<string>;
16
+ browser: CreationOptional<string>;
17
+ device: CreationOptional<string>;
18
+ ip: CreationOptional<string>;
19
+ os: CreationOptional<string>;
20
+ client_id: CreationOptional<string | null>;
21
+ scope: CreationOptional<string>;
22
+ login_type: CreationOptional<string>;
23
+ refresh_ttl_seconds: CreationOptional<number | null>;
24
+ session_cookie: CreationOptional<boolean>;
25
+ }
26
+ export type TokenAttributes = InferAttributes<TokenModel>;
27
+ export type TokenCreationAttributes = InferCreationAttributes<TokenModel>;
28
+ export interface SequelizeTokenStoreOptions {
29
+ sequelize: Sequelize;
30
+ tablePrefix?: string;
31
+ tokenModel?: ModelStatic<TokenModel>;
32
+ tokenModelFactory?: (sequelize: Sequelize, options?: {
33
+ tablePrefix?: string;
34
+ }) => ModelStatic<TokenModel>;
35
+ }
36
+ export declare class SequelizeTokenStore extends TokenStore {
37
+ readonly Tokens: ModelStatic<TokenModel>;
38
+ constructor(options: SequelizeTokenStoreOptions);
39
+ save(record: Token): Promise<void>;
40
+ get(query: Partial<Token>, opts?: {
41
+ includeExpired?: boolean;
42
+ }): Promise<Token | null>;
43
+ delete(query: Partial<Token>): Promise<number>;
44
+ update(params: Partial<Token> & {
45
+ refreshToken: string;
46
+ }): Promise<boolean>;
47
+ list(userId: string | number, opts?: {
48
+ limit?: number;
49
+ offset?: number;
50
+ includeExpired?: boolean;
51
+ }): Promise<Token[]>;
52
+ close(): Promise<void>;
53
+ private normalizeUserId;
54
+ private resolveRealUserId;
55
+ private encodeScope;
56
+ private toTokenRecord;
57
+ }
58
+ export {};
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,34 @@
1
+ export type TokenStatus = 'active' | 'expired' | 'revoked';
2
+ export interface Token {
3
+ accessToken: string;
4
+ refreshToken: string;
5
+ userId: string;
6
+ expires?: Date;
7
+ issuedAt?: Date;
8
+ lastSeenAt?: Date;
9
+ status?: TokenStatus;
10
+ ruid?: string;
11
+ clientId?: string;
12
+ /**
13
+ * Optional session partition key. Token stores may use `domain` and `fingerprint`
14
+ * to replace previous sessions that match the same bucket.
15
+ */
16
+ domain?: string;
17
+ /**
18
+ * Optional device/session fingerprint used together with `domain` for session bucketing.
19
+ */
20
+ fingerprint?: string;
21
+ label?: string;
22
+ browser?: string;
23
+ device?: string;
24
+ ip?: string;
25
+ os?: string;
26
+ scope?: string[];
27
+ loginType?: string;
28
+ refreshTtlSeconds?: number;
29
+ sessionCookie?: boolean;
30
+ }
31
+ export interface TokenPair {
32
+ accessToken: string;
33
+ refreshToken: string;
34
+ }
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryTusUploadStore = exports.TusUploadExceedsLengthError = exports.TusUploadOffsetError = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ function cloneRecord(record) {
6
+ return {
7
+ ...record,
8
+ metadata: { ...record.metadata },
9
+ createdAt: new Date(record.createdAt),
10
+ updatedAt: new Date(record.updatedAt),
11
+ ...(record.completedAt ? { completedAt: new Date(record.completedAt) } : {})
12
+ };
13
+ }
14
+ class TusUploadOffsetError extends Error {
15
+ constructor(currentOffset) {
16
+ super('Upload offset does not match current offset');
17
+ this.currentOffset = currentOffset;
18
+ }
19
+ }
20
+ exports.TusUploadOffsetError = TusUploadOffsetError;
21
+ class TusUploadExceedsLengthError extends Error {
22
+ constructor() {
23
+ super('Upload exceeds declared length');
24
+ }
25
+ }
26
+ exports.TusUploadExceedsLengthError = TusUploadExceedsLengthError;
27
+ class MemoryTusUploadStore {
28
+ constructor() {
29
+ this.uploads = new Map();
30
+ this.chunks = new Map();
31
+ }
32
+ async createUpload(input) {
33
+ const id = input.id?.trim() || (0, node_crypto_1.randomUUID)();
34
+ if (this.uploads.has(id)) {
35
+ throw new Error(`Upload ${id} already exists`);
36
+ }
37
+ const now = new Date();
38
+ const record = {
39
+ id,
40
+ length: Math.max(0, Math.floor(input.length)),
41
+ offset: 0,
42
+ metadata: { ...input.metadata },
43
+ ...(input.userId ? { userId: input.userId } : {}),
44
+ createdAt: now,
45
+ updatedAt: now
46
+ };
47
+ this.uploads.set(id, record);
48
+ this.chunks.set(id, []);
49
+ return cloneRecord(record);
50
+ }
51
+ async getUpload(uploadId) {
52
+ const found = this.uploads.get(uploadId);
53
+ return found ? cloneRecord(found) : null;
54
+ }
55
+ async appendUpload(input) {
56
+ const current = this.uploads.get(input.uploadId);
57
+ if (!current) {
58
+ throw new Error('Upload not found');
59
+ }
60
+ if (input.offset !== current.offset) {
61
+ throw new TusUploadOffsetError(current.offset);
62
+ }
63
+ const nextOffset = current.offset + input.chunk.length;
64
+ if (nextOffset > current.length) {
65
+ throw new TusUploadExceedsLengthError();
66
+ }
67
+ const now = new Date();
68
+ const updated = {
69
+ ...current,
70
+ offset: nextOffset,
71
+ updatedAt: now,
72
+ ...(nextOffset === current.length ? { completedAt: now } : {})
73
+ };
74
+ this.uploads.set(input.uploadId, updated);
75
+ if (input.chunk.length > 0) {
76
+ this.chunks.get(input.uploadId)?.push(Buffer.from(input.chunk));
77
+ }
78
+ return cloneRecord(updated);
79
+ }
80
+ async deleteUpload(uploadId) {
81
+ this.chunks.delete(uploadId);
82
+ return this.uploads.delete(uploadId);
83
+ }
84
+ readUpload(uploadId) {
85
+ const chunks = this.chunks.get(uploadId);
86
+ if (!chunks) {
87
+ return null;
88
+ }
89
+ return Buffer.concat(chunks);
90
+ }
91
+ }
92
+ exports.MemoryTusUploadStore = MemoryTusUploadStore;
@@ -0,0 +1,17 @@
1
+ import type { TusAppendInput, TusCreateUploadInput, TusUploadRecord, TusUploadStore } from './types.js';
2
+ export declare class TusUploadOffsetError extends Error {
3
+ readonly currentOffset: number;
4
+ constructor(currentOffset: number);
5
+ }
6
+ export declare class TusUploadExceedsLengthError extends Error {
7
+ constructor();
8
+ }
9
+ export declare class MemoryTusUploadStore implements TusUploadStore {
10
+ private readonly uploads;
11
+ private readonly chunks;
12
+ createUpload(input: TusCreateUploadInput): Promise<TusUploadRecord>;
13
+ getUpload(uploadId: string): Promise<TusUploadRecord | null>;
14
+ appendUpload(input: TusAppendInput): Promise<TusUploadRecord>;
15
+ deleteUpload(uploadId: string): Promise<boolean>;
16
+ readUpload(uploadId: string): Buffer | null;
17
+ }