@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,29 @@
1
+ import { TokenStore } from './base.js';
2
+ import type { Token } from './types.js';
3
+ export interface MemoryTokenStoreOptions {
4
+ maxTokens?: number;
5
+ }
6
+ export declare class MemoryTokenStore extends TokenStore {
7
+ private readonly tokens;
8
+ private readonly tokensByUser;
9
+ private readonly maxTokens?;
10
+ constructor(options?: MemoryTokenStoreOptions);
11
+ private indexToken;
12
+ private unindexToken;
13
+ private removeByRefreshToken;
14
+ save(record: Token): Promise<void>;
15
+ get(query: Partial<Token>, opts?: {
16
+ includeExpired?: boolean;
17
+ }): Promise<Token | null>;
18
+ delete(query: Partial<Token>): Promise<number>;
19
+ update(params: Partial<Token> & {
20
+ refreshToken: string;
21
+ }): Promise<boolean>;
22
+ list(userId: string | number, opts?: {
23
+ limit?: number;
24
+ offset?: number;
25
+ includeExpired?: boolean;
26
+ }): Promise<Token[]>;
27
+ close(): Promise<void>;
28
+ private enforceCapacity;
29
+ }
@@ -0,0 +1,230 @@
1
+ import { TokenStore } from './base.js';
2
+ function comparableUserId(value) {
3
+ if (value === undefined) {
4
+ return undefined;
5
+ }
6
+ return String(value);
7
+ }
8
+ function cloneToken(record) {
9
+ return {
10
+ ...record,
11
+ scope: record.scope ? [...record.scope] : undefined,
12
+ expires: record.expires ? new Date(record.expires) : undefined,
13
+ issuedAt: record.issuedAt ? new Date(record.issuedAt) : undefined,
14
+ lastSeenAt: record.lastSeenAt ? new Date(record.lastSeenAt) : undefined
15
+ };
16
+ }
17
+ function matchesQuery(record, query, includeExpired) {
18
+ if (query.refreshToken !== undefined && record.refreshToken !== query.refreshToken) {
19
+ return false;
20
+ }
21
+ if (query.accessToken !== undefined && record.accessToken !== query.accessToken) {
22
+ return false;
23
+ }
24
+ if (query.userId !== undefined && comparableUserId(record.userId) !== comparableUserId(query.userId)) {
25
+ return false;
26
+ }
27
+ if (query.clientId !== undefined && record.clientId !== query.clientId) {
28
+ return false;
29
+ }
30
+ if (query.domain !== undefined && (record.domain ?? '') !== (query.domain ?? '')) {
31
+ return false;
32
+ }
33
+ if (query.fingerprint !== undefined && (record.fingerprint ?? '') !== (query.fingerprint ?? '')) {
34
+ return false;
35
+ }
36
+ if (query.loginType !== undefined && record.loginType !== (query.loginType ?? undefined)) {
37
+ return false;
38
+ }
39
+ if (query.label !== undefined && record.label !== query.label) {
40
+ return false;
41
+ }
42
+ if (!includeExpired) {
43
+ const expiresTime = record.expires ? record.expires.getTime() : Infinity;
44
+ if (expiresTime < Date.now()) {
45
+ return false;
46
+ }
47
+ }
48
+ return true;
49
+ }
50
+ export class MemoryTokenStore extends TokenStore {
51
+ constructor(options = {}) {
52
+ super();
53
+ this.tokens = new Map();
54
+ this.tokensByUser = new Map();
55
+ this.maxTokens =
56
+ typeof options.maxTokens === 'number' && Number.isFinite(options.maxTokens) && options.maxTokens > 0
57
+ ? Math.floor(options.maxTokens)
58
+ : undefined;
59
+ }
60
+ indexToken(token) {
61
+ const userId = comparableUserId(token.userId);
62
+ if (!userId) {
63
+ return;
64
+ }
65
+ let userTokens = this.tokensByUser.get(userId);
66
+ if (!userTokens) {
67
+ userTokens = new Set();
68
+ this.tokensByUser.set(userId, userTokens);
69
+ }
70
+ userTokens.add(token.refreshToken);
71
+ }
72
+ unindexToken(token) {
73
+ const userId = comparableUserId(token.userId);
74
+ if (!userId) {
75
+ return;
76
+ }
77
+ const userTokens = this.tokensByUser.get(userId);
78
+ if (!userTokens) {
79
+ return;
80
+ }
81
+ userTokens.delete(token.refreshToken);
82
+ if (userTokens.size === 0) {
83
+ this.tokensByUser.delete(userId);
84
+ }
85
+ }
86
+ removeByRefreshToken(refreshToken) {
87
+ const existing = this.tokens.get(refreshToken);
88
+ if (!existing) {
89
+ return;
90
+ }
91
+ this.unindexToken(existing);
92
+ this.tokens.delete(refreshToken);
93
+ }
94
+ async save(record) {
95
+ const stored = this.normalizeToken(record);
96
+ const normalizedUserId = comparableUserId(stored.userId);
97
+ if (!normalizedUserId) {
98
+ throw new Error('userId is required');
99
+ }
100
+ const domainProvided = record.domain !== undefined;
101
+ const fingerprintProvided = record.fingerprint !== undefined;
102
+ const userRefreshTokens = [...(this.tokensByUser.get(normalizedUserId) ?? [])];
103
+ for (const refreshToken of userRefreshTokens) {
104
+ const existing = this.tokens.get(refreshToken);
105
+ if (!existing) {
106
+ continue;
107
+ }
108
+ if (comparableUserId(existing.userId) !== normalizedUserId) {
109
+ continue;
110
+ }
111
+ if (record.clientId && existing.clientId !== record.clientId) {
112
+ continue;
113
+ }
114
+ if (domainProvided && existing.domain !== stored.domain) {
115
+ continue;
116
+ }
117
+ if (fingerprintProvided && existing.fingerprint !== stored.fingerprint) {
118
+ continue;
119
+ }
120
+ this.removeByRefreshToken(existing.refreshToken);
121
+ }
122
+ this.removeByRefreshToken(stored.refreshToken);
123
+ this.tokens.set(stored.refreshToken, stored);
124
+ this.indexToken(stored);
125
+ this.enforceCapacity();
126
+ }
127
+ async get(query, opts) {
128
+ if (!query.refreshToken && !query.accessToken && query.userId === undefined) {
129
+ throw new Error('At least one token lookup field must be provided');
130
+ }
131
+ const includeExpired = opts?.includeExpired ?? false;
132
+ if (query.refreshToken) {
133
+ const record = this.tokens.get(query.refreshToken);
134
+ return record && matchesQuery(record, query, includeExpired) ? cloneToken(record) : null;
135
+ }
136
+ for (const token of this.tokens.values()) {
137
+ if (matchesQuery(token, query, includeExpired)) {
138
+ return cloneToken(token);
139
+ }
140
+ }
141
+ return null;
142
+ }
143
+ async delete(query) {
144
+ if (!query.refreshToken && !query.accessToken && query.userId === undefined && !query.clientId) {
145
+ return 0;
146
+ }
147
+ let removed = 0;
148
+ const refreshTokens = [...this.tokens.keys()];
149
+ for (const refreshToken of refreshTokens) {
150
+ const token = this.tokens.get(refreshToken);
151
+ if (token && matchesQuery(token, query, true)) {
152
+ this.removeByRefreshToken(refreshToken);
153
+ removed += 1;
154
+ }
155
+ }
156
+ return removed;
157
+ }
158
+ async update(params) {
159
+ const token = this.tokens.get(params.refreshToken);
160
+ if (!token) {
161
+ return false;
162
+ }
163
+ if (params.clientId && token.clientId !== params.clientId) {
164
+ return false;
165
+ }
166
+ const merged = { ...token };
167
+ const maybeAssign = (key) => {
168
+ const value = params[key];
169
+ if (value !== undefined && value !== null) {
170
+ merged[key] = value;
171
+ }
172
+ };
173
+ maybeAssign('accessToken');
174
+ maybeAssign('expires');
175
+ maybeAssign('issuedAt');
176
+ maybeAssign('lastSeenAt');
177
+ maybeAssign('scope');
178
+ maybeAssign('label');
179
+ maybeAssign('domain');
180
+ maybeAssign('fingerprint');
181
+ maybeAssign('browser');
182
+ maybeAssign('device');
183
+ maybeAssign('ip');
184
+ maybeAssign('os');
185
+ maybeAssign('refreshTtlSeconds');
186
+ maybeAssign('loginType');
187
+ maybeAssign('sessionCookie');
188
+ const normalized = this.normalizeToken(merged);
189
+ const previousUserId = token.userId;
190
+ const previousRefreshToken = token.refreshToken;
191
+ const userChanged = comparableUserId(previousUserId) !== comparableUserId(normalized.userId);
192
+ Object.assign(token, normalized);
193
+ if (userChanged || previousRefreshToken !== token.refreshToken) {
194
+ this.unindexToken({ ...token, userId: previousUserId, refreshToken: previousRefreshToken });
195
+ this.indexToken(token);
196
+ if (previousRefreshToken !== token.refreshToken) {
197
+ this.tokens.delete(previousRefreshToken);
198
+ this.tokens.set(token.refreshToken, token);
199
+ }
200
+ }
201
+ return true;
202
+ }
203
+ async list(userId, opts = {}) {
204
+ const includeExpired = opts.includeExpired ?? false;
205
+ const normalizedUserId = comparableUserId(userId);
206
+ const userRefreshTokens = normalizedUserId ? [...(this.tokensByUser.get(normalizedUserId) ?? [])] : [];
207
+ const filtered = userRefreshTokens
208
+ .map((refreshToken) => this.tokens.get(refreshToken))
209
+ .filter((token) => Boolean(token))
210
+ .filter((token) => matchesQuery(token, { userId: normalizedUserId }, includeExpired));
211
+ const offset = opts.offset ?? 0;
212
+ const limit = opts.limit ?? filtered.length;
213
+ return filtered.slice(offset, offset + limit).map(cloneToken);
214
+ }
215
+ async close() {
216
+ return;
217
+ }
218
+ enforceCapacity() {
219
+ if (!this.maxTokens) {
220
+ return;
221
+ }
222
+ while (this.tokens.size > this.maxTokens) {
223
+ const oldestRefresh = this.tokens.keys().next().value;
224
+ if (!oldestRefresh) {
225
+ return;
226
+ }
227
+ this.removeByRefreshToken(oldestRefresh);
228
+ }
229
+ }
230
+ }
@@ -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 {};