feat: Add TypeScript definitions (#9693)
This commit is contained in:
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -147,6 +147,8 @@ jobs:
|
||||
- run: npm ci
|
||||
- name: Build types
|
||||
run: npm run build:types
|
||||
- name: Test Types
|
||||
run: npm run test:types
|
||||
check-mongo:
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
2
.npmignore
Normal file
2
.npmignore
Normal file
@@ -0,0 +1,2 @@
|
||||
types/tests.ts
|
||||
types/eslint.config.mjs
|
||||
@@ -21,6 +21,7 @@
|
||||
- [Good to Know](#good-to-know)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Please Do's](#please-dos)
|
||||
- [TypeScript Tests](#typescript-tests)
|
||||
- [Test against Postgres](#test-against-postgres)
|
||||
- [Postgres with Docker](#postgres-with-docker)
|
||||
- [Breaking Changes](#breaking-changes)
|
||||
@@ -239,6 +240,15 @@ Once you have babel running in watch mode, you can start making changes to parse
|
||||
* Mocks belong in the `spec/support` folder.
|
||||
* Please consider if any changes to the [docs](http://docs.parseplatform.org) are needed or add additional sections in the case of an enhancement or feature.
|
||||
|
||||
#### TypeScript Tests
|
||||
|
||||
Type tests are located in [/types/tests.ts](/types/tests.ts) and are responsible for ensuring that the type generation for each class is behaving as expected. Types are generated by manually running the script `npm run build:types`. The generated types are `.d.ts` files located in [/types](/types) and must not be manually changed after generation.
|
||||
|
||||
> [!CAUTION]
|
||||
> An exemption are type changes to `src/Options/index.js` which must be manually updated in `types/Options/index.d.ts`, as these types are not generated via a script.
|
||||
|
||||
When developing type definitions you can run `npm run watch:ts` in order to rebuild your changes automatically upon each save. Use `npm run test:types` in order to run types tests against generated `.d.ts` files.
|
||||
|
||||
### Test against Postgres
|
||||
|
||||
If your pull request introduces a change that may affect the storage or retrieval of objects, you may want to make sure it plays nice with Postgres.
|
||||
|
||||
@@ -3,7 +3,7 @@ const babelParser = require("@babel/eslint-parser");
|
||||
const globals = require("globals");
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ["**/lib/**", "**/coverage/**", "**/out/**"],
|
||||
ignores: ["**/lib/**", "**/coverage/**", "**/out/**", "**/types/**"],
|
||||
},
|
||||
js.configs.recommended,
|
||||
{
|
||||
|
||||
952
package-lock.json
generated
952
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -88,6 +88,7 @@
|
||||
"cross-env": "7.0.3",
|
||||
"deep-diff": "1.0.2",
|
||||
"eslint": "9.23.0",
|
||||
"eslint-plugin-expect-type": "0.6.2",
|
||||
"flow-bin": "0.266.1",
|
||||
"form-data": "4.0.2",
|
||||
"globals": "16.0.0",
|
||||
@@ -109,6 +110,7 @@
|
||||
"prettier": "2.0.5",
|
||||
"semantic-release": "24.2.3",
|
||||
"typescript": "5.8.2",
|
||||
"typescript-eslint": "8.29.0",
|
||||
"yaml": "2.7.1"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -122,6 +124,7 @@
|
||||
"build": "babel src/ -d lib/ --copy-files --extensions '.ts,.js'",
|
||||
"build:types": "tsc",
|
||||
"watch": "babel --watch src/ -d lib/ --copy-files",
|
||||
"watch:ts": "tsc --watch",
|
||||
"test:mongodb:runnerstart": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017",
|
||||
"test:mongodb:testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine",
|
||||
"test:mongodb": "npm run test:mongodb:runnerstart --dbversion=$npm_config_dbversion && npm run test:mongodb:testonly --dbversion=$npm_config_dbversion",
|
||||
@@ -132,6 +135,7 @@
|
||||
"pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=8.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017",
|
||||
"testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=8.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine",
|
||||
"test": "npm run testonly",
|
||||
"test:types": "eslint types/tests.ts -c ./types/eslint.config.mjs",
|
||||
"posttest": "cross-env mongodb-runner stop --all",
|
||||
"coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=8.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 nyc jasmine",
|
||||
"start": "node ./bin/parse-server",
|
||||
|
||||
@@ -3,6 +3,7 @@ import Parse from 'parse/node';
|
||||
import { Subscription } from './Subscription';
|
||||
import { Client } from './Client';
|
||||
import { ParseWebSocketServer } from './ParseWebSocketServer';
|
||||
// @ts-ignore
|
||||
import logger from '../logger';
|
||||
import RequestSchema from './RequestSchema';
|
||||
import { matchesQuery, queryHash } from './QueryTools';
|
||||
@@ -26,13 +27,17 @@ import { isDeepStrictEqual } from 'util';
|
||||
import deepcopy from 'deepcopy';
|
||||
|
||||
class ParseLiveQueryServer {
|
||||
clients: Map;
|
||||
server: any;
|
||||
config: any;
|
||||
clients: Map<string, any>;
|
||||
// className -> (queryHash -> subscription)
|
||||
subscriptions: Object;
|
||||
parseWebSocketServer: Object;
|
||||
subscriptions: Map<string, any>;
|
||||
parseWebSocketServer: any;
|
||||
keyPairs: any;
|
||||
// The subscriber we use to get object update from publisher
|
||||
subscriber: Object;
|
||||
subscriber: any;
|
||||
authCache: any;
|
||||
cacheController: any;
|
||||
|
||||
constructor(server: any, config: any = {}, parseServerConfig: any = {}) {
|
||||
this.server = server;
|
||||
@@ -168,7 +173,7 @@ class ParseLiveQueryServer {
|
||||
|
||||
// Message is the JSON object from publisher after inflated. Message.currentParseObject is the ParseObject after changes.
|
||||
// Message.originalParseObject is the original ParseObject.
|
||||
async _onAfterDelete(message: any): void {
|
||||
async _onAfterDelete(message: any): Promise<void> {
|
||||
logger.verbose(Parse.applicationId + 'afterDelete is triggered');
|
||||
|
||||
let deletedParseObject = message.currentParseObject.toJSON();
|
||||
@@ -197,7 +202,7 @@ class ParseLiveQueryServer {
|
||||
const acl = message.currentParseObject.getACL();
|
||||
// Check CLP
|
||||
const op = this._getCLPOperation(subscription.query);
|
||||
let res = {};
|
||||
let res: any = {};
|
||||
try {
|
||||
await this._matchesCLP(
|
||||
classLevelPermissions,
|
||||
@@ -261,7 +266,7 @@ class ParseLiveQueryServer {
|
||||
|
||||
// Message is the JSON object from publisher after inflated. Message.currentParseObject is the ParseObject after changes.
|
||||
// Message.originalParseObject is the original ParseObject.
|
||||
async _onAfterSave(message: any): void {
|
||||
async _onAfterSave(message: any): Promise<void> {
|
||||
logger.verbose(Parse.applicationId + 'afterSave is triggered');
|
||||
|
||||
let originalParseObject = null;
|
||||
@@ -309,7 +314,7 @@ class ParseLiveQueryServer {
|
||||
// Set current ParseObject ACL checking promise, if the object does not match
|
||||
// subscription, we do not need to check ACL
|
||||
let currentACLCheckingPromise;
|
||||
let res = {};
|
||||
let res: any = {};
|
||||
if (!isCurrentSubscriptionMatched) {
|
||||
currentACLCheckingPromise = Promise.resolve(false);
|
||||
} else {
|
||||
@@ -548,7 +553,7 @@ class ParseLiveQueryServer {
|
||||
}
|
||||
}
|
||||
|
||||
getAuthForSessionToken(sessionToken: ?string): Promise<{ auth: ?Auth, userId: ?string }> {
|
||||
getAuthForSessionToken(sessionToken?: string): Promise<{ auth?: Auth, userId?: string }> {
|
||||
if (!sessionToken) {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
@@ -565,7 +570,7 @@ class ParseLiveQueryServer {
|
||||
})
|
||||
.catch(error => {
|
||||
// There was an error with the session token
|
||||
const result = {};
|
||||
const result: any = {};
|
||||
if (error && error.code === Parse.Error.INVALID_SESSION_TOKEN) {
|
||||
result.error = error;
|
||||
this.authCache.set(sessionToken, Promise.resolve(result), this.config.cacheTimeout);
|
||||
@@ -579,12 +584,12 @@ class ParseLiveQueryServer {
|
||||
}
|
||||
|
||||
async _matchesCLP(
|
||||
classLevelPermissions: ?any,
|
||||
object: any,
|
||||
client: any,
|
||||
requestId: number,
|
||||
op: string
|
||||
): any {
|
||||
classLevelPermissions?: any,
|
||||
object?: any,
|
||||
client?: any,
|
||||
requestId?: number,
|
||||
op?: string
|
||||
): Promise<any> {
|
||||
// try to match on user first, less expensive than with roles
|
||||
const subscriptionInfo = client.getSubscriptionInfo(requestId);
|
||||
const aclGroup = ['*'];
|
||||
@@ -621,12 +626,12 @@ class ParseLiveQueryServer {
|
||||
}
|
||||
|
||||
async _filterSensitiveData(
|
||||
classLevelPermissions: ?any,
|
||||
res: any,
|
||||
client: any,
|
||||
requestId: number,
|
||||
op: string,
|
||||
query: any
|
||||
classLevelPermissions?: any,
|
||||
res?: any,
|
||||
client?: any,
|
||||
requestId?: number,
|
||||
op?: string,
|
||||
query?: any
|
||||
) {
|
||||
const subscriptionInfo = client.getSubscriptionInfo(requestId);
|
||||
const aclGroup = ['*'];
|
||||
@@ -718,7 +723,7 @@ class ParseLiveQueryServer {
|
||||
});
|
||||
}
|
||||
|
||||
async getAuthFromClient(client: any, requestId: number, sessionToken: string) {
|
||||
async getAuthFromClient(client: any, requestId: number, sessionToken?: string) {
|
||||
const getSessionFromClient = () => {
|
||||
const subscriptionInfo = client.getSubscriptionInfo(requestId);
|
||||
if (typeof subscriptionInfo === 'undefined') {
|
||||
@@ -772,7 +777,7 @@ class ParseLiveQueryServer {
|
||||
return false;
|
||||
}
|
||||
|
||||
async _handleConnect(parseWebsocket: any, request: any): any {
|
||||
async _handleConnect(parseWebsocket: any, request: any): Promise<any> {
|
||||
if (!this._validateKeys(request, this.keyPairs)) {
|
||||
Client.pushError(parseWebsocket, 4, 'Key in request is not valid');
|
||||
logger.error('Key in request is not valid');
|
||||
@@ -796,6 +801,7 @@ class ParseLiveQueryServer {
|
||||
sessionToken: request.sessionToken,
|
||||
useMasterKey: client.hasMasterKey,
|
||||
installationId: request.installationId,
|
||||
user: undefined,
|
||||
};
|
||||
const trigger = getTrigger('@Connect', 'beforeConnect', Parse.applicationId);
|
||||
if (trigger) {
|
||||
@@ -845,7 +851,7 @@ class ParseLiveQueryServer {
|
||||
return isValid;
|
||||
}
|
||||
|
||||
async _handleSubscribe(parseWebsocket: any, request: any): any {
|
||||
async _handleSubscribe(parseWebsocket: any, request: any): Promise<any> {
|
||||
// If we can not find this client, return error to client
|
||||
if (!Object.prototype.hasOwnProperty.call(parseWebsocket, 'clientId')) {
|
||||
Client.pushError(
|
||||
@@ -918,7 +924,7 @@ class ParseLiveQueryServer {
|
||||
}
|
||||
|
||||
// Add subscriptionInfo to client
|
||||
const subscriptionInfo = {
|
||||
const subscriptionInfo: any = {
|
||||
subscription: subscription,
|
||||
};
|
||||
// Add selected fields, sessionToken and installationId for this subscription if necessary
|
||||
@@ -56,6 +56,11 @@ const connections = new Connections();
|
||||
// ParseServer works like a constructor of an express app.
|
||||
// https://parseplatform.org/parse-server/api/master/ParseServerOptions.html
|
||||
class ParseServer {
|
||||
_app: any;
|
||||
config: any;
|
||||
server: any;
|
||||
expressApp: any;
|
||||
liveQueryServer: any;
|
||||
/**
|
||||
* @constructor
|
||||
* @param {ParseServerOptions} options the parse server initialization options
|
||||
@@ -111,7 +116,7 @@ class ParseServer {
|
||||
|
||||
const diff = validateKeyNames(options, optionsBlueprint);
|
||||
if (diff.length > 0) {
|
||||
const logger = logging.logger;
|
||||
const logger = (logging as any).logger;
|
||||
logger.error(`Invalid key(s) found in Parse Server configuration: ${diff.join(', ')}`);
|
||||
}
|
||||
|
||||
@@ -129,7 +134,7 @@ class ParseServer {
|
||||
Config.validateOptions(options);
|
||||
const allControllers = controllers.getControllers(options);
|
||||
|
||||
options.state = 'initialized';
|
||||
(options as any).state = 'initialized';
|
||||
this.config = Config.put(Object.assign({}, options, allControllers));
|
||||
this.config.masterKeyIpsStore = new Map();
|
||||
this.config.maintenanceKeyIpsStore = new Map();
|
||||
@@ -140,7 +145,7 @@ class ParseServer {
|
||||
* Starts Parse Server as an express app; this promise resolves when Parse Server is ready to accept requests.
|
||||
*/
|
||||
|
||||
async start() {
|
||||
async start(): Promise<this> {
|
||||
try {
|
||||
if (this.config.state === 'ok') {
|
||||
return this;
|
||||
@@ -331,7 +336,7 @@ class ParseServer {
|
||||
if (!process.env.TESTING) {
|
||||
//This causes tests to spew some useless warnings, so disable in test
|
||||
/* istanbul ignore next */
|
||||
process.on('uncaughtException', err => {
|
||||
process.on('uncaughtException', (err: any) => {
|
||||
if (err.code === 'EADDRINUSE') {
|
||||
// user-friendly message for this common error
|
||||
process.stderr.write(`Unable to listen on port ${err.port}. The port is already in use.`);
|
||||
@@ -497,7 +502,7 @@ class ParseServer {
|
||||
httpServer,
|
||||
config: LiveQueryServerOptions,
|
||||
options: ParseServerOptions
|
||||
) {
|
||||
): Promise<ParseLiveQueryServer> {
|
||||
if (!httpServer || (config && config.port)) {
|
||||
var app = express();
|
||||
httpServer = require('http').createServer(app);
|
||||
@@ -1,11 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "types",
|
||||
"noImplicitAny": false,
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"deepcopy": ["./types/@types/deepcopy"],
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/*.ts"
|
||||
|
||||
5
types/@types/@parse/fs-files-adapter/index.d.ts
vendored
Normal file
5
types/@types/@parse/fs-files-adapter/index.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// TODO: Remove when @parse/fs-files-adapter is typed
|
||||
declare module '@parse/fs-files-adapter' {
|
||||
const FileSystemAdapter: any;
|
||||
export default FileSystemAdapter;
|
||||
}
|
||||
5
types/@types/deepcopy/index.d.ts
vendored
Normal file
5
types/@types/deepcopy/index.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// TODO: Remove when https://github.com/sasaplus1/deepcopy.js/issues/278 is fixed
|
||||
declare type Customizer = (value: any, valueType: string) => unknown;
|
||||
declare type Options = Customizer | { customizer: Customizer };
|
||||
declare function deepcopy<T>(value: T, options?: Options): T;
|
||||
export default deepcopy;
|
||||
40
types/LiveQuery/ParseLiveQueryServer.d.ts
vendored
Normal file
40
types/LiveQuery/ParseLiveQueryServer.d.ts
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
import { Auth } from '../Auth';
|
||||
declare class ParseLiveQueryServer {
|
||||
server: any;
|
||||
config: any;
|
||||
clients: Map<string, any>;
|
||||
subscriptions: Map<string, any>;
|
||||
parseWebSocketServer: any;
|
||||
keyPairs: any;
|
||||
subscriber: any;
|
||||
authCache: any;
|
||||
cacheController: any;
|
||||
constructor(server: any, config?: any, parseServerConfig?: any);
|
||||
connect(): Promise<void>;
|
||||
shutdown(): Promise<void>;
|
||||
_createSubscribers(): void;
|
||||
_inflateParseObject(message: any): void;
|
||||
_onAfterDelete(message: any): Promise<void>;
|
||||
_onAfterSave(message: any): Promise<void>;
|
||||
_onConnect(parseWebsocket: any): void;
|
||||
_matchesSubscription(parseObject: any, subscription: any): boolean;
|
||||
_clearCachedRoles(userId: string): Promise<void>;
|
||||
getAuthForSessionToken(sessionToken?: string): Promise<{
|
||||
auth?: Auth;
|
||||
userId?: string;
|
||||
}>;
|
||||
_matchesCLP(classLevelPermissions?: any, object?: any, client?: any, requestId?: number, op?: string): Promise<any>;
|
||||
_filterSensitiveData(classLevelPermissions?: any, res?: any, client?: any, requestId?: number, op?: string, query?: any): Promise<void>;
|
||||
_getCLPOperation(query: any): "get" | "find";
|
||||
_verifyACL(acl: any, token: string): Promise<boolean>;
|
||||
getAuthFromClient(client: any, requestId: number, sessionToken?: string): Promise<Auth>;
|
||||
_checkWatchFields(client: any, requestId: any, message: any): any;
|
||||
_matchesACL(acl: any, client: any, requestId: number): Promise<boolean>;
|
||||
_handleConnect(parseWebsocket: any, request: any): Promise<any>;
|
||||
_hasMasterKey(request: any, validKeyPairs: any): boolean;
|
||||
_validateKeys(request: any, validKeyPairs: any): boolean;
|
||||
_handleSubscribe(parseWebsocket: any, request: any): Promise<any>;
|
||||
_handleUpdateSubscription(parseWebsocket: any, request: any): any;
|
||||
_handleUnsubscribe(parseWebsocket: any, request: any, notifyClient?: boolean): any;
|
||||
}
|
||||
export { ParseLiveQueryServer };
|
||||
251
types/Options/index.d.ts
vendored
Normal file
251
types/Options/index.d.ts
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
// This file is manually updated to match src/Options/index.js until typed
|
||||
import { AnalyticsAdapter } from '../Adapters/Analytics/AnalyticsAdapter';
|
||||
import { CacheAdapter } from '../Adapters/Cache/CacheAdapter';
|
||||
import { MailAdapter } from '../Adapters/Email/MailAdapter';
|
||||
import { FilesAdapter } from '../Adapters/Files/FilesAdapter';
|
||||
import { LoggerAdapter } from '../Adapters/Logger/LoggerAdapter';
|
||||
import { PubSubAdapter } from '../Adapters/PubSub/PubSubAdapter';
|
||||
import { StorageAdapter } from '../Adapters/Storage/StorageAdapter';
|
||||
import { WSSAdapter } from '../Adapters/WebSocketServer/WSSAdapter';
|
||||
import { CheckGroup } from '../Security/CheckGroup';
|
||||
export interface SchemaOptions {
|
||||
definitions: any;
|
||||
strict?: boolean;
|
||||
deleteExtraFields?: boolean;
|
||||
recreateModifiedFields?: boolean;
|
||||
lockSchemas?: boolean;
|
||||
beforeMigration?: () => void | Promise<void>;
|
||||
afterMigration?: () => void | Promise<void>;
|
||||
}
|
||||
type Adapter<T> = string | T;
|
||||
type NumberOrBoolean = number | boolean;
|
||||
type NumberOrString = number | string;
|
||||
type ProtectedFields = any;
|
||||
type StringOrStringArray = string | string[];
|
||||
type RequestKeywordDenylist = {
|
||||
key: string;
|
||||
value: any;
|
||||
};
|
||||
export interface ParseServerOptions {
|
||||
appId: string;
|
||||
masterKey: (() => void) | string;
|
||||
masterKeyTtl?: number;
|
||||
maintenanceKey: string;
|
||||
serverURL: string;
|
||||
masterKeyIps?: (string[]);
|
||||
maintenanceKeyIps?: (string[]);
|
||||
appName?: string;
|
||||
allowHeaders?: (string[]);
|
||||
allowOrigin?: StringOrStringArray;
|
||||
analyticsAdapter?: Adapter<AnalyticsAdapter>;
|
||||
filesAdapter?: Adapter<FilesAdapter>;
|
||||
push?: any;
|
||||
scheduledPush?: boolean;
|
||||
loggerAdapter?: Adapter<LoggerAdapter>;
|
||||
jsonLogs?: boolean;
|
||||
logsFolder?: string;
|
||||
verbose?: boolean;
|
||||
logLevel?: string;
|
||||
logLevels?: LogLevels;
|
||||
maxLogFiles?: NumberOrString;
|
||||
silent?: boolean;
|
||||
databaseURI: string;
|
||||
databaseOptions?: DatabaseOptions;
|
||||
databaseAdapter?: Adapter<StorageAdapter>;
|
||||
enableCollationCaseComparison?: boolean;
|
||||
convertEmailToLowercase?: boolean;
|
||||
convertUsernameToLowercase?: boolean;
|
||||
cloud?: string;
|
||||
collectionPrefix?: string;
|
||||
clientKey?: string;
|
||||
javascriptKey?: string;
|
||||
dotNetKey?: string;
|
||||
encryptionKey?: string;
|
||||
restAPIKey?: string;
|
||||
readOnlyMasterKey?: string;
|
||||
webhookKey?: string;
|
||||
fileKey?: string;
|
||||
preserveFileName?: boolean;
|
||||
userSensitiveFields?: (string[]);
|
||||
protectedFields?: ProtectedFields;
|
||||
enableAnonymousUsers?: boolean;
|
||||
allowClientClassCreation?: boolean;
|
||||
allowCustomObjectId?: boolean;
|
||||
auth?: Record<string, AuthAdapter>;
|
||||
enableInsecureAuthAdapters?: boolean;
|
||||
maxUploadSize?: string;
|
||||
verifyUserEmails?: (boolean | void);
|
||||
preventLoginWithUnverifiedEmail?: boolean;
|
||||
preventSignupWithUnverifiedEmail?: boolean;
|
||||
emailVerifyTokenValidityDuration?: number;
|
||||
emailVerifyTokenReuseIfValid?: boolean;
|
||||
sendUserEmailVerification?: (boolean | void);
|
||||
accountLockout?: AccountLockoutOptions;
|
||||
passwordPolicy?: PasswordPolicyOptions;
|
||||
cacheAdapter?: Adapter<CacheAdapter>;
|
||||
emailAdapter?: Adapter<MailAdapter>;
|
||||
encodeParseObjectInCloudFunction?: boolean;
|
||||
publicServerURL?: string;
|
||||
pages?: PagesOptions;
|
||||
customPages?: CustomPagesOptions;
|
||||
liveQuery?: LiveQueryOptions;
|
||||
sessionLength?: number;
|
||||
extendSessionOnUse?: boolean;
|
||||
defaultLimit?: number;
|
||||
maxLimit?: number;
|
||||
expireInactiveSessions?: boolean;
|
||||
revokeSessionOnPasswordReset?: boolean;
|
||||
cacheTTL?: number;
|
||||
cacheMaxSize?: number;
|
||||
directAccess?: boolean;
|
||||
enableExpressErrorHandler?: boolean;
|
||||
objectIdSize?: number;
|
||||
port?: number;
|
||||
host?: string;
|
||||
mountPath?: string;
|
||||
cluster?: NumberOrBoolean;
|
||||
middleware?: ((() => void) | string);
|
||||
trustProxy?: any;
|
||||
startLiveQueryServer?: boolean;
|
||||
liveQueryServerOptions?: LiveQueryServerOptions;
|
||||
idempotencyOptions?: IdempotencyOptions;
|
||||
fileUpload?: FileUploadOptions;
|
||||
graphQLSchema?: string;
|
||||
mountGraphQL?: boolean;
|
||||
graphQLPath?: string;
|
||||
mountPlayground?: boolean;
|
||||
playgroundPath?: string;
|
||||
schema?: SchemaOptions;
|
||||
serverCloseComplete?: () => void;
|
||||
security?: SecurityOptions;
|
||||
enforcePrivateUsers?: boolean;
|
||||
allowExpiredAuthDataToken?: boolean;
|
||||
requestKeywordDenylist?: (RequestKeywordDenylist[]);
|
||||
rateLimit?: (RateLimitOptions[]);
|
||||
}
|
||||
export interface RateLimitOptions {
|
||||
requestPath: string;
|
||||
requestTimeWindow?: number;
|
||||
requestCount?: number;
|
||||
errorResponseMessage?: string;
|
||||
requestMethods?: (string[]);
|
||||
includeMasterKey?: boolean;
|
||||
includeInternalRequests?: boolean;
|
||||
redisUrl?: string;
|
||||
zone?: string;
|
||||
}
|
||||
export interface SecurityOptions {
|
||||
enableCheck?: boolean;
|
||||
enableCheckLog?: boolean;
|
||||
checkGroups?: (CheckGroup[]);
|
||||
}
|
||||
export interface PagesOptions {
|
||||
enableRouter?: boolean;
|
||||
enableLocalization?: boolean;
|
||||
localizationJsonPath?: string;
|
||||
localizationFallbackLocale?: string;
|
||||
placeholders?: any;
|
||||
forceRedirect?: boolean;
|
||||
pagesPath?: string;
|
||||
pagesEndpoint?: string;
|
||||
customUrls?: PagesCustomUrlsOptions;
|
||||
customRoutes?: (PagesRoute[]);
|
||||
}
|
||||
export interface PagesRoute {
|
||||
path: string;
|
||||
method: string;
|
||||
handler: () => void;
|
||||
}
|
||||
export interface PagesCustomUrlsOptions {
|
||||
passwordReset?: string;
|
||||
passwordResetLinkInvalid?: string;
|
||||
passwordResetSuccess?: string;
|
||||
emailVerificationSuccess?: string;
|
||||
emailVerificationSendFail?: string;
|
||||
emailVerificationSendSuccess?: string;
|
||||
emailVerificationLinkInvalid?: string;
|
||||
emailVerificationLinkExpired?: string;
|
||||
}
|
||||
export interface CustomPagesOptions {
|
||||
invalidLink?: string;
|
||||
linkSendFail?: string;
|
||||
choosePassword?: string;
|
||||
linkSendSuccess?: string;
|
||||
verifyEmailSuccess?: string;
|
||||
passwordResetSuccess?: string;
|
||||
invalidVerificationLink?: string;
|
||||
expiredVerificationLink?: string;
|
||||
invalidPasswordResetLink?: string;
|
||||
parseFrameURL?: string;
|
||||
}
|
||||
export interface LiveQueryOptions {
|
||||
classNames?: (string[]);
|
||||
redisOptions?: any;
|
||||
redisURL?: string;
|
||||
pubSubAdapter?: Adapter<PubSubAdapter>;
|
||||
wssAdapter?: Adapter<WSSAdapter>;
|
||||
}
|
||||
export interface LiveQueryServerOptions {
|
||||
appId?: string;
|
||||
masterKey?: string;
|
||||
serverURL?: string;
|
||||
keyPairs?: any;
|
||||
websocketTimeout?: number;
|
||||
cacheTimeout?: number;
|
||||
logLevel?: string;
|
||||
port?: number;
|
||||
redisOptions?: any;
|
||||
redisURL?: string;
|
||||
pubSubAdapter?: Adapter<PubSubAdapter>;
|
||||
wssAdapter?: Adapter<WSSAdapter>;
|
||||
}
|
||||
export interface IdempotencyOptions {
|
||||
paths?: (string[]);
|
||||
ttl?: number;
|
||||
}
|
||||
export interface AccountLockoutOptions {
|
||||
duration?: number;
|
||||
threshold?: number;
|
||||
unlockOnPasswordReset?: boolean;
|
||||
}
|
||||
export interface PasswordPolicyOptions {
|
||||
validatorPattern?: string;
|
||||
validatorCallback?: () => void;
|
||||
validationError?: string;
|
||||
doNotAllowUsername?: boolean;
|
||||
maxPasswordAge?: number;
|
||||
maxPasswordHistory?: number;
|
||||
resetTokenValidityDuration?: number;
|
||||
resetTokenReuseIfValid?: boolean;
|
||||
resetPasswordSuccessOnInvalidEmail?: boolean;
|
||||
}
|
||||
export interface FileUploadOptions {
|
||||
fileExtensions?: (string[]);
|
||||
enableForAnonymousUser?: boolean;
|
||||
enableForAuthenticatedUser?: boolean;
|
||||
enableForPublic?: boolean;
|
||||
}
|
||||
export interface DatabaseOptions {
|
||||
enableSchemaHooks?: boolean;
|
||||
schemaCacheTtl?: number;
|
||||
retryWrites?: boolean;
|
||||
maxTimeMS?: number;
|
||||
maxStalenessSeconds?: number;
|
||||
minPoolSize?: number;
|
||||
maxPoolSize?: number;
|
||||
connectTimeoutMS?: number;
|
||||
socketTimeoutMS?: number;
|
||||
autoSelectFamily?: boolean;
|
||||
autoSelectFamilyAttemptTimeout?: number;
|
||||
}
|
||||
export interface AuthAdapter {
|
||||
enabled?: boolean;
|
||||
}
|
||||
export interface LogLevels {
|
||||
triggerAfter?: string;
|
||||
triggerBeforeSuccess?: string;
|
||||
triggerBeforeError?: string;
|
||||
cloudFunctionSuccess?: string;
|
||||
cloudFunctionError?: string;
|
||||
}
|
||||
export {};
|
||||
60
types/ParseServer.d.ts
vendored
Normal file
60
types/ParseServer.d.ts
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
import { ParseServerOptions, LiveQueryServerOptions } from './Options';
|
||||
import { ParseLiveQueryServer } from './LiveQuery/ParseLiveQueryServer';
|
||||
declare class ParseServer {
|
||||
_app: any;
|
||||
config: any;
|
||||
server: any;
|
||||
expressApp: any;
|
||||
liveQueryServer: any;
|
||||
/**
|
||||
* @constructor
|
||||
* @param {ParseServerOptions} options the parse server initialization options
|
||||
*/
|
||||
constructor(options: ParseServerOptions);
|
||||
/**
|
||||
* Starts Parse Server as an express app; this promise resolves when Parse Server is ready to accept requests.
|
||||
*/
|
||||
start(): Promise<this>;
|
||||
get app(): any;
|
||||
/**
|
||||
* Stops the parse server, cancels any ongoing requests and closes all connections.
|
||||
*
|
||||
* Currently, express doesn't shut down immediately after receiving SIGINT/SIGTERM
|
||||
* if it has client connections that haven't timed out.
|
||||
* (This is a known issue with node - https://github.com/nodejs/node/issues/2642)
|
||||
*
|
||||
* @returns {Promise<void>} a promise that resolves when the server is stopped
|
||||
*/
|
||||
handleShutdown(): Promise<void>;
|
||||
/**
|
||||
* @static
|
||||
* Create an express app for the parse server
|
||||
* @param {Object} options let you specify the maxUploadSize when creating the express app */
|
||||
static app(options: any): any;
|
||||
static promiseRouter({ appId }: {
|
||||
appId: any;
|
||||
}): any;
|
||||
/**
|
||||
* starts the parse server's express app
|
||||
* @param {ParseServerOptions} options to use to start the server
|
||||
* @returns {ParseServer} the parse server instance
|
||||
*/
|
||||
startApp(options: ParseServerOptions): Promise<this>;
|
||||
/**
|
||||
* Creates a new ParseServer and starts it.
|
||||
* @param {ParseServerOptions} options used to start the server
|
||||
* @returns {ParseServer} the parse server instance
|
||||
*/
|
||||
static startApp(options: ParseServerOptions): Promise<ParseServer>;
|
||||
/**
|
||||
* Helper method to create a liveQuery server
|
||||
* @static
|
||||
* @param {Server} httpServer an optional http server to pass
|
||||
* @param {LiveQueryServerOptions} config options for the liveQueryServer
|
||||
* @param {ParseServerOptions} options options for the ParseServer
|
||||
* @returns {Promise<ParseLiveQueryServer>} the live query server instance
|
||||
*/
|
||||
static createLiveQueryServer(httpServer: any, config: LiveQueryServerOptions, options: ParseServerOptions): Promise<ParseLiveQueryServer>;
|
||||
static verifyServerUrl(): any;
|
||||
}
|
||||
export default ParseServer;
|
||||
30
types/eslint.config.mjs
Normal file
30
types/eslint.config.mjs
Normal file
@@ -0,0 +1,30 @@
|
||||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import expectType from 'eslint-plugin-expect-type/configs/recommended';
|
||||
|
||||
export default tseslint.config({
|
||||
files: ['**/*.js', '**/*.ts'],
|
||||
extends: [
|
||||
expectType,
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
...tseslint.configs.recommendedTypeChecked,
|
||||
],
|
||||
plugins: {
|
||||
'@typescript-eslint': tseslint.plugin,
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-expressions': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unsafe-return": "off",
|
||||
},
|
||||
languageOptions: {
|
||||
parser: tseslint.parser,
|
||||
parserOptions: {
|
||||
projectService: true,
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
});
|
||||
21
types/index.d.ts
vendored
21
types/index.d.ts
vendored
@@ -0,0 +1,21 @@
|
||||
import ParseServer from './ParseServer';
|
||||
import FileSystemAdapter from '@parse/fs-files-adapter';
|
||||
import InMemoryCacheAdapter from './Adapters/Cache/InMemoryCacheAdapter';
|
||||
import NullCacheAdapter from './Adapters/Cache/NullCacheAdapter';
|
||||
import RedisCacheAdapter from './Adapters/Cache/RedisCacheAdapter';
|
||||
import LRUCacheAdapter from './Adapters/Cache/LRUCache.js';
|
||||
import * as TestUtils from './TestUtils';
|
||||
import * as SchemaMigrations from './SchemaMigrations/Migrations';
|
||||
import AuthAdapter from './Adapters/Auth/AuthAdapter';
|
||||
import { PushWorker } from './Push/PushWorker';
|
||||
import { ParseServerOptions } from './Options';
|
||||
import { ParseGraphQLServer } from './GraphQL/ParseGraphQLServer';
|
||||
declare const _ParseServer: {
|
||||
(options: ParseServerOptions): ParseServer;
|
||||
createLiveQueryServer: typeof ParseServer.createLiveQueryServer;
|
||||
startApp: typeof ParseServer.startApp;
|
||||
};
|
||||
declare const S3Adapter: any;
|
||||
declare const GCSAdapter: any;
|
||||
export default ParseServer;
|
||||
export { S3Adapter, GCSAdapter, FileSystemAdapter, InMemoryCacheAdapter, NullCacheAdapter, RedisCacheAdapter, LRUCacheAdapter, TestUtils, PushWorker, ParseGraphQLServer, _ParseServer as ParseServer, SchemaMigrations, AuthAdapter, };
|
||||
|
||||
44
types/tests.ts
Normal file
44
types/tests.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import ParseServer, { FileSystemAdapter } from 'parse-server';
|
||||
|
||||
async function server() {
|
||||
// $ExpectType ParseServer
|
||||
const parseServer = await ParseServer.startApp({});
|
||||
|
||||
// $ExpectType void
|
||||
await parseServer.handleShutdown();
|
||||
|
||||
// $ExpectType any
|
||||
parseServer.app;
|
||||
|
||||
// $ExpectType any
|
||||
ParseServer.app({});
|
||||
|
||||
// $ExpectType any
|
||||
ParseServer.promiseRouter({ appId: 'appId' });
|
||||
|
||||
// $ExpectType ParseLiveQueryServer
|
||||
await ParseServer.createLiveQueryServer({}, {}, {});
|
||||
|
||||
// $ExpectType any
|
||||
ParseServer.verifyServerUrl();
|
||||
|
||||
// $ExpectError
|
||||
await ParseServer.startApp();
|
||||
|
||||
// $ExpectError
|
||||
ParseServer.promiseRouter();
|
||||
|
||||
// $ExpectError
|
||||
await ParseServer.createLiveQueryServer();
|
||||
|
||||
// $ExpectType ParseServer
|
||||
const parseServer2 = new ParseServer({});
|
||||
|
||||
// $ExpectType ParseServer
|
||||
await parseServer2.start();
|
||||
}
|
||||
|
||||
function exports() {
|
||||
// $ExpectType any
|
||||
FileSystemAdapter;
|
||||
}
|
||||
@@ -13,6 +13,12 @@
|
||||
// If the library is an external module (uses `export`), this allows your test file to import "mylib" instead of "./index".
|
||||
// If the library is global (cannot be imported via `import` or `require`), leave this out.
|
||||
"baseUrl": ".",
|
||||
"paths": { "parse": ["."] }
|
||||
}
|
||||
"paths": {
|
||||
"parse-server": ["."],
|
||||
"@parse/fs-files-adapter": ["./@types/@parse/fs-files-adapter"],
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"tests.ts"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user