@proteinjs/user-server 1.5.3 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/LICENSE +21 -0
- package/dist/generated/index.d.ts +1 -4
- package/dist/generated/index.d.ts.map +1 -1
- package/dist/generated/index.js +6 -7
- package/dist/generated/index.js.map +1 -1
- package/dist/src/authentication/DbSessionStore.d.ts +0 -1
- package/dist/src/authentication/DbSessionStore.d.ts.map +1 -1
- package/dist/src/routes/avatar.d.ts +19 -0
- package/dist/src/routes/avatar.d.ts.map +1 -0
- package/dist/src/routes/avatar.js +108 -0
- package/dist/src/routes/avatar.js.map +1 -0
- package/dist/src/routes/devLogin.d.ts.map +1 -1
- package/dist/src/routes/devLogin.js +11 -0
- package/dist/src/routes/devLogin.js.map +1 -1
- package/dist/src/services/UpdateUserInfo.d.ts +40 -2
- package/dist/src/services/UpdateUserInfo.d.ts.map +1 -1
- package/dist/src/services/UpdateUserInfo.js +183 -12
- package/dist/src/services/UpdateUserInfo.js.map +1 -1
- package/dist/test/UpdateUserInfoAvatar.test.d.ts +2 -0
- package/dist/test/UpdateUserInfoAvatar.test.d.ts.map +1 -0
- package/dist/test/UpdateUserInfoAvatar.test.js +337 -0
- package/dist/test/UpdateUserInfoAvatar.test.js.map +1 -0
- package/dist/test/UpdateUserInfoSessionRefresh.test.d.ts +2 -0
- package/dist/test/UpdateUserInfoSessionRefresh.test.d.ts.map +1 -0
- package/dist/test/UpdateUserInfoSessionRefresh.test.js +94 -0
- package/dist/test/UpdateUserInfoSessionRefresh.test.js.map +1 -0
- package/dist/test/UserServerTestEnvironment.d.ts +26 -0
- package/dist/test/UserServerTestEnvironment.d.ts.map +1 -0
- package/dist/test/UserServerTestEnvironment.js +202 -0
- package/dist/test/UserServerTestEnvironment.js.map +1 -0
- package/dist/test/avatarRoute.test.d.ts +2 -0
- package/dist/test/avatarRoute.test.d.ts.map +1 -0
- package/dist/test/avatarRoute.test.js +222 -0
- package/dist/test/avatarRoute.test.js.map +1 -0
- package/generated/index.ts +21 -25
- package/jest.config.js +4 -1
- package/package.json +9 -5
- package/src/heic-decode.d.ts +13 -0
- package/src/routes/avatar.ts +57 -0
- package/src/routes/devLogin.ts +5 -0
- package/src/services/UpdateUserInfo.ts +124 -13
- package/test/UpdateUserInfoAvatar.test.ts +174 -0
- package/test/UpdateUserInfoSessionRefresh.test.ts +37 -0
- package/test/UserServerTestEnvironment.ts +97 -0
- package/test/avatarRoute.test.ts +111 -0
- package/test/emulatorLock.globalSetup.js +11 -0
- package/test/emulatorLock.globalTeardown.js +8 -0
- package/test/emulatorLock.js +124 -0
- package/test/fixtures/fixture.heic +0 -0
- package/tsconfig.json +1 -0
package/generated/index.ts
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
/** Load Dependency Source Graphs */
|
|
2
2
|
|
|
3
3
|
import '@proteinjs/db';
|
|
4
|
+
import '@proteinjs/db-file';
|
|
4
5
|
import '@proteinjs/email-server';
|
|
5
6
|
import '@proteinjs/logger';
|
|
6
|
-
import '@proteinjs/reflection';
|
|
7
7
|
import '@proteinjs/server';
|
|
8
8
|
import '@proteinjs/server-api';
|
|
9
9
|
import '@proteinjs/user';
|
|
10
|
-
import 'crypto-js';
|
|
11
|
-
import 'express-session';
|
|
12
|
-
import 'moment';
|
|
13
|
-
|
|
14
10
|
|
|
15
11
|
/** Generate Source Graph */
|
|
16
12
|
|
|
17
|
-
const sourceGraph = "{\"options\":{\"directed\":true,\"multigraph\":false,\"compound\":false},\"nodes\":[{\"v\":\"@proteinjs/user-server/DbSessionStore\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"DbSessionStore\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/authentication/DbSessionStore.ts\",\"qualifiedName\":\"@proteinjs/user-server/DbSessionStore\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"TWELVE_HOURS\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":true,\"visibility\":\"private\"},{\"name\":\"TOUCH_INTERVAL\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":true,\"visibility\":\"private\"},{\"name\":\"logger\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"lastTouchMs\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\"},{\"name\":\"get\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"},{\"name\":\"set\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"},{\"name\":\"touch\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"},{\"name\":\"destroy\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[{\"name\":\"isTransactionAbort\",\"returnType\":{\"packageName\":\"\",\"name\":\"boolean\",\"filePath\":null,\"qualifiedName\":\"/boolean\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":true,\"visibility\":\"private\",\"parameters\":[{\"name\":\"error\",\"type\":{\"packageName\":\"\",\"name\":\"any\",\"filePath\":null,\"qualifiedName\":\"/any\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"insertOrUpdate\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<void>\",\"filePath\":null,\"qualifiedName\":\"/Promise<void>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\",\"parameters\":[{\"name\":\"sessionId\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"session\",\"type\":{\"packageName\":\"\",\"name\":\"Express.SessionData\",\"filePath\":null,\"qualifiedName\":\"/Express.SessionData\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"cb\",\"type\":{\"packageName\":\"\",\"name\":\"(error?: any) => void\",\"filePath\":null,\"qualifiedName\":\"/(error?: any) => void\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"sweep\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<void>\",\"filePath\":null,\"qualifiedName\":\"/Promise<void>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\",\"parameters\":[]}],\"typeParameters\":[],\"directParentInterfaces\":[],\"directParentClasses\":[{\"packageName\":\"express-session\",\"name\":\"Store\",\"filePath\":null,\"qualifiedName\":\"express-session/Store\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParentInterfaces\":[],\"directParentClasses\":[]}],\"sourceType\":2}},{\"v\":\"express-session/Store\"},{\"v\":\"@proteinjs/user-server/SocketIOSessionWatcher\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"SocketIOSessionWatcher\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/authentication/SocketIOSessionWatcher.ts\",\"qualifiedName\":\"@proteinjs/user-server/SocketIOSessionWatcher\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[],\"methods\":[{\"name\":\"name\",\"returnType\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[]},{\"name\":\"table\",\"returnType\":{\"packageName\":\"\",\"name\":\"Table<Session>\",\"filePath\":null,\"qualifiedName\":\"/Table<Session>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[]},{\"name\":\"afterDelete\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<void>\",\"filePath\":null,\"qualifiedName\":\"/Promise<void>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"recordDeleteCount\",\"type\":{\"packageName\":\"\",\"name\":\"number\",\"filePath\":null,\"qualifiedName\":\"/number\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"deletedRecords\",\"type\":{\"packageName\":\"\",\"name\":\"T[]\",\"filePath\":null,\"qualifiedName\":\"/T[]\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"initialQb\",\"type\":{\"packageName\":\"\",\"name\":\"QueryBuilder<T>\",\"filePath\":null,\"qualifiedName\":\"/QueryBuilder<T>\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"deleteQb\",\"type\":{\"packageName\":\"\",\"name\":\"QueryBuilder<T>\",\"filePath\":null,\"qualifiedName\":\"/QueryBuilder<T>\",\"typeParameters\":null,\"directParents\":null}}]}],\"typeParameters\":[],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/db\",\"name\":\"TableWatcher\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/db/TableWatcher\",\"properties\":[],\"methods\":[],\"typeParameters\":[\"@proteinjs/user/Session\"],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/db/TableWatcher\"},{\"v\":\"@proteinjs/user-server/userCache\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"userCache\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/authorization/userCache.ts\",\"qualifiedName\":\"@proteinjs/user-server/userCache\",\"type\":{\"packageName\":\"\",\"name\":\"SessionDataCache<User>\",\"filePath\":null,\"qualifiedName\":\"/SessionDataCache<User>\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"SessionDataCache\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/SessionDataCache\",\"typeParameters\":[\"@proteinjs/user/User\"],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/server-api/SessionDataCache\"},{\"v\":\"@proteinjs/user-server/devLogin\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"devLogin\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/routes/devLogin.ts\",\"qualifiedName\":\"@proteinjs/user-server/devLogin\",\"type\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/server-api/Route\"},{\"v\":\"@proteinjs/user-server/executePasswordReset\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"executePasswordReset\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/routes/executePasswordReset.ts\",\"qualifiedName\":\"@proteinjs/user-server/executePasswordReset\",\"type\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/user-server/initiatePasswordReset\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"initiatePasswordReset\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/routes/initiatePasswordReset.ts\",\"qualifiedName\":\"@proteinjs/user-server/initiatePasswordReset\",\"type\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/user-server/login\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"login\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/routes/login.ts\",\"qualifiedName\":\"@proteinjs/user-server/login\",\"type\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/user-server/logout\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"logout\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/routes/logout.ts\",\"qualifiedName\":\"@proteinjs/user-server/logout\",\"type\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/user-server/validateResetPasswordToken\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"validateResetPasswordToken\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/routes/validateResetPasswordToken.ts\",\"qualifiedName\":\"@proteinjs/user-server/validateResetPasswordToken\",\"type\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/user-server/AccessInvite\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"AccessInvite\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/services/AccessInvite.ts\",\"qualifiedName\":\"@proteinjs/user-server/AccessInvite\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"serviceMetadata\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[{\"name\":\"createAccessInvite\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<CreateAccessInviteResponse>\",\"filePath\":null,\"qualifiedName\":\"/Promise<CreateAccessInviteResponse>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"params\",\"type\":{\"packageName\":\"@proteinjs/user\",\"name\":\"CreateAccessInviteParams\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/user/CreateAccessInviteParams\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"acceptAccessInvite\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<T | undefined>\",\"filePath\":null,\"qualifiedName\":\"/Promise<T | undefined>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"token\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}}]}],\"typeParameters\":[\"/T extends Record\"],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/user\",\"name\":\"AccessInviteService\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/user/AccessInviteService\",\"properties\":[],\"methods\":[],\"typeParameters\":[\"/T\"],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/user/AccessInviteService\"},{\"v\":\"@proteinjs/user-server/Roles\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"Roles\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/services/Roles.ts\",\"qualifiedName\":\"@proteinjs/user-server/Roles\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"serviceMetadata\",\"type\":{\"packageName\":\"\",\"name\":\"Service['serviceMetadata']\",\"filePath\":null,\"qualifiedName\":\"/Service['serviceMetadata']\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[{\"name\":\"grantRole\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<void>\",\"filePath\":null,\"qualifiedName\":\"/Promise<void>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"userId\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"role\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"revokeRole\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<void>\",\"filePath\":null,\"qualifiedName\":\"/Promise<void>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"userId\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"role\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"changeRole\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<void>\",\"filePath\":null,\"qualifiedName\":\"/Promise<void>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\",\"parameters\":[{\"name\":\"userId\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"role\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"action\",\"type\":{\"packageName\":\"\",\"name\":\"'grant' | 'revoke'\",\"filePath\":null,\"qualifiedName\":\"/'grant' | 'revoke'\",\"typeParameters\":null,\"directParents\":null}}]}],\"typeParameters\":[],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/user\",\"name\":\"RolesService\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/user/RolesService\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/user/RolesService\"},{\"v\":\"@proteinjs/user-server/DefaultInviteConfigFactory\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"DefaultInviteConfigFactory\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/services/Signup.ts\",\"qualifiedName\":\"@proteinjs/user-server/DefaultInviteConfigFactory\",\"properties\":[],\"methods\":[{\"name\":\"getConfig\",\"returnType\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"InviteConfig\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/user-server/InviteConfig\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":true,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[]}],\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/reflection\",\"name\":\"Loadable\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/reflection/Loadable\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParents\":[]}],\"sourceType\":3}},{\"v\":\"@proteinjs/reflection/Loadable\"},{\"v\":\"@proteinjs/user-server/Signup\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"Signup\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/services/Signup.ts\",\"qualifiedName\":\"@proteinjs/user-server/Signup\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"serviceMetadata\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[{\"name\":\"createUser\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<void>\",\"filePath\":null,\"qualifiedName\":\"/Promise<void>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"user\",\"type\":{\"packageName\":\"@proteinjs/user\",\"name\":\"UserSignup\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/user/UserSignup\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"token\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"sendInvite\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<SendInviteResponse>\",\"filePath\":null,\"qualifiedName\":\"/Promise<SendInviteResponse>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"email\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"revokeInvite\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<void>\",\"filePath\":null,\"qualifiedName\":\"/Promise<void>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"email\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"initializeSignup\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<InitializeSignupResponse>\",\"filePath\":null,\"qualifiedName\":\"/Promise<InitializeSignupResponse>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"inviteToken\",\"type\":{\"packageName\":\"\",\"name\":\"string | undefined\",\"filePath\":null,\"qualifiedName\":\"/string | undefined\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"lookupInvite\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<InviteLookup>\",\"filePath\":null,\"qualifiedName\":\"/Promise<InviteLookup>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\",\"parameters\":[{\"name\":\"token\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}}]}],\"typeParameters\":[],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/user\",\"name\":\"SignupService\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/user/SignupService\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/user/SignupService\"},{\"v\":\"@proteinjs/user-server/UpdateUserInfo\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"UpdateUserInfo\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/services/UpdateUserInfo.ts\",\"qualifiedName\":\"@proteinjs/user-server/UpdateUserInfo\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"serviceMetadata\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[{\"name\":\"updateName\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<void>\",\"filePath\":null,\"qualifiedName\":\"/Promise<void>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"name\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"updatePassword\",\"returnType\":{\"packageName\":\"\",\"name\":\"Promise<UpdatePasswordResponse>\",\"filePath\":null,\"qualifiedName\":\"/Promise<UpdatePasswordResponse>\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":true,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"currentPassword\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}},{\"name\":\"newPassword\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null}}]}],\"typeParameters\":[],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/user\",\"name\":\"UpdateUserInfoService\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/user/UpdateUserInfoService\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/user/UpdateUserInfoService\"},{\"v\":\"@proteinjs/user-server/BackfillUserRolesArray\",\"value\":{\"packageName\":\"@proteinjs/user-server\",\"name\":\"BackfillUserRolesArray\",\"filePath\":\"/Users/brentbahry/repos/farm/n3xa2/packages/proteinjs/packages/user/packages/server/src/migrations/BackfillUserRolesArray.ts\",\"qualifiedName\":\"@proteinjs/user-server/BackfillUserRolesArray\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"table\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"},{\"name\":\"record\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[],\"typeParameters\":[],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/db\",\"name\":\"SourceRecordLoader\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/db/SourceRecordLoader\",\"properties\":[],\"methods\":[],\"typeParameters\":[\"@proteinjs/db/Migration\"],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/db/SourceRecordLoader\"}],\"edges\":[{\"v\":\"@proteinjs/user-server/DbSessionStore\",\"w\":\"express-session/Store\",\"value\":\"extends class\"},{\"v\":\"@proteinjs/user-server/SocketIOSessionWatcher\",\"w\":\"@proteinjs/db/TableWatcher\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/user-server/userCache\",\"w\":\"@proteinjs/server-api/SessionDataCache\",\"value\":\"has type\"},{\"v\":\"@proteinjs/user-server/devLogin\",\"w\":\"@proteinjs/server-api/Route\",\"value\":\"has type\"},{\"v\":\"@proteinjs/user-server/executePasswordReset\",\"w\":\"@proteinjs/server-api/Route\",\"value\":\"has type\"},{\"v\":\"@proteinjs/user-server/initiatePasswordReset\",\"w\":\"@proteinjs/server-api/Route\",\"value\":\"has type\"},{\"v\":\"@proteinjs/user-server/login\",\"w\":\"@proteinjs/server-api/Route\",\"value\":\"has type\"},{\"v\":\"@proteinjs/user-server/logout\",\"w\":\"@proteinjs/server-api/Route\",\"value\":\"has type\"},{\"v\":\"@proteinjs/user-server/validateResetPasswordToken\",\"w\":\"@proteinjs/server-api/Route\",\"value\":\"has type\"},{\"v\":\"@proteinjs/user-server/AccessInvite\",\"w\":\"@proteinjs/user/AccessInviteService\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/user-server/Roles\",\"w\":\"@proteinjs/user/RolesService\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/user-server/DefaultInviteConfigFactory\",\"w\":\"@proteinjs/reflection/Loadable\",\"value\":\"extends interface\"},{\"v\":\"@proteinjs/user-server/Signup\",\"w\":\"@proteinjs/user/SignupService\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/user-server/UpdateUserInfo\",\"w\":\"@proteinjs/user/UpdateUserInfoService\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/user-server/BackfillUserRolesArray\",\"w\":\"@proteinjs/db/SourceRecordLoader\",\"value\":\"implements interface\"}]}";
|
|
18
|
-
|
|
13
|
+
const sourceGraph =
|
|
14
|
+
'{"options":{"directed":true,"multigraph":false,"compound":false},"nodes":[{"v":"@proteinjs/user-server/DbSessionStore","value":{"packageName":"@proteinjs/user-server","name":"DbSessionStore","filePath":"/home/runner/work/user/user/packages/server/src/authentication/DbSessionStore.ts","qualifiedName":"@proteinjs/user-server/DbSessionStore","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"TWELVE_HOURS","type":null,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"TOUCH_INTERVAL","type":null,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"logger","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"lastTouchMs","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private"},{"name":"get","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"set","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"touch","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"destroy","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"isTransactionAbort","returnType":{"packageName":"","name":"boolean","filePath":null,"qualifiedName":"/boolean","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private","parameters":[{"name":"error","type":{"packageName":"","name":"any","filePath":null,"qualifiedName":"/any","typeParameters":null,"directParents":null}}]},{"name":"insertOrUpdate","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"sessionId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"session","type":{"packageName":"","name":"Express.SessionData","filePath":null,"qualifiedName":"/Express.SessionData","typeParameters":null,"directParents":null}},{"name":"cb","type":{"packageName":"","name":"(error?: any) => void","filePath":null,"qualifiedName":"/(error?: any) => void","typeParameters":null,"directParents":null}}]},{"name":"sweep","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[]}],"typeParameters":[],"directParentInterfaces":[],"directParentClasses":[{"packageName":"express-session","name":"Store","filePath":null,"qualifiedName":"express-session/Store","isAbstract":false,"isStatic":false,"visibility":"public","properties":[],"methods":[],"typeParameters":[],"directParentInterfaces":[],"directParentClasses":[]}],"sourceType":2}},{"v":"express-session/Store"},{"v":"@proteinjs/user-server/SocketIOSessionWatcher","value":{"packageName":"@proteinjs/user-server","name":"SocketIOSessionWatcher","filePath":"/home/runner/work/user/user/packages/server/src/authentication/SocketIOSessionWatcher.ts","qualifiedName":"@proteinjs/user-server/SocketIOSessionWatcher","isAbstract":false,"isStatic":false,"visibility":"public","properties":[],"methods":[{"name":"name","returnType":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"table","returnType":{"packageName":"","name":"Table<Session>","filePath":null,"qualifiedName":"/Table<Session>","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"afterDelete","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"recordDeleteCount","type":{"packageName":"","name":"number","filePath":null,"qualifiedName":"/number","typeParameters":null,"directParents":null}},{"name":"deletedRecords","type":{"packageName":"","name":"T[]","filePath":null,"qualifiedName":"/T[]","typeParameters":null,"directParents":null}},{"name":"initialQb","type":{"packageName":"","name":"QueryBuilder<T>","filePath":null,"qualifiedName":"/QueryBuilder<T>","typeParameters":null,"directParents":null}},{"name":"deleteQb","type":{"packageName":"","name":"QueryBuilder<T>","filePath":null,"qualifiedName":"/QueryBuilder<T>","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/db","name":"TableWatcher","filePath":null,"qualifiedName":"@proteinjs/db/TableWatcher","properties":[],"methods":[],"typeParameters":["@proteinjs/user/Session"],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/db/TableWatcher"},{"v":"@proteinjs/user-server/userCache","value":{"packageName":"@proteinjs/user-server","name":"userCache","filePath":"/home/runner/work/user/user/packages/server/src/authorization/userCache.ts","qualifiedName":"@proteinjs/user-server/userCache","type":{"packageName":"","name":"SessionDataCache<User>","filePath":null,"qualifiedName":"/SessionDataCache<User>","typeParameters":[],"directParents":[{"packageName":"@proteinjs/server-api","name":"SessionDataCache","filePath":null,"qualifiedName":"@proteinjs/server-api/SessionDataCache","typeParameters":["@proteinjs/user/User"],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/server-api/SessionDataCache"},{"v":"@proteinjs/user-server/BackfillUserRolesArray","value":{"packageName":"@proteinjs/user-server","name":"BackfillUserRolesArray","filePath":"/home/runner/work/user/user/packages/server/src/migrations/BackfillUserRolesArray.ts","qualifiedName":"@proteinjs/user-server/BackfillUserRolesArray","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"table","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"record","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/db","name":"SourceRecordLoader","filePath":null,"qualifiedName":"@proteinjs/db/SourceRecordLoader","properties":[],"methods":[],"typeParameters":["@proteinjs/db/Migration"],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/db/SourceRecordLoader"},{"v":"@proteinjs/user-server/avatar","value":{"packageName":"@proteinjs/user-server","name":"avatar","filePath":"/home/runner/work/user/user/packages/server/src/routes/avatar.ts","qualifiedName":"@proteinjs/user-server/avatar","type":{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":[{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/server-api/Route"},{"v":"@proteinjs/user-server/devLogin","value":{"packageName":"@proteinjs/user-server","name":"devLogin","filePath":"/home/runner/work/user/user/packages/server/src/routes/devLogin.ts","qualifiedName":"@proteinjs/user-server/devLogin","type":{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":[{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/user-server/executePasswordReset","value":{"packageName":"@proteinjs/user-server","name":"executePasswordReset","filePath":"/home/runner/work/user/user/packages/server/src/routes/executePasswordReset.ts","qualifiedName":"@proteinjs/user-server/executePasswordReset","type":{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":[{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/user-server/initiatePasswordReset","value":{"packageName":"@proteinjs/user-server","name":"initiatePasswordReset","filePath":"/home/runner/work/user/user/packages/server/src/routes/initiatePasswordReset.ts","qualifiedName":"@proteinjs/user-server/initiatePasswordReset","type":{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":[{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/user-server/login","value":{"packageName":"@proteinjs/user-server","name":"login","filePath":"/home/runner/work/user/user/packages/server/src/routes/login.ts","qualifiedName":"@proteinjs/user-server/login","type":{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":[{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/user-server/logout","value":{"packageName":"@proteinjs/user-server","name":"logout","filePath":"/home/runner/work/user/user/packages/server/src/routes/logout.ts","qualifiedName":"@proteinjs/user-server/logout","type":{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":[{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/user-server/validateResetPasswordToken","value":{"packageName":"@proteinjs/user-server","name":"validateResetPasswordToken","filePath":"/home/runner/work/user/user/packages/server/src/routes/validateResetPasswordToken.ts","qualifiedName":"@proteinjs/user-server/validateResetPasswordToken","type":{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":[{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/user-server/AccessInvite","value":{"packageName":"@proteinjs/user-server","name":"AccessInvite","filePath":"/home/runner/work/user/user/packages/server/src/services/AccessInvite.ts","qualifiedName":"@proteinjs/user-server/AccessInvite","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"serviceMetadata","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"createAccessInvite","returnType":{"packageName":"","name":"Promise<CreateAccessInviteResponse>","filePath":null,"qualifiedName":"/Promise<CreateAccessInviteResponse>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"params","type":{"packageName":"@proteinjs/user","name":"CreateAccessInviteParams","filePath":null,"qualifiedName":"@proteinjs/user/CreateAccessInviteParams","typeParameters":null,"directParents":null}}]},{"name":"acceptAccessInvite","returnType":{"packageName":"","name":"Promise<T | undefined>","filePath":null,"qualifiedName":"/Promise<T | undefined>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"token","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]}],"typeParameters":["/T extends Record"],"directParentInterfaces":[{"packageName":"@proteinjs/user","name":"AccessInviteService","filePath":null,"qualifiedName":"@proteinjs/user/AccessInviteService","properties":[],"methods":[],"typeParameters":["/T"],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/user/AccessInviteService"},{"v":"@proteinjs/user-server/Roles","value":{"packageName":"@proteinjs/user-server","name":"Roles","filePath":"/home/runner/work/user/user/packages/server/src/services/Roles.ts","qualifiedName":"@proteinjs/user-server/Roles","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"serviceMetadata","type":{"packageName":"","name":"Service[\'serviceMetadata\']","filePath":null,"qualifiedName":"/Service[\'serviceMetadata\']","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"grantRole","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"userId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"role","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"revokeRole","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"userId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"role","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"changeRole","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"userId","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"role","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"action","type":{"packageName":"","name":"\'grant\' | \'revoke\'","filePath":null,"qualifiedName":"/\'grant\' | \'revoke\'","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/user","name":"RolesService","filePath":null,"qualifiedName":"@proteinjs/user/RolesService","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/user/RolesService"},{"v":"@proteinjs/user-server/DefaultInviteConfigFactory","value":{"packageName":"@proteinjs/user-server","name":"DefaultInviteConfigFactory","filePath":"/home/runner/work/user/user/packages/server/src/services/Signup.ts","qualifiedName":"@proteinjs/user-server/DefaultInviteConfigFactory","properties":[],"methods":[{"name":"getConfig","returnType":{"packageName":"@proteinjs/user-server","name":"InviteConfig","filePath":null,"qualifiedName":"@proteinjs/user-server/InviteConfig","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":true,"isStatic":false,"visibility":"public","parameters":[]}],"typeParameters":[],"directParents":[{"packageName":"@proteinjs/reflection","name":"Loadable","filePath":null,"qualifiedName":"@proteinjs/reflection/Loadable","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"sourceType":3}},{"v":"@proteinjs/reflection/Loadable"},{"v":"@proteinjs/user-server/Signup","value":{"packageName":"@proteinjs/user-server","name":"Signup","filePath":"/home/runner/work/user/user/packages/server/src/services/Signup.ts","qualifiedName":"@proteinjs/user-server/Signup","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"serviceMetadata","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"createUser","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"user","type":{"packageName":"@proteinjs/user","name":"UserSignup","filePath":null,"qualifiedName":"@proteinjs/user/UserSignup","typeParameters":null,"directParents":null}},{"name":"token","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"sendInvite","returnType":{"packageName":"","name":"Promise<SendInviteResponse>","filePath":null,"qualifiedName":"/Promise<SendInviteResponse>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"email","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"revokeInvite","returnType":{"packageName":"","name":"Promise<void>","filePath":null,"qualifiedName":"/Promise<void>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"email","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"initializeSignup","returnType":{"packageName":"","name":"Promise<InitializeSignupResponse>","filePath":null,"qualifiedName":"/Promise<InitializeSignupResponse>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"inviteToken","type":{"packageName":"","name":"string | undefined","filePath":null,"qualifiedName":"/string | undefined","typeParameters":null,"directParents":null}}]},{"name":"lookupInvite","returnType":{"packageName":"","name":"Promise<InviteLookup>","filePath":null,"qualifiedName":"/Promise<InviteLookup>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"token","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/user","name":"SignupService","filePath":null,"qualifiedName":"@proteinjs/user/SignupService","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/user/SignupService"},{"v":"@proteinjs/user-server/UpdateUserInfo","value":{"packageName":"@proteinjs/user-server","name":"UpdateUserInfo","filePath":"/home/runner/work/user/user/packages/server/src/services/UpdateUserInfo.ts","qualifiedName":"@proteinjs/user-server/UpdateUserInfo","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"AVATAR_SIZE","type":null,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"AVATAR_JPEG_QUALITY","type":null,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"MAX_AVATAR_PHOTO_BYTES","type":null,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"ACCEPTED_AVATAR_MIME_TYPES","type":null,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"MAX_AVATAR_EMOJI_LENGTH","type":null,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"EMOJI_PATTERN","type":null,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"serviceMetadata","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"updateName","returnType":{"packageName":"","name":"Promise<UpdatedUser>","filePath":null,"qualifiedName":"/Promise<UpdatedUser>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"name","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"updatePassword","returnType":{"packageName":"","name":"Promise<UpdatePasswordResponse>","filePath":null,"qualifiedName":"/Promise<UpdatePasswordResponse>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"currentPassword","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"newPassword","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"updateAvatarPhoto","returnType":{"packageName":"","name":"Promise<UpdatedUser>","filePath":null,"qualifiedName":"/Promise<UpdatedUser>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"fileData","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}},{"name":"mimeType","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"updateAvatarEmoji","returnType":{"packageName":"","name":"Promise<UpdatedUser>","filePath":null,"qualifiedName":"/Promise<UpdatedUser>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"emoji","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"clearAvatar","returnType":{"packageName":"","name":"Promise<UpdatedUser>","filePath":null,"qualifiedName":"/Promise<UpdatedUser>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"setAvatar","returnType":{"packageName":"","name":"Promise<UpdatedUser>","filePath":null,"qualifiedName":"/Promise<UpdatedUser>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"avatar","type":{"packageName":"@proteinjs/user-server","name":"{ avatarEmoji: string | null; avatarFileId: string | null }","filePath":null,"qualifiedName":"@proteinjs/user-server/{ avatarEmoji: string | null; avatarFileId: string | null }","typeParameters":null,"directParents":null}}]},{"name":"saveUserInfo","returnType":{"packageName":"","name":"Promise<UpdatedUser>","filePath":null,"qualifiedName":"/Promise<UpdatedUser>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"changes","type":{"packageName":"","name":"Partial<User>","filePath":null,"qualifiedName":"/Partial<User>","typeParameters":null,"directParents":null}}]},{"name":"toAvatarJpeg","returnType":{"packageName":"","name":"Promise<Buffer>","filePath":null,"qualifiedName":"/Promise<Buffer>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"imageBytes","type":{"packageName":"","name":"Buffer","filePath":null,"qualifiedName":"/Buffer","typeParameters":null,"directParents":null}},{"name":"mimeType","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null}}]},{"name":"decodeHeic","returnType":{"packageName":"","name":"Promise<sharp.Sharp>","filePath":null,"qualifiedName":"/Promise<sharp.Sharp>","typeParameters":null,"directParents":null},"isAsync":true,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[{"name":"imageBytes","type":{"packageName":"","name":"Buffer","filePath":null,"qualifiedName":"/Buffer","typeParameters":null,"directParents":null}}]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/user","name":"UpdateUserInfoService","filePath":null,"qualifiedName":"@proteinjs/user/UpdateUserInfoService","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/user/UpdateUserInfoService"}],"edges":[{"v":"@proteinjs/user-server/DbSessionStore","w":"express-session/Store","value":"extends class"},{"v":"@proteinjs/user-server/SocketIOSessionWatcher","w":"@proteinjs/db/TableWatcher","value":"implements interface"},{"v":"@proteinjs/user-server/userCache","w":"@proteinjs/server-api/SessionDataCache","value":"has type"},{"v":"@proteinjs/user-server/BackfillUserRolesArray","w":"@proteinjs/db/SourceRecordLoader","value":"implements interface"},{"v":"@proteinjs/user-server/avatar","w":"@proteinjs/server-api/Route","value":"has type"},{"v":"@proteinjs/user-server/devLogin","w":"@proteinjs/server-api/Route","value":"has type"},{"v":"@proteinjs/user-server/executePasswordReset","w":"@proteinjs/server-api/Route","value":"has type"},{"v":"@proteinjs/user-server/initiatePasswordReset","w":"@proteinjs/server-api/Route","value":"has type"},{"v":"@proteinjs/user-server/login","w":"@proteinjs/server-api/Route","value":"has type"},{"v":"@proteinjs/user-server/logout","w":"@proteinjs/server-api/Route","value":"has type"},{"v":"@proteinjs/user-server/validateResetPasswordToken","w":"@proteinjs/server-api/Route","value":"has type"},{"v":"@proteinjs/user-server/AccessInvite","w":"@proteinjs/user/AccessInviteService","value":"implements interface"},{"v":"@proteinjs/user-server/Roles","w":"@proteinjs/user/RolesService","value":"implements interface"},{"v":"@proteinjs/user-server/DefaultInviteConfigFactory","w":"@proteinjs/reflection/Loadable","value":"extends interface"},{"v":"@proteinjs/user-server/Signup","w":"@proteinjs/user/SignupService","value":"implements interface"},{"v":"@proteinjs/user-server/UpdateUserInfo","w":"@proteinjs/user/UpdateUserInfoService","value":"implements interface"}]}';
|
|
19
15
|
|
|
20
16
|
/** Generate Source Links */
|
|
21
17
|
|
|
22
18
|
import { DbSessionStore } from '../src/authentication/DbSessionStore';
|
|
23
19
|
import { SocketIOSessionWatcher } from '../src/authentication/SocketIOSessionWatcher';
|
|
24
20
|
import { userCache } from '../src/authorization/userCache';
|
|
21
|
+
import { BackfillUserRolesArray } from '../src/migrations/BackfillUserRolesArray';
|
|
22
|
+
import { avatar } from '../src/routes/avatar';
|
|
25
23
|
import { devLogin } from '../src/routes/devLogin';
|
|
26
24
|
import { executePasswordReset } from '../src/routes/executePasswordReset';
|
|
27
25
|
import { initiatePasswordReset } from '../src/routes/initiatePasswordReset';
|
|
@@ -32,30 +30,28 @@ import { AccessInvite } from '../src/services/AccessInvite';
|
|
|
32
30
|
import { Roles } from '../src/services/Roles';
|
|
33
31
|
import { Signup } from '../src/services/Signup';
|
|
34
32
|
import { UpdateUserInfo } from '../src/services/UpdateUserInfo';
|
|
35
|
-
import { BackfillUserRolesArray } from '../src/migrations/BackfillUserRolesArray';
|
|
36
33
|
|
|
37
34
|
const sourceLinks = {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
35
|
+
'@proteinjs/user-server/DbSessionStore': DbSessionStore,
|
|
36
|
+
'@proteinjs/user-server/SocketIOSessionWatcher': SocketIOSessionWatcher,
|
|
37
|
+
'@proteinjs/user-server/userCache': userCache,
|
|
38
|
+
'@proteinjs/user-server/BackfillUserRolesArray': BackfillUserRolesArray,
|
|
39
|
+
'@proteinjs/user-server/avatar': avatar,
|
|
40
|
+
'@proteinjs/user-server/devLogin': devLogin,
|
|
41
|
+
'@proteinjs/user-server/executePasswordReset': executePasswordReset,
|
|
42
|
+
'@proteinjs/user-server/initiatePasswordReset': initiatePasswordReset,
|
|
43
|
+
'@proteinjs/user-server/login': login,
|
|
44
|
+
'@proteinjs/user-server/logout': logout,
|
|
45
|
+
'@proteinjs/user-server/validateResetPasswordToken': validateResetPasswordToken,
|
|
46
|
+
'@proteinjs/user-server/AccessInvite': AccessInvite,
|
|
47
|
+
'@proteinjs/user-server/Roles': Roles,
|
|
48
|
+
'@proteinjs/user-server/Signup': Signup,
|
|
49
|
+
'@proteinjs/user-server/UpdateUserInfo': UpdateUserInfo,
|
|
52
50
|
};
|
|
53
51
|
|
|
54
|
-
|
|
55
52
|
/** Load Source Graph and Links */
|
|
56
53
|
|
|
57
54
|
import { SourceRepository } from '@proteinjs/reflection';
|
|
58
55
|
SourceRepository.merge(sourceGraph, sourceLinks);
|
|
59
56
|
|
|
60
|
-
|
|
61
|
-
export * from '../index';
|
|
57
|
+
export * from '../index';
|
package/jest.config.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
roots: ['<rootDir>/test'],
|
|
3
|
-
setupFiles: ['<rootDir>/test/setup.js'],
|
|
4
3
|
transform: {
|
|
5
4
|
'^.+\\.tsx?$': 'ts-jest',
|
|
6
5
|
},
|
|
7
6
|
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
|
|
8
7
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
|
|
9
8
|
testEnvironment: 'node',
|
|
9
|
+
testTimeout: 60000,
|
|
10
|
+
setupFiles: ['./test/setup'],
|
|
11
|
+
globalSetup: './test/emulatorLock.globalSetup.js',
|
|
12
|
+
globalTeardown: './test/emulatorLock.globalTeardown.js',
|
|
10
13
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proteinjs/user-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "User server components",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -23,18 +23,21 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@proteinjs/db": "^1.28.1",
|
|
26
|
+
"@proteinjs/db-file": "^1.4.5",
|
|
26
27
|
"@proteinjs/email-server": "^1.3.0",
|
|
27
28
|
"@proteinjs/logger": "^1.0.21",
|
|
28
29
|
"@proteinjs/reflection": "^1.1.12",
|
|
29
30
|
"@proteinjs/server": "^3.2.1",
|
|
30
31
|
"@proteinjs/server-api": "^3.0.11",
|
|
31
|
-
"@proteinjs/user": "^1.
|
|
32
|
+
"@proteinjs/user": "^1.8.0",
|
|
32
33
|
"crypto-js": "4.0.0",
|
|
33
34
|
"express-session": "1.17.1",
|
|
34
|
-
"
|
|
35
|
+
"heic-decode": "^2.1.0",
|
|
36
|
+
"moment": "2.29.4",
|
|
37
|
+
"sharp": "0.33.4"
|
|
35
38
|
},
|
|
36
39
|
"devDependencies": {
|
|
37
|
-
"@proteinjs/db-driver-spanner": "^1.
|
|
40
|
+
"@proteinjs/db-driver-spanner": "^1.16.0",
|
|
38
41
|
"@proteinjs/reflection-build": "^1.4.10",
|
|
39
42
|
"@types/crypto-js": "4.0.1",
|
|
40
43
|
"@types/express-session": "1.17.0",
|
|
@@ -50,5 +53,6 @@
|
|
|
50
53
|
"typescript": "5.2.2"
|
|
51
54
|
},
|
|
52
55
|
"main": "./dist/generated/index.js",
|
|
53
|
-
"types": "./dist/generated/index.d.ts"
|
|
56
|
+
"types": "./dist/generated/index.d.ts",
|
|
57
|
+
"gitHead": "ca5406c5d08120454950abcb42a54bf77531b3e0"
|
|
54
58
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ambient types for `heic-decode` (ships no TypeScript types): WASM libheif + libde265, so it
|
|
3
|
+
* decodes HEVC-encoded HEIC — which sharp's prebuilt libvips cannot — on any platform.
|
|
4
|
+
*/
|
|
5
|
+
declare module 'heic-decode' {
|
|
6
|
+
function decode(input: { buffer: Buffer | Uint8Array }): Promise<{
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
/** RGBA pixel data, `width * height * 4` bytes. */
|
|
10
|
+
data: Uint8ClampedArray;
|
|
11
|
+
}>;
|
|
12
|
+
export = decode;
|
|
13
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Route } from '@proteinjs/server-api';
|
|
2
|
+
import { getDbAsSystem } from '@proteinjs/db';
|
|
3
|
+
import { tables, UserAuth, getScopedDbAsSystem } from '@proteinjs/user';
|
|
4
|
+
import { FileStorage, File, tables as fileTables } from '@proteinjs/db-file';
|
|
5
|
+
import { Logger } from '@proteinjs/logger';
|
|
6
|
+
|
|
7
|
+
const logger = new Logger({ name: 'avatarRoute' });
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Shared avatar serving: `GET /avatar/:userId?v=<avatarFileId>` — any logged-in user can render
|
|
11
|
+
* any user's avatar photo (attribution in shared spaces). File rows are ScopedRecords and
|
|
12
|
+
* `GET /file/:id` reads through the OWNER's scope, so other users cannot fetch an avatar through
|
|
13
|
+
* it; this route is the deliberate, narrow door instead.
|
|
14
|
+
*
|
|
15
|
+
* System-read surface is exactly: the user row (for `avatarFileId`), that one file row (for the
|
|
16
|
+
* Content-Type), and its bytes. The file id is never taken from the request — only the id stored
|
|
17
|
+
* on the user row is served, so this can never read arbitrary files. `:userId` is a keyed lookup
|
|
18
|
+
* (no path/filesystem interpretation).
|
|
19
|
+
*
|
|
20
|
+
* Cache contract (mirrored by `avatarRoute` in @proteinjs/user routes.ts): the path is stable per
|
|
21
|
+
* user, so responses carry `max-age=86400, immutable`; clients bust with `?v=<avatarFileId>`,
|
|
22
|
+
* which changes on every photo update. Users with an emoji avatar or no avatar answer 404 — the
|
|
23
|
+
* client renders emoji/initials from the user record; this route serves photos only.
|
|
24
|
+
*/
|
|
25
|
+
export const avatar: Route = {
|
|
26
|
+
path: '/avatar/:userId',
|
|
27
|
+
method: 'get',
|
|
28
|
+
onRequest: async (request, response): Promise<void> => {
|
|
29
|
+
if (!UserAuth.isLoggedIn()) {
|
|
30
|
+
response.status(401).send('User not logged in');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const userId = request.params.userId;
|
|
36
|
+
const user = await getDbAsSystem().get(tables.User, { id: userId });
|
|
37
|
+
if (!user || !user.avatarFileId) {
|
|
38
|
+
response.status(404).send('No avatar photo');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const file = await getScopedDbAsSystem<File>().get(fileTables.File, { id: user.avatarFileId });
|
|
43
|
+
if (!file) {
|
|
44
|
+
response.status(404).send('No avatar photo');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const fileDataBase64 = await new FileStorage().getFileData(file.id);
|
|
49
|
+
response.setHeader('Content-Type', file.type);
|
|
50
|
+
response.setHeader('Cache-Control', 'public, max-age=86400, immutable');
|
|
51
|
+
response.send(Buffer.from(fileDataBase64, 'base64'));
|
|
52
|
+
} catch (error: any) {
|
|
53
|
+
logger.error({ message: 'Failed to serve avatar', error });
|
|
54
|
+
response.status(500).send('Internal Server Error');
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
};
|
package/src/routes/devLogin.ts
CHANGED
|
@@ -24,6 +24,11 @@ export const devLogin: Route = {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
await new Promise((resolve) => request.login(email, resolve));
|
|
27
|
+
// Explicit save before redirecting: with a DB-backed session store, save-on-response-end
|
|
28
|
+
// races the redirected GET / — the follow-up request can read the session row before the
|
|
29
|
+
// write commits and render the login page (observed: first /dev/login load lands on /login,
|
|
30
|
+
// second succeeds). Awaiting the store write closes the race.
|
|
31
|
+
await new Promise((resolve) => request.session.save(resolve));
|
|
27
32
|
logger.info({ message: 'Dev auto-login session established', obj: { email } });
|
|
28
33
|
response.redirect('/');
|
|
29
34
|
},
|
|
@@ -1,19 +1,36 @@
|
|
|
1
|
+
import sharp from 'sharp';
|
|
2
|
+
import heicDecode from 'heic-decode';
|
|
3
|
+
import sha256 from 'crypto-js/sha256';
|
|
1
4
|
import { getDbAsSystem } from '@proteinjs/db';
|
|
2
|
-
import {
|
|
5
|
+
import { FileStorage } from '@proteinjs/db-file';
|
|
6
|
+
import { tables, User, UserRepo, UpdatePasswordResponse, UpdatedUser, UpdateUserInfoService } from '@proteinjs/user';
|
|
3
7
|
import { EmailSender, getDefaultPasswordUpdatedEmailConfigFactory } from '@proteinjs/email-server';
|
|
4
|
-
import sha256 from 'crypto-js/sha256';
|
|
5
8
|
|
|
6
9
|
export class UpdateUserInfo implements UpdateUserInfoService {
|
|
10
|
+
/** Stored avatar photos are exactly this: cover-cropped square JPEG. */
|
|
11
|
+
private static readonly AVATAR_SIZE = 512;
|
|
12
|
+
private static readonly AVATAR_JPEG_QUALITY = 85;
|
|
13
|
+
private static readonly MAX_AVATAR_PHOTO_BYTES = 10 * 1024 * 1024;
|
|
14
|
+
private static readonly ACCEPTED_AVATAR_MIME_TYPES = [
|
|
15
|
+
'image/jpeg',
|
|
16
|
+
'image/png',
|
|
17
|
+
'image/webp',
|
|
18
|
+
'image/heic',
|
|
19
|
+
'image/heif',
|
|
20
|
+
];
|
|
21
|
+
/** One emoji (possibly a ZWJ/flag/keycap sequence) — a handful of code units, never a sentence. */
|
|
22
|
+
private static readonly MAX_AVATAR_EMOJI_LENGTH = 16;
|
|
23
|
+
/** Constructed (not a literal) because tsconfig targets es5; runs on Node, which supports `\p`. */
|
|
24
|
+
private static readonly EMOJI_PATTERN = new RegExp('[\\p{Extended_Pictographic}\\p{Regional_Indicator}\\u20E3]', 'u');
|
|
25
|
+
|
|
7
26
|
public serviceMetadata = {
|
|
8
27
|
auth: {
|
|
9
28
|
allUsers: true,
|
|
10
29
|
},
|
|
11
30
|
};
|
|
12
31
|
|
|
13
|
-
async updateName(name: string): Promise<
|
|
14
|
-
|
|
15
|
-
const user = new UserRepo().getUser();
|
|
16
|
-
await db.update(tables.User, { name }, { id: user.id });
|
|
32
|
+
async updateName(name: string): Promise<UpdatedUser> {
|
|
33
|
+
return await this.saveUserInfo({ name });
|
|
17
34
|
}
|
|
18
35
|
|
|
19
36
|
async updatePassword(currentPassword: string, newPassword: string): Promise<UpdatePasswordResponse> {
|
|
@@ -54,13 +71,7 @@ export class UpdateUserInfo implements UpdateUserInfoService {
|
|
|
54
71
|
// hash and store new password
|
|
55
72
|
try {
|
|
56
73
|
const hashedNewPassword = sha256(newPassword).toString();
|
|
57
|
-
await
|
|
58
|
-
tables.User,
|
|
59
|
-
{
|
|
60
|
-
password: hashedNewPassword,
|
|
61
|
-
},
|
|
62
|
-
{ id: user.id }
|
|
63
|
-
);
|
|
74
|
+
await this.saveUserInfo({ password: hashedNewPassword });
|
|
64
75
|
} catch (error: any) {
|
|
65
76
|
return {
|
|
66
77
|
updated: false,
|
|
@@ -72,4 +83,104 @@ export class UpdateUserInfo implements UpdateUserInfoService {
|
|
|
72
83
|
updated: true,
|
|
73
84
|
};
|
|
74
85
|
}
|
|
86
|
+
|
|
87
|
+
async updateAvatarPhoto(fileData: string, mimeType: string): Promise<UpdatedUser> {
|
|
88
|
+
if (!UpdateUserInfo.ACCEPTED_AVATAR_MIME_TYPES.includes(mimeType)) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`Unsupported avatar image type '${mimeType}'. Accepted: ${UpdateUserInfo.ACCEPTED_AVATAR_MIME_TYPES.join(', ')}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const imageBytes = Buffer.from(fileData, 'base64');
|
|
95
|
+
if (imageBytes.length > UpdateUserInfo.MAX_AVATAR_PHOTO_BYTES) {
|
|
96
|
+
throw new Error(`Avatar photo is too large (max 10MB)`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const jpeg = await this.toAvatarJpeg(imageBytes, mimeType);
|
|
100
|
+
const file = await new FileStorage().createFile(
|
|
101
|
+
{ name: 'avatar.jpg', type: 'image/jpeg', size: jpeg.length },
|
|
102
|
+
jpeg.toString('base64')
|
|
103
|
+
);
|
|
104
|
+
return await this.setAvatar({ avatarEmoji: null, avatarFileId: file.id });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async updateAvatarEmoji(emoji: string): Promise<UpdatedUser> {
|
|
108
|
+
const trimmed = emoji.trim();
|
|
109
|
+
if (
|
|
110
|
+
!trimmed ||
|
|
111
|
+
trimmed.length > UpdateUserInfo.MAX_AVATAR_EMOJI_LENGTH ||
|
|
112
|
+
/\s/.test(trimmed) ||
|
|
113
|
+
!UpdateUserInfo.EMOJI_PATTERN.test(trimmed)
|
|
114
|
+
) {
|
|
115
|
+
throw new Error(`Avatar emoji must be a single short emoji`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return await this.setAvatar({ avatarEmoji: trimmed, avatarFileId: null });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async clearAvatar(): Promise<UpdatedUser> {
|
|
122
|
+
return await this.setAvatar({ avatarEmoji: null, avatarFileId: null });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The single door to the avatar columns — every mutation assigns BOTH, which is what enforces
|
|
127
|
+
* the exactly-one-active invariant (photo OR emoji, never both). Also the previous photo's
|
|
128
|
+
* cleanup point: an avatar file the user row no longer points to is unreachable (the /avatar
|
|
129
|
+
* route serves only the id on the user row), so it is deleted here, after the row moves off it.
|
|
130
|
+
*/
|
|
131
|
+
private async setAvatar(avatar: { avatarEmoji: string | null; avatarFileId: string | null }): Promise<UpdatedUser> {
|
|
132
|
+
const userId = new UserRepo().getUser().id;
|
|
133
|
+
const previousAvatarFileId = (await getDbAsSystem().get(tables.User, { id: userId })).avatarFileId;
|
|
134
|
+
const updated = await this.saveUserInfo(avatar);
|
|
135
|
+
if (previousAvatarFileId && previousAvatarFileId !== avatar.avatarFileId) {
|
|
136
|
+
await new FileStorage().deleteFile(previousAvatarFileId);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return updated;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The one path every user-info mutation persists through: writes the changes AND refreshes the
|
|
144
|
+
* server session cache in the same stroke. userCache serializes the user into session data at
|
|
145
|
+
* request start; a bare DB write leaves every read through UserRepo in the same context — and
|
|
146
|
+
* long-lived contexts that keep their session data (sockets) — serving the STALE user until
|
|
147
|
+
* re-login (the rename wart). Fusing persist + refresh here means no future mutation can
|
|
148
|
+
* reintroduce that class of staleness.
|
|
149
|
+
*/
|
|
150
|
+
private async saveUserInfo(changes: Partial<User>): Promise<UpdatedUser> {
|
|
151
|
+
const userRepo = new UserRepo();
|
|
152
|
+
const userId = userRepo.getUser().id;
|
|
153
|
+
const db = getDbAsSystem();
|
|
154
|
+
await db.update(tables.User, changes, { id: userId });
|
|
155
|
+
const updated = await db.get(tables.User, { id: userId });
|
|
156
|
+
delete (updated as Partial<User>).password; // the same password-less shape userCache caches
|
|
157
|
+
userRepo.setUser(updated);
|
|
158
|
+
return updated;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Minimal avatar pipeline: decode, auto-orient from EXIF, cover-crop square, re-encode JPEG.
|
|
163
|
+
* sharp drops metadata (EXIF orientation/GPS) on re-encode by default — the stored avatar
|
|
164
|
+
* carries no camera metadata. HEIC/HEIF (default iPhone camera format) is decoded with WASM
|
|
165
|
+
* libheif (`heic-decode`) because sharp's prebuilt libvips has no HEVC decoder. NOTE: this
|
|
166
|
+
* duplicates the HEIC-normalization approach of chat-server's FileTransformer, which this
|
|
167
|
+
* package cannot import (layering) — future extraction target: a shared image-transform seam.
|
|
168
|
+
*/
|
|
169
|
+
private async toAvatarJpeg(imageBytes: Buffer, mimeType: string): Promise<Buffer> {
|
|
170
|
+
const pipeline =
|
|
171
|
+
mimeType === 'image/heic' || mimeType === 'image/heif'
|
|
172
|
+
? await this.decodeHeic(imageBytes)
|
|
173
|
+
: sharp(imageBytes).rotate();
|
|
174
|
+
return await pipeline
|
|
175
|
+
.resize(UpdateUserInfo.AVATAR_SIZE, UpdateUserInfo.AVATAR_SIZE, { fit: 'cover' })
|
|
176
|
+
.jpeg({ quality: UpdateUserInfo.AVATAR_JPEG_QUALITY })
|
|
177
|
+
.toBuffer();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private async decodeHeic(imageBytes: Buffer): Promise<sharp.Sharp> {
|
|
181
|
+
const { width, height, data } = await heicDecode({ buffer: imageBytes });
|
|
182
|
+
return sharp(Buffer.from(data.buffer, data.byteOffset, data.byteLength), {
|
|
183
|
+
raw: { width, height, channels: 4 },
|
|
184
|
+
});
|
|
185
|
+
}
|
|
75
186
|
}
|