rxdb-server 17.1.0 → 17.2.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +4 -0
  3. package/dist/cjs/plugins/adapter-express/index.js +10 -1
  4. package/dist/cjs/plugins/adapter-express/index.js.map +1 -1
  5. package/dist/cjs/plugins/client-rest/index.js +12 -7
  6. package/dist/cjs/plugins/client-rest/index.js.map +1 -1
  7. package/dist/cjs/plugins/replication-server/index.js +1 -1
  8. package/dist/cjs/plugins/replication-server/index.js.map +1 -1
  9. package/dist/cjs/plugins/server/endpoint-replication.js +9 -2
  10. package/dist/cjs/plugins/server/endpoint-replication.js.map +1 -1
  11. package/dist/cjs/plugins/server/endpoint-rest.js +58 -9
  12. package/dist/cjs/plugins/server/endpoint-rest.js.map +1 -1
  13. package/dist/cjs/plugins/server/helper.js +44 -7
  14. package/dist/cjs/plugins/server/helper.js.map +1 -1
  15. package/dist/cjs/plugins/server/index.js +12 -0
  16. package/dist/cjs/plugins/server/index.js.map +1 -1
  17. package/dist/cjs/plugins/server/performance-test.js +276 -0
  18. package/dist/cjs/plugins/server/performance-test.js.map +1 -0
  19. package/dist/esm/plugins/adapter-express/index.js +10 -1
  20. package/dist/esm/plugins/adapter-express/index.js.map +1 -1
  21. package/dist/esm/plugins/client-rest/index.js +12 -7
  22. package/dist/esm/plugins/client-rest/index.js.map +1 -1
  23. package/dist/esm/plugins/replication-server/index.js +1 -1
  24. package/dist/esm/plugins/replication-server/index.js.map +1 -1
  25. package/dist/esm/plugins/server/endpoint-replication.js +10 -3
  26. package/dist/esm/plugins/server/endpoint-replication.js.map +1 -1
  27. package/dist/esm/plugins/server/endpoint-rest.js +59 -10
  28. package/dist/esm/plugins/server/endpoint-rest.js.map +1 -1
  29. package/dist/esm/plugins/server/helper.js +43 -7
  30. package/dist/esm/plugins/server/helper.js.map +1 -1
  31. package/dist/esm/plugins/server/index.js +1 -0
  32. package/dist/esm/plugins/server/index.js.map +1 -1
  33. package/dist/esm/plugins/server/performance-test.js +271 -0
  34. package/dist/esm/plugins/server/performance-test.js.map +1 -0
  35. package/dist/types/plugins/server/helper.d.ts +11 -1
  36. package/dist/types/plugins/server/index.d.ts +1 -0
  37. package/dist/types/plugins/server/performance-test.d.ts +43 -0
  38. package/orga/changelog/README.md +18 -0
  39. package/orga/changelog/fix-cors-wildcard-with-credentials.md +1 -0
  40. package/orga/changelog/fix-false-conflicts-missing-server-only-field.md +1 -0
  41. package/orga/changelog/fix-replication-pull-url-encode-checkpoint.md +1 -0
  42. package/orga/changelog/fix-replication-push-new-doc-server-only-fields-strip.md +1 -0
  43. package/orga/changelog/fix-replication-push-new-doc-server-only-fields.md +1 -0
  44. package/orga/changelog/fix-replication-push-server-only-fields-insert.md +1 -0
  45. package/orga/changelog/fix-rest-client-missing-await.md +1 -0
  46. package/orga/changelog/fix-rest-client-observe-query-url-encode-base64.md +1 -0
  47. package/orga/changelog/fix-rest-delete-server-only-fields-validator.md +1 -0
  48. package/orga/changelog/fix-rest-query-observe-regex-rejection.md +1 -0
  49. package/orga/changelog/fix-rest-set-change-validator-new-doc.md +1 -0
  50. package/orga/changelog/fix-rest-set-document-ownership-check.md +1 -0
  51. package/orga/changelog/fix-rest-set-server-only-fields-insert.md +1 -0
  52. package/orga/changelog/fix-rest-set-server-only-fields-overwrite.md +1 -0
  53. package/package.json +7 -40
@@ -1 +1 @@
1
- {"version":3,"file":"endpoint-rest.js","names":["normalizeMangoQuery","filter","mergeMap","ensureNotFalsy","docContainsServerOnlyFields","doesContainRegexQuerySelector","getAuthDataByRequest","getDocAllowedMatcher","removeServerOnlyFieldsMonad","setCors","REST_PATHS","RxServerRestEndpoint","server","name","collection","queryModifier","changeValidator","serverOnlyFields","cors","_this","type","adapter","join","blockPreviousReplicationVersionPathsRest","schema","version","urlPath","primaryPath","authData","query","selector","Error","change","assumedMasterState","newDocumentState","removeServerOnlyFields","post","serverApp","req","res","getRequestBody","useQuery","jsonSchema","err","closeConnection","rxQuery","find","result","exec","setResponseHeader","endResponseJson","documents","map","d","toJSON","get","setSSEHeaders","JSON","parse","atob","getRequestQuery","subscription","$","pipe","resultData","doc","authHandler","getRequestHeaders","f","subscribe","responseWrite","stringify","onRequestClose","unsubscribe","endResponse","ids","findByIds","resultMap","resultValues","Array","from","values","docMatcher","useDocs","docDataMatcherWrite","docsData","docData","allowed","onWriteError","rxdb","code","push","length","promises","docs","useDocsData","slice","_loop","_docData","id","insert","catch","isAllowed","v","patch","_ret","Promise","all","useIds","docsMap","_loop2","isAllowedDoc","isAllowedChange","remove","_ret2","path","currentVersion","_loop3","forEach","subPath"],"sources":["../../../../src/plugins/server/endpoint-rest.ts"],"sourcesContent":["import {\n FilledMangoQuery,\n RxCollection,\n RxError,\n normalizeMangoQuery\n} from 'rxdb/plugins/core';\nimport type { RxServer } from './rx-server.ts';\nimport type {\n RxServerChangeValidator,\n RxServerEndpoint,\n RxServerQueryModifier\n} from './types.ts';\nimport { filter, mergeMap } from 'rxjs';\nimport {\n ensureNotFalsy,\n getFromMapOrThrow\n} from 'rxdb/plugins/utils';\n\nimport {\n docContainsServerOnlyFields,\n doesContainRegexQuerySelector,\n getAuthDataByRequest,\n getDocAllowedMatcher,\n removeServerOnlyFieldsMonad,\n setCors\n} from './helper.ts';\n\n\nexport const REST_PATHS = [\n 'query',\n 'query/observe',\n 'get',\n 'set',\n 'delete',\n\n // TODO\n /*\n 'attachments/add',\n 'attachments/delete',\n 'events'\n */\n] as const;\n\n\nexport class RxServerRestEndpoint<ServerAppType, AuthType, RxDocType> implements RxServerEndpoint<AuthType, RxDocType> {\n readonly type = 'rest';\n readonly urlPath: string;\n readonly changeValidator: RxServerChangeValidator<AuthType, RxDocType>;\n readonly queryModifier: RxServerQueryModifier<AuthType, RxDocType>;\n constructor(\n public readonly server: RxServer<ServerAppType, AuthType>,\n public readonly name: string,\n public readonly collection: RxCollection<RxDocType>,\n queryModifier: RxServerQueryModifier<AuthType, RxDocType>,\n changeValidator: RxServerChangeValidator<AuthType, RxDocType>,\n public readonly serverOnlyFields: string[],\n public readonly cors?: string\n ) {\n const adapter = server.adapter;\n setCors(this.server, [this.name].join('/'), cors);\n blockPreviousReplicationVersionPathsRest(this.server, [this.name].join('/'), collection.schema.version);\n\n this.urlPath = [this.name, collection.schema.version].join('/');\n const primaryPath = this.collection.schema.primaryPath;\n\n this.queryModifier = (authData, query) => {\n if (doesContainRegexQuerySelector(query.selector)) {\n throw new Error('$regex queries not allowed because of DOS-attacks');\n }\n return queryModifier(authData, query);\n }\n this.changeValidator = (authData, change) => {\n if (\n (\n change.assumedMasterState &&\n docContainsServerOnlyFields(serverOnlyFields, change.assumedMasterState)\n ) ||\n docContainsServerOnlyFields(serverOnlyFields, change.newDocumentState)\n ) {\n return false;\n }\n return changeValidator(authData, change);\n }\n const removeServerOnlyFields = removeServerOnlyFieldsMonad(this.serverOnlyFields);\n\n this.server.adapter.post(this.server.serverApp, '/' + this.urlPath + '/query', async (req, res) => {\n ensureNotFalsy(adapter.getRequestBody(req), 'req body is empty');\n const authData = await getAuthDataByRequest(this.server, req, res);\n if (!authData) { return; }\n\n let useQuery: FilledMangoQuery<RxDocType>\n try {\n useQuery = this.queryModifier(\n ensureNotFalsy(authData),\n normalizeMangoQuery(\n this.collection.schema.jsonSchema,\n adapter.getRequestBody(req)\n )\n );\n } catch (err) {\n adapter.closeConnection(res, 400, 'Bad Request');\n return;\n }\n const rxQuery = this.collection.find(useQuery as any);\n const result = await rxQuery.exec();\n adapter.setResponseHeader(res, 'Content-Type', 'application/json');\n adapter.endResponseJson(res, {\n documents: result.map(d => removeServerOnlyFields(d.toJSON()))\n });\n });\n\n /**\n * It is not possible to send data with server send events,\n * so we send the query as query parameter in base64\n * like ?query=e3NlbGVjdG9yOiB7fX0=\n */\n this.server.adapter.get(this.server.serverApp, '/' + this.urlPath + '/query/observe', async (req, res) => {\n let authData = await getAuthDataByRequest(this.server, req, res);\n if (!authData) { return; }\n\n adapter.setSSEHeaders(res);\n\n const useQuery: FilledMangoQuery<RxDocType> = this.queryModifier(\n ensureNotFalsy(authData),\n normalizeMangoQuery(\n this.collection.schema.jsonSchema,\n JSON.parse(atob(adapter.getRequestQuery(req).query as string))\n )\n );\n\n const rxQuery = this.collection.find(useQuery as any);\n const subscription = rxQuery.$.pipe(\n mergeMap(async (result) => {\n const resultData = result.map(doc => removeServerOnlyFields(doc.toJSON()));\n\n /**\n * The auth-data might be expired\n * so we re-run the auth parsing each time\n * before emitting the new results.\n */\n try {\n authData = await server.authHandler(adapter.getRequestHeaders(req)) as any;\n } catch (err) {\n adapter.closeConnection(res, 401, 'Unauthorized');\n return null;\n }\n\n return resultData;\n }),\n filter(f => f !== null)\n ).subscribe(resultData => {\n adapter.responseWrite(res, 'data: ' + JSON.stringify(resultData) + '\\n\\n');\n });\n\n /**\n * @link https://youtu.be/0PcMuYGJPzM?si=AxkczxcMaUwhh8k9&t=363\n */\n adapter.onRequestClose(req, () => {\n subscription.unsubscribe();\n adapter.endResponse(res);\n });\n });\n\n\n this.server.adapter.post(this.server.serverApp, '/' + this.urlPath + '/get', async (req, res) => {\n const authData = await getAuthDataByRequest(this.server, req, res);\n if (!authData) { return; }\n\n const ids: string[] = adapter.getRequestBody(req);\n\n const rxQuery = this.collection.findByIds(ids);\n const resultMap = await rxQuery.exec();\n const resultValues = Array.from(resultMap.values());\n const docMatcher = getDocAllowedMatcher(this, ensureNotFalsy(authData) as any);\n let useDocs = resultValues.map(d => d.toJSON());\n useDocs = useDocs.filter(d => docMatcher(d as any));\n useDocs = useDocs.map(d => removeServerOnlyFields(d))\n\n adapter.setResponseHeader(res, 'Content-Type', 'application/json');\n adapter.endResponseJson(res, {\n documents: useDocs\n });\n });\n\n this.server.adapter.post(this.server.serverApp, '/' + this.urlPath + '/set', async (req, res) => {\n const authData = await getAuthDataByRequest(this.server, req, res);\n if (!authData) { return; }\n\n const docDataMatcherWrite = getDocAllowedMatcher(this, ensureNotFalsy(authData) as any);\n\n let docsData: RxDocType[] = adapter.getRequestBody(req);\n\n for (const docData of docsData) {\n const allowed = docDataMatcherWrite(docData as any);\n if (!allowed) {\n adapter.closeConnection(res, 403, 'Forbidden');\n return;\n }\n }\n\n function onWriteError(err: RxError, docData: RxDocType) {\n if (err.rxdb && err.code === 'CONFLICT') {\n // just retry on conflicts\n docsData.push(docData);\n } else {\n adapter.closeConnection(res, 500, 'Internal Server Error');\n throw err;\n }\n }\n\n while (docsData.length > 0) {\n const promises: Promise<any>[] = [];\n const docs = await collection.findByIds(docsData.map(d => (d as any)[primaryPath])).exec();\n let useDocsData = docsData.slice();\n docsData = [];\n for (const docData of useDocsData) {\n const id = (docData as any)[primaryPath];\n const doc = docs.get(id);\n if (!doc) {\n promises.push(this.collection.insert(docData).catch(err => onWriteError(err, docData)));\n } else {\n const isAllowed = this.changeValidator(authData, {\n newDocumentState: removeServerOnlyFields(docData as any),\n assumedMasterState: removeServerOnlyFields(doc.toJSON(true))\n });\n if (!isAllowed) {\n adapter.closeConnection(res, 403, 'Forbidden');\n return;\n }\n promises.push(doc.patch(docData).catch(err => onWriteError(err, docData)));\n }\n }\n await Promise.all(promises);\n }\n\n adapter.setResponseHeader(res, 'Content-Type', 'application/json')\n adapter.endResponseJson(res, {});\n });\n\n this.server.adapter.post(this.server.serverApp, '/' + this.urlPath + '/delete', async (req, res) => {\n const authData = await getAuthDataByRequest(this.server, req, res);\n if (!authData) { return; }\n\n const docDataMatcherWrite = getDocAllowedMatcher(this, ensureNotFalsy(authData));\n\n let ids: string[] = adapter.getRequestBody(req);\n while (ids.length > 0) {\n const useIds = ids.slice(0);\n ids = [];\n const promises: Promise<any>[] = [];\n const docsMap = await this.collection.findByIds(useIds).exec();\n for (const id of useIds) {\n const doc = docsMap.get(id);\n if (doc) {\n const isAllowedDoc = docDataMatcherWrite(doc.toJSON(true) as any);\n if (!isAllowedDoc) {\n adapter.closeConnection(res, 403, 'Forbidden');\n return;\n }\n\n const isAllowedChange = this.changeValidator(authData, {\n newDocumentState: doc.toJSON(true) as any,\n assumedMasterState: doc.toJSON(true) as any\n });\n if (!isAllowedChange) {\n adapter.closeConnection(res, 403, 'Forbidden');\n return;\n }\n\n promises.push(doc.remove().catch((err: RxError) => {\n if (err.rxdb && err.code === 'CONFLICT') {\n // just retry on conflicts\n ids.push(id);\n } else {\n adapter.closeConnection(res, 500, 'Internal Server Error');\n throw err;\n }\n }));\n }\n }\n await Promise.all(promises);\n }\n adapter.setResponseHeader(res, 'Content-Type', 'application/json');\n adapter.endResponseJson(res, {});\n });\n }\n}\n\n\n/**\n * \"block\" the previous version urls and send a 426 on them so that\n * the clients know they must update.\n */\nexport function blockPreviousReplicationVersionPathsRest(\n server: RxServer<any, any>,\n path: string,\n currentVersion: number\n\n) {\n let v = 0;\n while (v < currentVersion) {\n const version = v;\n /**\n * Some adapters do not allow regex or handle them property (like Koa),\n * so to make it easier, use the hard-coded array of path parts.\n */\n [\n '',\n 'query',\n 'query/observe',\n 'get',\n 'set',\n 'delete'\n ].forEach(subPath => {\n server.adapter.all(server.serverApp, '/' + path + '/' + version + '/' + subPath, (req, res) => {\n server.adapter.closeConnection(res, 426, 'Outdated version ' + version + ' (newest is ' + currentVersion + ')');\n });\n });\n v++;\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"endpoint-rest.js","names":["normalizeMangoQuery","filter","mergeMap","ensureNotFalsy","docContainsServerOnlyFields","doesContainRegexQuerySelector","getAuthDataByRequest","getDocAllowedMatcher","mergeServerDocumentFieldsMonad","removeServerOnlyFieldsMonad","setCors","stripServerOnlyFieldsMonad","REST_PATHS","RxServerRestEndpoint","server","name","collection","queryModifier","changeValidator","serverOnlyFields","cors","_this","type","adapter","join","blockPreviousReplicationVersionPathsRest","schema","version","urlPath","primaryPath","authData","query","selector","Error","change","assumedMasterState","newDocumentState","removeServerOnlyFields","mergeServerDocumentFields","stripServerOnlyFields","post","serverApp","req","res","getRequestBody","useQuery","jsonSchema","err","closeConnection","rxQuery","find","result","exec","setResponseHeader","endResponseJson","documents","map","d","toJSON","get","JSON","parse","atob","getRequestQuery","setSSEHeaders","subscription","$","pipe","resultData","doc","authHandler","getRequestHeaders","f","subscribe","responseWrite","stringify","onRequestClose","unsubscribe","endResponse","ids","findByIds","resultMap","resultValues","Array","from","values","docMatcher","useDocs","docDataMatcherWrite","docsData","docData","allowed","onWriteError","rxdb","code","push","length","promises","docs","useDocsData","slice","_loop","id","isAllowed","undefined","v","mergedDocData","insert","catch","isExistingDocAllowed","patch","_ret","Promise","all","useIds","docsMap","_loop2","isAllowedDoc","isAllowedChange","remove","_ret2","path","currentVersion","_loop3","forEach","subPath"],"sources":["../../../../src/plugins/server/endpoint-rest.ts"],"sourcesContent":["import {\n FilledMangoQuery,\n RxCollection,\n RxError,\n normalizeMangoQuery\n} from 'rxdb/plugins/core';\nimport type { RxServer } from './rx-server.ts';\nimport type {\n RxServerChangeValidator,\n RxServerEndpoint,\n RxServerQueryModifier\n} from './types.ts';\nimport { filter, mergeMap } from 'rxjs';\nimport {\n ensureNotFalsy,\n getFromMapOrThrow\n} from 'rxdb/plugins/utils';\n\nimport {\n docContainsServerOnlyFields,\n doesContainRegexQuerySelector,\n getAuthDataByRequest,\n getDocAllowedMatcher,\n mergeServerDocumentFieldsMonad,\n removeServerOnlyFieldsMonad,\n setCors,\n stripServerOnlyFieldsMonad\n} from './helper.ts';\n\n\nexport const REST_PATHS = [\n 'query',\n 'query/observe',\n 'get',\n 'set',\n 'delete',\n\n // TODO\n /*\n 'attachments/add',\n 'attachments/delete',\n 'events'\n */\n] as const;\n\n\nexport class RxServerRestEndpoint<ServerAppType, AuthType, RxDocType> implements RxServerEndpoint<AuthType, RxDocType> {\n readonly type = 'rest';\n readonly urlPath: string;\n readonly changeValidator: RxServerChangeValidator<AuthType, RxDocType>;\n readonly queryModifier: RxServerQueryModifier<AuthType, RxDocType>;\n constructor(\n public readonly server: RxServer<ServerAppType, AuthType>,\n public readonly name: string,\n public readonly collection: RxCollection<RxDocType>,\n queryModifier: RxServerQueryModifier<AuthType, RxDocType>,\n changeValidator: RxServerChangeValidator<AuthType, RxDocType>,\n public readonly serverOnlyFields: string[],\n public readonly cors?: string\n ) {\n const adapter = server.adapter;\n setCors(this.server, [this.name].join('/'), cors);\n blockPreviousReplicationVersionPathsRest(this.server, [this.name].join('/'), collection.schema.version);\n\n this.urlPath = [this.name, collection.schema.version].join('/');\n const primaryPath = this.collection.schema.primaryPath;\n\n this.queryModifier = (authData, query) => {\n if (doesContainRegexQuerySelector(query.selector)) {\n throw new Error('$regex queries not allowed because of DOS-attacks');\n }\n return queryModifier(authData, query);\n }\n this.changeValidator = (authData, change) => {\n if (\n (\n change.assumedMasterState &&\n docContainsServerOnlyFields(serverOnlyFields, change.assumedMasterState)\n ) ||\n docContainsServerOnlyFields(serverOnlyFields, change.newDocumentState)\n ) {\n return false;\n }\n return changeValidator(authData, change);\n }\n const removeServerOnlyFields = removeServerOnlyFieldsMonad(this.serverOnlyFields);\n const mergeServerDocumentFields = mergeServerDocumentFieldsMonad<RxDocType>(this.serverOnlyFields);\n const stripServerOnlyFields = stripServerOnlyFieldsMonad<RxDocType>(this.serverOnlyFields);\n\n this.server.adapter.post(this.server.serverApp, '/' + this.urlPath + '/query', async (req, res) => {\n ensureNotFalsy(adapter.getRequestBody(req), 'req body is empty');\n const authData = await getAuthDataByRequest(this.server, req, res);\n if (!authData) { return; }\n\n let useQuery: FilledMangoQuery<RxDocType>\n try {\n useQuery = this.queryModifier(\n ensureNotFalsy(authData),\n normalizeMangoQuery(\n this.collection.schema.jsonSchema,\n adapter.getRequestBody(req)\n )\n );\n } catch (err) {\n adapter.closeConnection(res, 400, 'Bad Request');\n return;\n }\n const rxQuery = this.collection.find(useQuery as any);\n const result = await rxQuery.exec();\n adapter.setResponseHeader(res, 'Content-Type', 'application/json');\n adapter.endResponseJson(res, {\n documents: result.map(d => removeServerOnlyFields(d.toJSON()))\n });\n });\n\n /**\n * It is not possible to send data with server send events,\n * so we send the query as query parameter in base64\n * like ?query=e3NlbGVjdG9yOiB7fX0=\n */\n this.server.adapter.get(this.server.serverApp, '/' + this.urlPath + '/query/observe', async (req, res) => {\n let authData = await getAuthDataByRequest(this.server, req, res);\n if (!authData) { return; }\n\n // Run the queryModifier BEFORE setSSEHeaders so that a bad\n // request (e.g. a $regex query that the modifier wrapper\n // rejects to prevent DOS attacks) can still be answered\n // with a proper 400 response, mirroring the /query endpoint.\n let useQuery: FilledMangoQuery<RxDocType>;\n try {\n useQuery = this.queryModifier(\n ensureNotFalsy(authData),\n normalizeMangoQuery(\n this.collection.schema.jsonSchema,\n JSON.parse(atob(adapter.getRequestQuery(req).query as string))\n )\n );\n } catch (err) {\n adapter.closeConnection(res, 400, 'Bad Request');\n return;\n }\n\n adapter.setSSEHeaders(res);\n\n const rxQuery = this.collection.find(useQuery as any);\n const subscription = rxQuery.$.pipe(\n mergeMap(async (result) => {\n const resultData = result.map(doc => removeServerOnlyFields(doc.toJSON()));\n\n /**\n * The auth-data might be expired\n * so we re-run the auth parsing each time\n * before emitting the new results.\n */\n try {\n authData = await server.authHandler(adapter.getRequestHeaders(req)) as any;\n } catch (err) {\n adapter.closeConnection(res, 401, 'Unauthorized');\n return null;\n }\n\n return resultData;\n }),\n filter(f => f !== null)\n ).subscribe(resultData => {\n adapter.responseWrite(res, 'data: ' + JSON.stringify(resultData) + '\\n\\n');\n });\n\n /**\n * @link https://youtu.be/0PcMuYGJPzM?si=AxkczxcMaUwhh8k9&t=363\n */\n adapter.onRequestClose(req, () => {\n subscription.unsubscribe();\n adapter.endResponse(res);\n });\n });\n\n\n this.server.adapter.post(this.server.serverApp, '/' + this.urlPath + '/get', async (req, res) => {\n const authData = await getAuthDataByRequest(this.server, req, res);\n if (!authData) { return; }\n\n const ids: string[] = adapter.getRequestBody(req);\n\n const rxQuery = this.collection.findByIds(ids);\n const resultMap = await rxQuery.exec();\n const resultValues = Array.from(resultMap.values());\n const docMatcher = getDocAllowedMatcher(this, ensureNotFalsy(authData) as any);\n let useDocs = resultValues.map(d => d.toJSON());\n useDocs = useDocs.filter(d => docMatcher(d as any));\n useDocs = useDocs.map(d => removeServerOnlyFields(d))\n\n adapter.setResponseHeader(res, 'Content-Type', 'application/json');\n adapter.endResponseJson(res, {\n documents: useDocs\n });\n });\n\n this.server.adapter.post(this.server.serverApp, '/' + this.urlPath + '/set', async (req, res) => {\n const authData = await getAuthDataByRequest(this.server, req, res);\n if (!authData) { return; }\n\n const docDataMatcherWrite = getDocAllowedMatcher(this, ensureNotFalsy(authData) as any);\n\n let docsData: RxDocType[] = adapter.getRequestBody(req);\n\n for (const docData of docsData) {\n const allowed = docDataMatcherWrite(docData as any);\n if (!allowed) {\n adapter.closeConnection(res, 403, 'Forbidden');\n return;\n }\n }\n\n function onWriteError(err: RxError, docData: RxDocType) {\n if (err.rxdb && err.code === 'CONFLICT') {\n // just retry on conflicts\n docsData.push(docData);\n } else {\n adapter.closeConnection(res, 500, 'Internal Server Error');\n throw err;\n }\n }\n\n while (docsData.length > 0) {\n const promises: Promise<any>[] = [];\n const docs = await collection.findByIds(docsData.map(d => (d as any)[primaryPath])).exec();\n let useDocsData = docsData.slice();\n docsData = [];\n for (const docData of useDocsData) {\n const id = (docData as any)[primaryPath];\n const doc = docs.get(id);\n if (!doc) {\n // Run the changeValidator for new document inserts as\n // well. Previously the validator was only invoked for\n // updates, so a changeValidator that rejected writes\n // had no effect on inserts via /set, which is\n // inconsistent with the replication endpoint and the\n // documented contract that the validator gates all\n // changes.\n const isAllowed = this.changeValidator(authData, {\n newDocumentState: removeServerOnlyFields(docData as any),\n assumedMasterState: undefined\n });\n if (!isAllowed) {\n adapter.closeConnection(res, 403, 'Forbidden');\n return;\n }\n // Strip server-only fields from the client doc before\n // inserting. Without this, a client could populate\n // server-only (\"readonly\") fields when creating a new\n // document via /set, which contradicts the documented\n // behavior that clients cannot do writes where one of\n // the serverOnlyFields is set.\n const mergedDocData = stripServerOnlyFields(\n mergeServerDocumentFields(docData, undefined) as RxDocType\n );\n promises.push(this.collection.insert(mergedDocData).catch(err => onWriteError(err, mergedDocData)));\n } else {\n // The user must also be allowed to access the existing document.\n // Without this check, a client could overwrite arbitrary\n // documents by sending a write whose new state matches the\n // queryModifier while targeting a foreign document's primary.\n const isExistingDocAllowed = docDataMatcherWrite(doc.toJSON(true) as any);\n if (!isExistingDocAllowed) {\n adapter.closeConnection(res, 403, 'Forbidden');\n return;\n }\n const isAllowed = this.changeValidator(authData, {\n newDocumentState: removeServerOnlyFields(docData as any),\n assumedMasterState: removeServerOnlyFields(doc.toJSON(true))\n });\n if (!isAllowed) {\n adapter.closeConnection(res, 403, 'Forbidden');\n return;\n }\n const mergedDocData = mergeServerDocumentFields(docData, doc.toJSON(true) as any);\n promises.push(doc.patch(mergedDocData).catch(err => onWriteError(err, mergedDocData)));\n }\n }\n await Promise.all(promises);\n }\n\n adapter.setResponseHeader(res, 'Content-Type', 'application/json')\n adapter.endResponseJson(res, {});\n });\n\n this.server.adapter.post(this.server.serverApp, '/' + this.urlPath + '/delete', async (req, res) => {\n const authData = await getAuthDataByRequest(this.server, req, res);\n if (!authData) { return; }\n\n const docDataMatcherWrite = getDocAllowedMatcher(this, ensureNotFalsy(authData));\n\n let ids: string[] = adapter.getRequestBody(req);\n while (ids.length > 0) {\n const useIds = ids.slice(0);\n ids = [];\n const promises: Promise<any>[] = [];\n const docsMap = await this.collection.findByIds(useIds).exec();\n for (const id of useIds) {\n const doc = docsMap.get(id);\n if (doc) {\n const isAllowedDoc = docDataMatcherWrite(doc.toJSON(true) as any);\n if (!isAllowedDoc) {\n adapter.closeConnection(res, 403, 'Forbidden');\n return;\n }\n\n const isAllowedChange = this.changeValidator(authData, {\n newDocumentState: removeServerOnlyFields(doc.toJSON(true)) as any,\n assumedMasterState: removeServerOnlyFields(doc.toJSON(true)) as any\n });\n if (!isAllowedChange) {\n adapter.closeConnection(res, 403, 'Forbidden');\n return;\n }\n\n promises.push(doc.remove().catch((err: RxError) => {\n if (err.rxdb && err.code === 'CONFLICT') {\n // just retry on conflicts\n ids.push(id);\n } else {\n adapter.closeConnection(res, 500, 'Internal Server Error');\n throw err;\n }\n }));\n }\n }\n await Promise.all(promises);\n }\n adapter.setResponseHeader(res, 'Content-Type', 'application/json');\n adapter.endResponseJson(res, {});\n });\n }\n}\n\n\n/**\n * \"block\" the previous version urls and send a 426 on them so that\n * the clients know they must update.\n */\nexport function blockPreviousReplicationVersionPathsRest(\n server: RxServer<any, any>,\n path: string,\n currentVersion: number\n\n) {\n let v = 0;\n while (v < currentVersion) {\n const version = v;\n /**\n * Some adapters do not allow regex or handle them property (like Koa),\n * so to make it easier, use the hard-coded array of path parts.\n */\n [\n '',\n 'query',\n 'query/observe',\n 'get',\n 'set',\n 'delete'\n ].forEach(subPath => {\n server.adapter.all(server.serverApp, '/' + path + '/' + version + '/' + subPath, (req, res) => {\n server.adapter.closeConnection(res, 426, 'Outdated version ' + version + ' (newest is ' + currentVersion + ')');\n });\n });\n v++;\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -38,6 +38,28 @@ export function docContainsServerOnlyFields(serverOnlyFields, doc) {
38
38
  });
39
39
  return has;
40
40
  }
41
+
42
+ /**
43
+ * Returns a function that strips the given server-only fields from a client
44
+ * document. This is used when accepting a NEW document from a client write to
45
+ * make sure that server-only fields cannot be populated by the client.
46
+ *
47
+ * Unlike {@link removeServerOnlyFieldsMonad} this does not assign undefined to
48
+ * RxDB internal meta fields (`_meta`, `_rev`, `_attachments`) so the result can
49
+ * be safely passed to `RxCollection.insert()`.
50
+ */
51
+ export function stripServerOnlyFieldsMonad(serverOnlyFields) {
52
+ if (serverOnlyFields.length === 0) {
53
+ return docData => docData;
54
+ }
55
+ return docData => {
56
+ var ret = flatClone(docData);
57
+ for (var i = 0; i < serverOnlyFields.length; i++) {
58
+ delete ret[serverOnlyFields[i]];
59
+ }
60
+ return ret;
61
+ };
62
+ }
41
63
  export function removeServerOnlyFieldsMonad(serverOnlyFields) {
42
64
  var serverOnlyFieldsStencil = {
43
65
  _meta: undefined,
@@ -59,18 +81,32 @@ export function mergeServerDocumentFieldsMonad(serverOnlyFields) {
59
81
  // useFields.push('_attachments');
60
82
  useFields = uniqueArray(useFields);
61
83
  return (clientDoc, serverDoc) => {
84
+ if (!clientDoc) {
85
+ return clientDoc;
86
+ }
62
87
  var ret = flatClone(clientDoc);
63
88
  if (!serverDoc) {
64
- // Initialize server-only fields to null for new documents
65
- useFields.forEach(field => {
66
- ret[field] = null;
67
- });
89
+ // No prior server-side state exists for this document (new
90
+ // insert). The client must not be able to populate server-only
91
+ // fields on creation, so strip them from the merged document.
92
+ useFields.forEach(field => delete ret[field]);
68
93
  return ret;
69
94
  }
70
95
  useFields.forEach(field => {
71
- // Only copy if field exists on serverDoc to avoid creating
72
- // properties with undefined value (which break deepEqual key count)
73
- ret[field] = field in serverDoc ? serverDoc[field] : null;
96
+ /**
97
+ * If the field exists on the serverDoc, copy its value so that
98
+ * the server-side value always wins over anything the client
99
+ * sent. If it does not exist on the serverDoc, delete it from
100
+ * the merged document so the key is truly absent — using `null`
101
+ * here would make the merged row differ from the stored master
102
+ * state during the isEqual check inside masterWrite and
103
+ * produce a false conflict that silently reverts the write.
104
+ */
105
+ if (field in serverDoc) {
106
+ ret[field] = serverDoc[field];
107
+ } else {
108
+ delete ret[field];
109
+ }
74
110
  });
75
111
  return ret;
76
112
  };
@@ -1 +1 @@
1
- {"version":3,"file":"helper.js","names":["flatClone","getFromMapOrCreate","getQueryMatcher","normalizeMangoQuery","uniqueArray","setCors","server","path","cors","useCors","adapter","serverApp","AUTH_PER_REQUEST","WeakMap","getAuthDataByRequest","request","response","headers","getRequestHeaders","authData","authHandler","err","closeConnection","defaultMatchingQuery","selector","skip","sort","getDocAllowedMatcher","endpoint","useQuery","queryModifier","collection","schema","jsonSchema","docDataMatcher","docContainsServerOnlyFields","serverOnlyFields","doc","has","find","field","removeServerOnlyFieldsMonad","serverOnlyFieldsStencil","_meta","undefined","_rev","_attachments","forEach","docData","Object","assign","mergeServerDocumentFieldsMonad","useFields","slice","clientDoc","serverDoc","ret","doesContainRegexQuerySelector","Array","isArray","found","item","entries","key","value"],"sources":["../../../../src/plugins/server/helper.ts"],"sourcesContent":["import { RxServer } from './rx-server';\nimport type {\n Request,\n Response,\n NextFunction\n} from 'express';\nimport type { RxServerAdapter, RxServerAuthData, RxServerEndpoint } from './types';\nimport {\n FilledMangoQuery,\n MangoQuerySelector,\n RxDocumentData,\n flatClone,\n getFromMapOrCreate,\n getQueryMatcher,\n normalizeMangoQuery,\n uniqueArray\n} from 'rxdb/plugins/core';\n\nexport function setCors(\n server: RxServer<any, any>,\n path: string,\n cors?: string\n) {\n let useCors = cors;\n if (!useCors) {\n useCors = server.cors;\n }\n if (useCors) {\n server.adapter.setCors(server.serverApp, path, useCors);\n }\n}\n\n\nconst AUTH_PER_REQUEST = new WeakMap<any, Promise<any>>();\n\nexport async function getAuthDataByRequest<AuthType, RequestType, ResponseType>(\n server: RxServer<any, AuthType>,\n request: RequestType,\n response: ResponseType\n): Promise<RxServerAuthData<AuthType> | false> {\n return getFromMapOrCreate(\n AUTH_PER_REQUEST,\n request,\n async () => {\n try {\n const headers = server.adapter.getRequestHeaders(request);\n const authData = await server.authHandler(headers);\n return authData;\n } catch (err) {\n server.adapter.closeConnection(response, 401, 'Unauthorized');\n return false;\n }\n }\n );\n};\n\nconst defaultMatchingQuery: FilledMangoQuery<any> = {\n selector: {},\n skip: 0,\n sort: []\n} as const;\n\nexport function getDocAllowedMatcher<RxDocType, AuthType>(\n endpoint: RxServerEndpoint<AuthType, RxDocType>,\n authData: RxServerAuthData<AuthType>\n) {\n const useQuery: FilledMangoQuery<RxDocType> = endpoint.queryModifier ? endpoint.queryModifier(\n authData,\n normalizeMangoQuery(\n endpoint.collection.schema.jsonSchema,\n {}\n )\n ) : defaultMatchingQuery;\n const docDataMatcher = getQueryMatcher(endpoint.collection.schema.jsonSchema, useQuery);\n return docDataMatcher;\n}\n\n\nexport function docContainsServerOnlyFields(\n serverOnlyFields: string[],\n doc: any\n) {\n const has = serverOnlyFields.find(field => {\n return typeof doc[field] !== 'undefined'\n });\n return has;\n}\n\nexport function removeServerOnlyFieldsMonad<RxDocType>(serverOnlyFields: string[]) {\n const serverOnlyFieldsStencil: any = {\n _meta: undefined,\n _rev: undefined,\n _attachments: undefined\n };\n serverOnlyFields.forEach(field => serverOnlyFieldsStencil[field] = undefined);\n return (\n docData?: RxDocType | RxDocumentData<RxDocType>\n ) => {\n if (!docData) {\n return docData;\n }\n return Object.assign({}, docData, serverOnlyFieldsStencil);\n }\n}\n\nexport function mergeServerDocumentFieldsMonad<RxDocType>(serverOnlyFields: string[]) {\n let useFields = serverOnlyFields.slice(0);\n // useFields.push('_rev');\n // useFields.push('_meta');\n // useFields.push('_attachments');\n useFields = uniqueArray(useFields);\n\n return (\n clientDoc: RxDocType | RxDocumentData<RxDocType>,\n serverDoc?: RxDocType | RxDocumentData<RxDocType>\n ) => {\n const ret = flatClone(clientDoc);\n if (!serverDoc) {\n // Initialize server-only fields to null for new documents\n useFields.forEach(field => {\n (ret as any)[field] = null;\n });\n return ret;\n }\n useFields.forEach(field => {\n // Only copy if field exists on serverDoc to avoid creating\n // properties with undefined value (which break deepEqual key count)\n (ret as any)[field] = field in (serverDoc as any) ? (serverDoc as any)[field] : null;\n });\n return ret;\n }\n}\n\n\n/**\n * $regex queries are dangerous because they can dos-attack the server.\n * \n * @param selector \n */\nexport function doesContainRegexQuerySelector(selector: MangoQuerySelector<any> | any): boolean {\n if (!selector) {\n return false;\n }\n if (Array.isArray(selector)) {\n const found = !!selector.find(item => doesContainRegexQuerySelector(item));\n return found;\n }\n\n if (typeof selector !== 'object') {\n return false;\n }\n\n const entries = Object.entries(selector);\n for (const [key, value] of entries) {\n if (key === '$regex') {\n return true;\n } else {\n const has = doesContainRegexQuerySelector(value);\n if (has) {\n return true;\n }\n }\n }\n\n return false;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"helper.js","names":["flatClone","getFromMapOrCreate","getQueryMatcher","normalizeMangoQuery","uniqueArray","setCors","server","path","cors","useCors","adapter","serverApp","AUTH_PER_REQUEST","WeakMap","getAuthDataByRequest","request","response","headers","getRequestHeaders","authData","authHandler","err","closeConnection","defaultMatchingQuery","selector","skip","sort","getDocAllowedMatcher","endpoint","useQuery","queryModifier","collection","schema","jsonSchema","docDataMatcher","docContainsServerOnlyFields","serverOnlyFields","doc","has","find","field","stripServerOnlyFieldsMonad","length","docData","ret","i","removeServerOnlyFieldsMonad","serverOnlyFieldsStencil","_meta","undefined","_rev","_attachments","forEach","Object","assign","mergeServerDocumentFieldsMonad","useFields","slice","clientDoc","serverDoc","doesContainRegexQuerySelector","Array","isArray","found","item","entries","key","value"],"sources":["../../../../src/plugins/server/helper.ts"],"sourcesContent":["import { RxServer } from './rx-server';\nimport type {\n Request,\n Response,\n NextFunction\n} from 'express';\nimport type { RxServerAdapter, RxServerAuthData, RxServerEndpoint } from './types';\nimport {\n FilledMangoQuery,\n MangoQuerySelector,\n RxDocumentData,\n flatClone,\n getFromMapOrCreate,\n getQueryMatcher,\n normalizeMangoQuery,\n uniqueArray\n} from 'rxdb/plugins/core';\n\nexport function setCors(\n server: RxServer<any, any>,\n path: string,\n cors?: string\n) {\n let useCors = cors;\n if (!useCors) {\n useCors = server.cors;\n }\n if (useCors) {\n server.adapter.setCors(server.serverApp, path, useCors);\n }\n}\n\n\nconst AUTH_PER_REQUEST = new WeakMap<any, Promise<any>>();\n\nexport async function getAuthDataByRequest<AuthType, RequestType, ResponseType>(\n server: RxServer<any, AuthType>,\n request: RequestType,\n response: ResponseType\n): Promise<RxServerAuthData<AuthType> | false> {\n return getFromMapOrCreate(\n AUTH_PER_REQUEST,\n request,\n async () => {\n try {\n const headers = server.adapter.getRequestHeaders(request);\n const authData = await server.authHandler(headers);\n return authData;\n } catch (err) {\n server.adapter.closeConnection(response, 401, 'Unauthorized');\n return false;\n }\n }\n );\n};\n\nconst defaultMatchingQuery: FilledMangoQuery<any> = {\n selector: {},\n skip: 0,\n sort: []\n} as const;\n\nexport function getDocAllowedMatcher<RxDocType, AuthType>(\n endpoint: RxServerEndpoint<AuthType, RxDocType>,\n authData: RxServerAuthData<AuthType>\n) {\n const useQuery: FilledMangoQuery<RxDocType> = endpoint.queryModifier ? endpoint.queryModifier(\n authData,\n normalizeMangoQuery(\n endpoint.collection.schema.jsonSchema,\n {}\n )\n ) : defaultMatchingQuery;\n const docDataMatcher = getQueryMatcher(endpoint.collection.schema.jsonSchema, useQuery);\n return docDataMatcher;\n}\n\n\nexport function docContainsServerOnlyFields(\n serverOnlyFields: string[],\n doc: any\n) {\n const has = serverOnlyFields.find(field => {\n return typeof doc[field] !== 'undefined'\n });\n return has;\n}\n\n/**\n * Returns a function that strips the given server-only fields from a client\n * document. This is used when accepting a NEW document from a client write to\n * make sure that server-only fields cannot be populated by the client.\n *\n * Unlike {@link removeServerOnlyFieldsMonad} this does not assign undefined to\n * RxDB internal meta fields (`_meta`, `_rev`, `_attachments`) so the result can\n * be safely passed to `RxCollection.insert()`.\n */\nexport function stripServerOnlyFieldsMonad<RxDocType>(serverOnlyFields: string[]) {\n if (serverOnlyFields.length === 0) {\n return (docData: RxDocType | RxDocumentData<RxDocType>) => docData;\n }\n return (docData: RxDocType | RxDocumentData<RxDocType>) => {\n const ret: any = flatClone(docData);\n for (let i = 0; i < serverOnlyFields.length; i++) {\n delete ret[serverOnlyFields[i]];\n }\n return ret;\n };\n}\n\nexport function removeServerOnlyFieldsMonad<RxDocType>(serverOnlyFields: string[]) {\n const serverOnlyFieldsStencil: any = {\n _meta: undefined,\n _rev: undefined,\n _attachments: undefined\n };\n serverOnlyFields.forEach(field => serverOnlyFieldsStencil[field] = undefined);\n return (\n docData?: RxDocType | RxDocumentData<RxDocType>\n ) => {\n if (!docData) {\n return docData;\n }\n return Object.assign({}, docData, serverOnlyFieldsStencil);\n }\n}\n\nexport function mergeServerDocumentFieldsMonad<RxDocType>(serverOnlyFields: string[]) {\n let useFields = serverOnlyFields.slice(0);\n // useFields.push('_rev');\n // useFields.push('_meta');\n // useFields.push('_attachments');\n useFields = uniqueArray(useFields);\n\n return (\n clientDoc: RxDocType | RxDocumentData<RxDocType>,\n serverDoc?: RxDocType | RxDocumentData<RxDocType>\n ) => {\n if (!clientDoc) {\n return clientDoc;\n }\n const ret = flatClone(clientDoc);\n if (!serverDoc) {\n // No prior server-side state exists for this document (new\n // insert). The client must not be able to populate server-only\n // fields on creation, so strip them from the merged document.\n useFields.forEach(field => delete (ret as any)[field]);\n return ret;\n }\n useFields.forEach(field => {\n /**\n * If the field exists on the serverDoc, copy its value so that\n * the server-side value always wins over anything the client\n * sent. If it does not exist on the serverDoc, delete it from\n * the merged document so the key is truly absent — using `null`\n * here would make the merged row differ from the stored master\n * state during the isEqual check inside masterWrite and\n * produce a false conflict that silently reverts the write.\n */\n if (field in (serverDoc as any)) {\n (ret as any)[field] = (serverDoc as any)[field];\n } else {\n delete (ret as any)[field];\n }\n });\n return ret;\n }\n}\n\n\n/**\n * $regex queries are dangerous because they can dos-attack the server.\n * \n * @param selector \n */\nexport function doesContainRegexQuerySelector(selector: MangoQuerySelector<any> | any): boolean {\n if (!selector) {\n return false;\n }\n if (Array.isArray(selector)) {\n const found = !!selector.find(item => doesContainRegexQuerySelector(item));\n return found;\n }\n\n if (typeof selector !== 'object') {\n return false;\n }\n\n const entries = Object.entries(selector);\n for (const [key, value] of entries) {\n if (key === '$regex') {\n return true;\n } else {\n const has = doesContainRegexQuerySelector(value);\n if (has) {\n return true;\n }\n }\n }\n\n return false;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -4,6 +4,7 @@ export * from "./types.js";
4
4
  export * from "./endpoint-replication.js";
5
5
  export * from "./endpoint-rest.js";
6
6
  export * from "./helper.js";
7
+ export * from "./performance-test.js";
7
8
  export async function createRxServer(options) {
8
9
  options = flatClone(options);
9
10
  if (!options.serverApp) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["ensureNotFalsy","flatClone","RxServer","createRxServer","options","serverApp","adapter","create","authHandler","data","validUntil","Date","now","server","cors"],"sources":["../../../../src/plugins/server/index.ts"],"sourcesContent":["import { ensureNotFalsy, flatClone } from 'rxdb/plugins/utils';\nimport { RxServer } from './rx-server.ts';\nimport { RxServerAuthHandler, RxServerOptions } from './types.ts';\n\nexport * from './types.ts';\nexport * from './endpoint-replication.ts';\nexport * from './endpoint-rest.ts';\nexport * from './helper.ts';\n\nexport async function createRxServer<ServerAdapterType, AuthType>(\n options: RxServerOptions<ServerAdapterType, AuthType>\n): Promise<RxServer<ServerAdapterType, AuthType>> {\n options = flatClone(options);\n if (!options.serverApp) {\n options.serverApp = await options.adapter.create();\n }\n const authHandler: RxServerAuthHandler<AuthType> = options.authHandler ? options.authHandler : () => {\n return {\n data: {} as any,\n validUntil: Date.now() * 2\n };\n };\n\n const server = new RxServer<ServerAdapterType, AuthType>(\n options,\n authHandler,\n ensureNotFalsy(options.serverApp),\n options.cors\n );\n\n return server;\n}\n"],"mappings":"AAAA,SAASA,cAAc,EAAEC,SAAS,QAAQ,oBAAoB;AAC9D,SAASC,QAAQ,QAAQ,gBAAgB;AAGzC,cAAc,YAAY;AAC1B,cAAc,2BAA2B;AACzC,cAAc,oBAAoB;AAClC,cAAc,aAAa;AAE3B,OAAO,eAAeC,cAAcA,CAChCC,OAAqD,EACP;EAC9CA,OAAO,GAAGH,SAAS,CAACG,OAAO,CAAC;EAC5B,IAAI,CAACA,OAAO,CAACC,SAAS,EAAE;IACpBD,OAAO,CAACC,SAAS,GAAG,MAAMD,OAAO,CAACE,OAAO,CAACC,MAAM,CAAC,CAAC;EACtD;EACA,IAAMC,WAA0C,GAAGJ,OAAO,CAACI,WAAW,GAAGJ,OAAO,CAACI,WAAW,GAAG,MAAM;IACjG,OAAO;MACHC,IAAI,EAAE,CAAC,CAAQ;MACfC,UAAU,EAAEC,IAAI,CAACC,GAAG,CAAC,CAAC,GAAG;IAC7B,CAAC;EACL,CAAC;EAED,IAAMC,MAAM,GAAG,IAAIX,QAAQ,CACvBE,OAAO,EACPI,WAAW,EACXR,cAAc,CAACI,OAAO,CAACC,SAAS,CAAC,EACjCD,OAAO,CAACU,IACZ,CAAC;EAED,OAAOD,MAAM;AACjB","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["ensureNotFalsy","flatClone","RxServer","createRxServer","options","serverApp","adapter","create","authHandler","data","validUntil","Date","now","server","cors"],"sources":["../../../../src/plugins/server/index.ts"],"sourcesContent":["import { ensureNotFalsy, flatClone } from 'rxdb/plugins/utils';\nimport { RxServer } from './rx-server.ts';\nimport { RxServerAuthHandler, RxServerOptions } from './types.ts';\n\nexport * from './types.ts';\nexport * from './endpoint-replication.ts';\nexport * from './endpoint-rest.ts';\nexport * from './helper.ts';\nexport * from './performance-test.ts';\n\nexport async function createRxServer<ServerAdapterType, AuthType>(\n options: RxServerOptions<ServerAdapterType, AuthType>\n): Promise<RxServer<ServerAdapterType, AuthType>> {\n options = flatClone(options);\n if (!options.serverApp) {\n options.serverApp = await options.adapter.create();\n }\n const authHandler: RxServerAuthHandler<AuthType> = options.authHandler ? options.authHandler : () => {\n return {\n data: {} as any,\n validUntil: Date.now() * 2\n };\n };\n\n const server = new RxServer<ServerAdapterType, AuthType>(\n options,\n authHandler,\n ensureNotFalsy(options.serverApp),\n options.cors\n );\n\n return server;\n}\n"],"mappings":"AAAA,SAASA,cAAc,EAAEC,SAAS,QAAQ,oBAAoB;AAC9D,SAASC,QAAQ,QAAQ,gBAAgB;AAGzC,cAAc,YAAY;AAC1B,cAAc,2BAA2B;AACzC,cAAc,oBAAoB;AAClC,cAAc,aAAa;AAC3B,cAAc,uBAAuB;AAErC,OAAO,eAAeC,cAAcA,CAChCC,OAAqD,EACP;EAC9CA,OAAO,GAAGH,SAAS,CAACG,OAAO,CAAC;EAC5B,IAAI,CAACA,OAAO,CAACC,SAAS,EAAE;IACpBD,OAAO,CAACC,SAAS,GAAG,MAAMD,OAAO,CAACE,OAAO,CAACC,MAAM,CAAC,CAAC;EACtD;EACA,IAAMC,WAA0C,GAAGJ,OAAO,CAACI,WAAW,GAAGJ,OAAO,CAACI,WAAW,GAAG,MAAM;IACjG,OAAO;MACHC,IAAI,EAAE,CAAC,CAAQ;MACfC,UAAU,EAAEC,IAAI,CAACC,GAAG,CAAC,CAAC,GAAG;IAC7B,CAAC;EACL,CAAC;EAED,IAAMC,MAAM,GAAG,IAAIX,QAAQ,CACvBE,OAAO,EACPI,WAAW,EACXR,cAAc,CAACI,OAAO,CAACC,SAAS,CAAC,EACjCD,OAAO,CAACU,IACZ,CAAC;EAED,OAAOD,MAAM;AACjB","ignoreList":[]}
@@ -0,0 +1,271 @@
1
+ /**
2
+ * A reusable performance test suite for RxDB server adapters.
3
+ * This function can be exported and used by consumers who
4
+ * create custom adapters to verify their performance characteristics.
5
+ */
6
+
7
+ import { randomToken } from 'rxdb/plugins/core';
8
+ import { createRxServer } from "./index.js";
9
+ import { createRestClient } from "../client-rest/index.js";
10
+
11
+ /**
12
+ * Simple document schema for performance testing.
13
+ */
14
+
15
+ var PERF_SCHEMA = {
16
+ version: 0,
17
+ primaryKey: 'id',
18
+ type: 'object',
19
+ properties: {
20
+ id: {
21
+ type: 'string',
22
+ maxLength: 100
23
+ },
24
+ name: {
25
+ type: 'string'
26
+ },
27
+ value: {
28
+ type: 'number'
29
+ }
30
+ },
31
+ required: ['id', 'name', 'value']
32
+ };
33
+ function generateDocs(count) {
34
+ var docs = [];
35
+ for (var i = 0; i < count; i++) {
36
+ docs.push({
37
+ id: randomToken(12) + '-' + i,
38
+ name: 'doc-name-' + i + '-' + randomToken(6),
39
+ value: i
40
+ });
41
+ }
42
+ return docs;
43
+ }
44
+ async function createCollectionWithDocs(db, docs) {
45
+ var colName = 'perf' + randomToken(8).toLowerCase();
46
+ var collections = await db.addCollections({
47
+ [colName]: {
48
+ schema: PERF_SCHEMA
49
+ }
50
+ });
51
+ var col = collections[colName];
52
+ if (docs.length > 0) {
53
+ await col.bulkInsert(docs);
54
+ }
55
+ return col;
56
+ }
57
+
58
+ /**
59
+ * Runs a set of performance tests against the given adapter.
60
+ * Each test creates its own server and collection, measures
61
+ * the time for the operations, and tears down afterwards.
62
+ *
63
+ * Returns an array of results that callers can assert against
64
+ * or log for comparison purposes.
65
+ */
66
+ export async function performanceTest(options) {
67
+ var batchSize = options.batchSize ?? 30;
68
+ var results = [];
69
+
70
+ // ---------- Test 1: writes via REST /set ----------
71
+ {
72
+ var db = await options.createDatabase();
73
+ var col = await createCollectionWithDocs(db, []);
74
+ var port = await options.getPort();
75
+ var server = await createRxServer({
76
+ adapter: options.adapter,
77
+ database: db,
78
+ port
79
+ });
80
+ var endpoint = await server.addRestEndpoint({
81
+ name: randomToken(10),
82
+ collection: col
83
+ });
84
+ await server.start();
85
+ var client = createRestClient('http://localhost:' + port + '/' + endpoint.urlPath, {});
86
+ var docs = generateDocs(batchSize);
87
+ var start = performance.now();
88
+ await client.set(docs);
89
+ var timeMs = performance.now() - start;
90
+ results.push({
91
+ name: 'REST /set (bulk write ' + batchSize + ' docs)',
92
+ timeMs,
93
+ opsCount: batchSize,
94
+ opsPerSecond: Math.round(batchSize / timeMs * 1000)
95
+ });
96
+ await db.close();
97
+ }
98
+
99
+ // ---------- Test 2: query via REST /query ----------
100
+ {
101
+ var _docs = generateDocs(batchSize);
102
+ var _db = await options.createDatabase();
103
+ var _col = await createCollectionWithDocs(_db, _docs);
104
+ var _port = await options.getPort();
105
+ var _server = await createRxServer({
106
+ adapter: options.adapter,
107
+ database: _db,
108
+ port: _port
109
+ });
110
+ var _endpoint = await _server.addRestEndpoint({
111
+ name: randomToken(10),
112
+ collection: _col
113
+ });
114
+ await _server.start();
115
+ var _client = createRestClient('http://localhost:' + _port + '/' + _endpoint.urlPath, {});
116
+ var iterations = 10;
117
+ var _start = performance.now();
118
+ for (var i = 0; i < iterations; i++) {
119
+ await _client.query({
120
+ selector: {}
121
+ });
122
+ }
123
+ var _timeMs = performance.now() - _start;
124
+ results.push({
125
+ name: 'REST /query (' + iterations + ' queries, ' + batchSize + ' docs each)',
126
+ timeMs: _timeMs,
127
+ opsCount: iterations,
128
+ opsPerSecond: Math.round(iterations / _timeMs * 1000)
129
+ });
130
+ await _db.close();
131
+ }
132
+
133
+ // ---------- Test 3: get by IDs via REST /get ----------
134
+ {
135
+ var _docs2 = generateDocs(batchSize);
136
+ var _db2 = await options.createDatabase();
137
+ var _col2 = await createCollectionWithDocs(_db2, _docs2);
138
+ var _port2 = await options.getPort();
139
+ var _server2 = await createRxServer({
140
+ adapter: options.adapter,
141
+ database: _db2,
142
+ port: _port2
143
+ });
144
+ var _endpoint2 = await _server2.addRestEndpoint({
145
+ name: randomToken(10),
146
+ collection: _col2
147
+ });
148
+ await _server2.start();
149
+ var _client2 = createRestClient('http://localhost:' + _port2 + '/' + _endpoint2.urlPath, {});
150
+ var ids = _docs2.map(d => d.id);
151
+ var _iterations = 10;
152
+ var _start2 = performance.now();
153
+ for (var _i = 0; _i < _iterations; _i++) {
154
+ await _client2.get(ids);
155
+ }
156
+ var _timeMs2 = performance.now() - _start2;
157
+ results.push({
158
+ name: 'REST /get (' + _iterations + ' fetches, ' + batchSize + ' ids each)',
159
+ timeMs: _timeMs2,
160
+ opsCount: _iterations,
161
+ opsPerSecond: Math.round(_iterations / _timeMs2 * 1000)
162
+ });
163
+ await _db2.close();
164
+ }
165
+
166
+ // ---------- Test 4: delete via REST /delete ----------
167
+ {
168
+ var _docs3 = generateDocs(batchSize);
169
+ var _db3 = await options.createDatabase();
170
+ var _col3 = await createCollectionWithDocs(_db3, _docs3);
171
+ var _port3 = await options.getPort();
172
+ var _server3 = await createRxServer({
173
+ adapter: options.adapter,
174
+ database: _db3,
175
+ port: _port3
176
+ });
177
+ var _endpoint3 = await _server3.addRestEndpoint({
178
+ name: randomToken(10),
179
+ collection: _col3
180
+ });
181
+ await _server3.start();
182
+ var _client3 = createRestClient('http://localhost:' + _port3 + '/' + _endpoint3.urlPath, {});
183
+ var _ids = _docs3.map(d => d.id);
184
+ var _start3 = performance.now();
185
+ await _client3.delete(_ids);
186
+ var _timeMs3 = performance.now() - _start3;
187
+ results.push({
188
+ name: 'REST /delete (bulk delete ' + batchSize + ' docs)',
189
+ timeMs: _timeMs3,
190
+ opsCount: batchSize,
191
+ opsPerSecond: Math.round(batchSize / _timeMs3 * 1000)
192
+ });
193
+ await _db3.close();
194
+ }
195
+
196
+ // ---------- Test 5: sequential single-doc writes ----------
197
+ {
198
+ var _db4 = await options.createDatabase();
199
+ var _col4 = await createCollectionWithDocs(_db4, []);
200
+ var _port4 = await options.getPort();
201
+ var _server4 = await createRxServer({
202
+ adapter: options.adapter,
203
+ database: _db4,
204
+ port: _port4
205
+ });
206
+ var _endpoint4 = await _server4.addRestEndpoint({
207
+ name: randomToken(10),
208
+ collection: _col4
209
+ });
210
+ await _server4.start();
211
+ var _client4 = createRestClient('http://localhost:' + _port4 + '/' + _endpoint4.urlPath, {});
212
+ var count = batchSize;
213
+ var _start4 = performance.now();
214
+ for (var _i2 = 0; _i2 < count; _i2++) {
215
+ await _client4.set([{
216
+ id: randomToken(12) + '-' + _i2,
217
+ name: 'seq-' + _i2,
218
+ value: _i2
219
+ }]);
220
+ }
221
+ var _timeMs4 = performance.now() - _start4;
222
+ results.push({
223
+ name: 'REST /set sequential (' + count + ' single-doc writes)',
224
+ timeMs: _timeMs4,
225
+ opsCount: count,
226
+ opsPerSecond: Math.round(count / _timeMs4 * 1000)
227
+ });
228
+ await _db4.close();
229
+ }
230
+
231
+ // ---------- Test 6: replication pull via HTTP ----------
232
+ {
233
+ var _docs4 = generateDocs(batchSize);
234
+ var _db5 = await options.createDatabase();
235
+ var _col5 = await createCollectionWithDocs(_db5, _docs4);
236
+ var _port5 = await options.getPort();
237
+ var _server5 = await createRxServer({
238
+ adapter: options.adapter,
239
+ database: _db5,
240
+ port: _port5
241
+ });
242
+ var _endpoint5 = await _server5.addReplicationEndpoint({
243
+ name: randomToken(10),
244
+ collection: _col5
245
+ });
246
+ await _server5.start();
247
+ var url = 'http://localhost:' + _port5 + '/' + _endpoint5.urlPath;
248
+ var _iterations2 = 10;
249
+ var _start5 = performance.now();
250
+ for (var _i3 = 0; _i3 < _iterations2; _i3++) {
251
+ var response = await fetch(url + '/pull?lwt=0&id=&limit=' + batchSize, {
252
+ method: 'GET',
253
+ headers: {
254
+ 'Accept': 'application/json',
255
+ 'Content-Type': 'application/json'
256
+ }
257
+ });
258
+ await response.json();
259
+ }
260
+ var _timeMs5 = performance.now() - _start5;
261
+ results.push({
262
+ name: 'Replication /pull (' + _iterations2 + ' pulls, ' + batchSize + ' docs each)',
263
+ timeMs: _timeMs5,
264
+ opsCount: _iterations2,
265
+ opsPerSecond: Math.round(_iterations2 / _timeMs5 * 1000)
266
+ });
267
+ await _db5.close();
268
+ }
269
+ return results;
270
+ }
271
+ //# sourceMappingURL=performance-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"performance-test.js","names":["randomToken","createRxServer","createRestClient","PERF_SCHEMA","version","primaryKey","type","properties","id","maxLength","name","value","required","generateDocs","count","docs","i","push","createCollectionWithDocs","db","colName","toLowerCase","collections","addCollections","schema","col","length","bulkInsert","performanceTest","options","batchSize","results","createDatabase","port","getPort","server","adapter","database","endpoint","addRestEndpoint","collection","start","client","urlPath","performance","now","set","timeMs","opsCount","opsPerSecond","Math","round","close","iterations","query","selector","ids","map","d","get","delete","addReplicationEndpoint","url","response","fetch","method","headers","json"],"sources":["../../../../src/plugins/server/performance-test.ts"],"sourcesContent":["/**\n * A reusable performance test suite for RxDB server adapters.\n * This function can be exported and used by consumers who\n * create custom adapters to verify their performance characteristics.\n */\n\nimport type {\n RxDatabase,\n RxCollection\n} from 'rxdb/plugins/core';\nimport { randomToken } from 'rxdb/plugins/core';\nimport type { RxServerAdapter } from './types.ts';\nimport { createRxServer } from './index.ts';\nimport { createRestClient } from '../client-rest/index.ts';\n\nexport type PerformanceTestResult = {\n /** Name of the test case */\n name: string;\n /** Total time in milliseconds */\n timeMs: number;\n /** Number of operations performed */\n opsCount: number;\n /** Calculated operations per second */\n opsPerSecond: number;\n};\n\nexport type PerformanceTestOptions<ServerAppType> = {\n adapter: RxServerAdapter<ServerAppType, any, any>;\n /**\n * Function that creates a fresh RxDatabase instance.\n * Must return a database that can be closed after each test.\n */\n createDatabase: () => Promise<RxDatabase>;\n /**\n * Function that resolves an available port for the server.\n */\n getPort: () => Promise<number>;\n /**\n * Number of documents to use for each test.\n * [default=30]\n */\n batchSize?: number;\n};\n\n/**\n * Simple document schema for performance testing.\n */\ntype PerfDocType = {\n id: string;\n name: string;\n value: number;\n};\n\nconst PERF_SCHEMA = {\n version: 0,\n primaryKey: 'id',\n type: 'object' as const,\n properties: {\n id: { type: 'string' as const, maxLength: 100 },\n name: { type: 'string' as const },\n value: { type: 'number' as const }\n },\n required: ['id', 'name', 'value'] as const\n};\n\n\nfunction generateDocs(count: number): PerfDocType[] {\n const docs: PerfDocType[] = [];\n for (let i = 0; i < count; i++) {\n docs.push({\n id: randomToken(12) + '-' + i,\n name: 'doc-name-' + i + '-' + randomToken(6),\n value: i\n });\n }\n return docs;\n}\n\nasync function createCollectionWithDocs(\n db: RxDatabase,\n docs: PerfDocType[]\n): Promise<RxCollection<PerfDocType>> {\n const colName = 'perf' + randomToken(8).toLowerCase();\n const collections = await db.addCollections({\n [colName]: { schema: PERF_SCHEMA }\n });\n const col = collections[colName];\n if (docs.length > 0) {\n await col.bulkInsert(docs);\n }\n return col;\n}\n\n/**\n * Runs a set of performance tests against the given adapter.\n * Each test creates its own server and collection, measures\n * the time for the operations, and tears down afterwards.\n *\n * Returns an array of results that callers can assert against\n * or log for comparison purposes.\n */\nexport async function performanceTest<ServerAppType>(\n options: PerformanceTestOptions<ServerAppType>\n): Promise<PerformanceTestResult[]> {\n const batchSize = options.batchSize ?? 30;\n const results: PerformanceTestResult[] = [];\n\n // ---------- Test 1: writes via REST /set ----------\n {\n const db = await options.createDatabase();\n const col = await createCollectionWithDocs(db, []);\n const port = await options.getPort();\n\n const server = await createRxServer({\n adapter: options.adapter,\n database: db,\n port\n });\n const endpoint = await server.addRestEndpoint({\n name: randomToken(10),\n collection: col\n });\n await server.start();\n\n const client = createRestClient<PerfDocType>(\n 'http://localhost:' + port + '/' + endpoint.urlPath,\n {}\n );\n\n const docs = generateDocs(batchSize);\n const start = performance.now();\n await client.set(docs);\n const timeMs = performance.now() - start;\n\n results.push({\n name: 'REST /set (bulk write ' + batchSize + ' docs)',\n timeMs,\n opsCount: batchSize,\n opsPerSecond: Math.round((batchSize / timeMs) * 1000)\n });\n\n await db.close();\n }\n\n // ---------- Test 2: query via REST /query ----------\n {\n const docs = generateDocs(batchSize);\n const db = await options.createDatabase();\n const col = await createCollectionWithDocs(db, docs);\n const port = await options.getPort();\n\n const server = await createRxServer({\n adapter: options.adapter,\n database: db,\n port\n });\n const endpoint = await server.addRestEndpoint({\n name: randomToken(10),\n collection: col\n });\n await server.start();\n\n const client = createRestClient<PerfDocType>(\n 'http://localhost:' + port + '/' + endpoint.urlPath,\n {}\n );\n\n const iterations = 10;\n const start = performance.now();\n for (let i = 0; i < iterations; i++) {\n await client.query({ selector: {} });\n }\n const timeMs = performance.now() - start;\n\n results.push({\n name: 'REST /query (' + iterations + ' queries, ' + batchSize + ' docs each)',\n timeMs,\n opsCount: iterations,\n opsPerSecond: Math.round((iterations / timeMs) * 1000)\n });\n\n await db.close();\n }\n\n // ---------- Test 3: get by IDs via REST /get ----------\n {\n const docs = generateDocs(batchSize);\n const db = await options.createDatabase();\n const col = await createCollectionWithDocs(db, docs);\n const port = await options.getPort();\n\n const server = await createRxServer({\n adapter: options.adapter,\n database: db,\n port\n });\n const endpoint = await server.addRestEndpoint({\n name: randomToken(10),\n collection: col\n });\n await server.start();\n\n const client = createRestClient<PerfDocType>(\n 'http://localhost:' + port + '/' + endpoint.urlPath,\n {}\n );\n\n const ids = docs.map(d => d.id);\n const iterations = 10;\n const start = performance.now();\n for (let i = 0; i < iterations; i++) {\n await client.get(ids);\n }\n const timeMs = performance.now() - start;\n\n results.push({\n name: 'REST /get (' + iterations + ' fetches, ' + batchSize + ' ids each)',\n timeMs,\n opsCount: iterations,\n opsPerSecond: Math.round((iterations / timeMs) * 1000)\n });\n\n await db.close();\n }\n\n // ---------- Test 4: delete via REST /delete ----------\n {\n const docs = generateDocs(batchSize);\n const db = await options.createDatabase();\n const col = await createCollectionWithDocs(db, docs);\n const port = await options.getPort();\n\n const server = await createRxServer({\n adapter: options.adapter,\n database: db,\n port\n });\n const endpoint = await server.addRestEndpoint({\n name: randomToken(10),\n collection: col\n });\n await server.start();\n\n const client = createRestClient<PerfDocType>(\n 'http://localhost:' + port + '/' + endpoint.urlPath,\n {}\n );\n\n const ids = docs.map(d => d.id);\n const start = performance.now();\n await client.delete(ids);\n const timeMs = performance.now() - start;\n\n results.push({\n name: 'REST /delete (bulk delete ' + batchSize + ' docs)',\n timeMs,\n opsCount: batchSize,\n opsPerSecond: Math.round((batchSize / timeMs) * 1000)\n });\n\n await db.close();\n }\n\n // ---------- Test 5: sequential single-doc writes ----------\n {\n const db = await options.createDatabase();\n const col = await createCollectionWithDocs(db, []);\n const port = await options.getPort();\n\n const server = await createRxServer({\n adapter: options.adapter,\n database: db,\n port\n });\n const endpoint = await server.addRestEndpoint({\n name: randomToken(10),\n collection: col\n });\n await server.start();\n\n const client = createRestClient<PerfDocType>(\n 'http://localhost:' + port + '/' + endpoint.urlPath,\n {}\n );\n\n const count = batchSize;\n const start = performance.now();\n for (let i = 0; i < count; i++) {\n await client.set([{\n id: randomToken(12) + '-' + i,\n name: 'seq-' + i,\n value: i\n }]);\n }\n const timeMs = performance.now() - start;\n\n results.push({\n name: 'REST /set sequential (' + count + ' single-doc writes)',\n timeMs,\n opsCount: count,\n opsPerSecond: Math.round((count / timeMs) * 1000)\n });\n\n await db.close();\n }\n\n // ---------- Test 6: replication pull via HTTP ----------\n {\n const docs = generateDocs(batchSize);\n const db = await options.createDatabase();\n const col = await createCollectionWithDocs(db, docs);\n const port = await options.getPort();\n\n const server = await createRxServer({\n adapter: options.adapter,\n database: db,\n port\n });\n const endpoint = await server.addReplicationEndpoint({\n name: randomToken(10),\n collection: col\n });\n await server.start();\n\n const url = 'http://localhost:' + port + '/' + endpoint.urlPath;\n const iterations = 10;\n const start = performance.now();\n for (let i = 0; i < iterations; i++) {\n const response = await fetch(url + '/pull?lwt=0&id=&limit=' + batchSize, {\n method: 'GET',\n headers: {\n 'Accept': 'application/json',\n 'Content-Type': 'application/json'\n }\n });\n await response.json();\n }\n const timeMs = performance.now() - start;\n\n results.push({\n name: 'Replication /pull (' + iterations + ' pulls, ' + batchSize + ' docs each)',\n timeMs,\n opsCount: iterations,\n opsPerSecond: Math.round((iterations / timeMs) * 1000)\n });\n\n await db.close();\n }\n\n return results;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -5,8 +5,18 @@ export declare function setCors(server: RxServer<any, any>, path: string, cors?:
5
5
  export declare function getAuthDataByRequest<AuthType, RequestType, ResponseType>(server: RxServer<any, AuthType>, request: RequestType, response: ResponseType): Promise<RxServerAuthData<AuthType> | false>;
6
6
  export declare function getDocAllowedMatcher<RxDocType, AuthType>(endpoint: RxServerEndpoint<AuthType, RxDocType>, authData: RxServerAuthData<AuthType>): import("rxdb/plugins/core").QueryMatcher<RxDocumentData<RxDocType>>;
7
7
  export declare function docContainsServerOnlyFields(serverOnlyFields: string[], doc: any): string | undefined;
8
+ /**
9
+ * Returns a function that strips the given server-only fields from a client
10
+ * document. This is used when accepting a NEW document from a client write to
11
+ * make sure that server-only fields cannot be populated by the client.
12
+ *
13
+ * Unlike {@link removeServerOnlyFieldsMonad} this does not assign undefined to
14
+ * RxDB internal meta fields (`_meta`, `_rev`, `_attachments`) so the result can
15
+ * be safely passed to `RxCollection.insert()`.
16
+ */
17
+ export declare function stripServerOnlyFieldsMonad<RxDocType>(serverOnlyFields: string[]): (docData: RxDocType | RxDocumentData<RxDocType>) => any;
8
18
  export declare function removeServerOnlyFieldsMonad<RxDocType>(serverOnlyFields: string[]): (docData?: RxDocType | RxDocumentData<RxDocType>) => any;
9
- export declare function mergeServerDocumentFieldsMonad<RxDocType>(serverOnlyFields: string[]): (clientDoc: RxDocType | RxDocumentData<RxDocType>, serverDoc?: RxDocType | RxDocumentData<RxDocType>) => RxDocType | RxDocumentData<RxDocType>;
19
+ export declare function mergeServerDocumentFieldsMonad<RxDocType>(serverOnlyFields: string[]): (clientDoc: RxDocType | RxDocumentData<RxDocType>, serverDoc?: RxDocType | RxDocumentData<RxDocType>) => RxDocType;
10
20
  /**
11
21
  * $regex queries are dangerous because they can dos-attack the server.
12
22
  *
@@ -4,4 +4,5 @@ export * from './types.ts';
4
4
  export * from './endpoint-replication.ts';
5
5
  export * from './endpoint-rest.ts';
6
6
  export * from './helper.ts';
7
+ export * from './performance-test.ts';
7
8
  export declare function createRxServer<ServerAdapterType, AuthType>(options: RxServerOptions<ServerAdapterType, AuthType>): Promise<RxServer<ServerAdapterType, AuthType>>;