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
package/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ > **Do not add changes here.**
4
+ > All changelog entries belong in the [orga/changelog/](orga/changelog/) folder, one file per change.
5
+ > They will be merged into this file on release.
package/README.md CHANGED
@@ -9,3 +9,7 @@ The code of the server plugins is in a different repository because:
9
9
 
10
10
  ## Issues
11
11
  The issue section of this repository is closed. Please make issues directly at the [RxDB Repo](https://github.com/pubkey/rxdb/issues).
12
+
13
+
14
+
15
+ ----
@@ -16,8 +16,17 @@ var RxServerAdapterExpress = exports.RxServerAdapterExpress = {
16
16
  return app;
17
17
  },
18
18
  setCors(serverApp, path, cors) {
19
+ /**
20
+ * Per the CORS spec, `Access-Control-Allow-Origin: *` cannot be combined
21
+ * with `Access-Control-Allow-Credentials: true` - browsers reject such
22
+ * responses on credentialed requests. When the caller opted into the
23
+ * '*' default, reflect the request origin back instead (via `origin: true`),
24
+ * which keeps the "allow from anywhere" semantics while staying compatible
25
+ * with credentials.
26
+ */
27
+ var originOption = cors === '*' ? true : cors;
19
28
  serverApp.use('/' + path + '/*splat', (0, _cors.default)({
20
- origin: cors,
29
+ origin: originOption,
21
30
  // some legacy browsers (IE11, various SmartTVs) choke on 204
22
31
  optionsSuccessStatus: 200,
23
32
  credentials: true
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["_express","_interopRequireDefault","require","_cors","_core","HTTP_SERVER_BY_EXPRESS","exports","WeakMap","RxServerAdapterExpress","create","app","express","use","json","setCors","serverApp","path","cors","expressCors","origin","optionsSuccessStatus","credentials","getRequestBody","req","body","getRequestHeaders","headers","getRequestQuery","query","onRequestClose","fn","on","setResponseHeader","res","k","v","setHeader","responseWrite","data","write","endResponseJson","endResponse","end","closeConnection","response","code","message","error","status","set","JSON","stringify","setSSEHeaders","writeHead","flushHeaders","get","handler","post","all","listen","port","hostname","httpServer","Promise","rej","ret","ensureNotFalsy","close","getFromMapOrThrow","err","setImmediate","emit","closeAllConnections"],"sources":["../../../../src/plugins/adapter-express/index.ts"],"sourcesContent":["import type { RxServerAdapter } from '../server/types';\nimport type { Express } from 'express';\nimport express, { Request, Response } from 'express';\nimport {\n Server as HttpServer\n} from 'http';\nimport expressCors from 'cors';\nimport { ensureNotFalsy, getFromMapOrThrow } from 'rxdb/plugins/core';\n\nexport const HTTP_SERVER_BY_EXPRESS = new WeakMap<Express, HttpServer>();\n\nexport const RxServerAdapterExpress: RxServerAdapter<Express, Request, Response> = {\n async create() {\n const app = express();\n app.use(express.json());\n return app;\n },\n setCors(serverApp, path, cors) {\n serverApp.use('/' + path + '/*splat', expressCors({\n origin: cors,\n // some legacy browsers (IE11, various SmartTVs) choke on 204\n optionsSuccessStatus: 200,\n credentials: true\n }));\n },\n\n getRequestBody(req: Request) {\n return req.body;\n },\n getRequestHeaders(req: Request) {\n return req.headers as any;\n },\n getRequestQuery(req: Request) {\n return req.query;\n },\n onRequestClose(req: Request, fn) {\n req.on('close', () => {\n fn();\n });\n },\n\n setResponseHeader(res: Response, k: string, v: string) {\n res.setHeader(k, v);\n },\n responseWrite(res: Response, data: string) {\n res.write(data);\n },\n endResponseJson(res: Response, data: any) {\n res.json(data);\n },\n endResponse(res: Response) {\n res.end();\n },\n async closeConnection(response: Response, code: number, message: string) {\n const responseWrite = {\n code,\n error: true,\n message\n };\n response.status(code);\n response.set(\"Connection\", \"close\");\n await response.write(JSON.stringify(responseWrite));\n response.end();\n },\n setSSEHeaders(res: Response) {\n res.writeHead(200, {\n /**\n * Use exact these headers to make is less likely\n * for people to have problems.\n * @link https://www.youtube.com/watch?v=0PcMuYGJPzM\n */\n 'Content-Type': 'text/event-stream; charset=utf-8',\n 'Connection': 'keep-alive',\n 'Cache-Control': 'no-cache',\n\n /**\n * @link https://github.com/pubkey/rxdb-server/pull/58\n */\n 'access-control-allow-credentials': 'true',\n\n /**\n * Required for nginx\n * @link https://stackoverflow.com/q/61029079/3443137\n */\n 'X-Accel-Buffering': 'no'\n });\n res.flushHeaders();\n },\n\n get(serverApp, path, handler) {\n serverApp.get(path, handler);\n },\n post(serverApp, path, handler) {\n serverApp.post(path, handler);\n },\n all(serverApp, path, handler) {\n serverApp.all(path, handler);\n },\n async listen(serverApp, port, hostname) {\n const httpServer: HttpServer = await new Promise((res, rej) => {\n const ret = ensureNotFalsy(serverApp).listen(port, hostname, () => {\n res(ret);\n });\n });\n HTTP_SERVER_BY_EXPRESS.set(serverApp, httpServer);\n },\n async close(serverApp) {\n const httpServer = getFromMapOrThrow(HTTP_SERVER_BY_EXPRESS, serverApp);\n await new Promise<void>((res, rej) => {\n httpServer.close((err) => {\n if (err) { rej(err); } else { res(); }\n });\n /**\n * By default it will await all ongoing connections\n * before it closes. So we have to close it directly.\n * @link https://stackoverflow.com/a/36830072/3443137\n */\n setImmediate(() => httpServer.emit('close'));\n });\n },\n async closeAllConnections(serverApp) {\n const httpServer = HTTP_SERVER_BY_EXPRESS.get(serverApp);\n if (httpServer) {\n await httpServer.closeAllConnections();\n }\n }\n};\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["_express","_interopRequireDefault","require","_cors","_core","HTTP_SERVER_BY_EXPRESS","exports","WeakMap","RxServerAdapterExpress","create","app","express","use","json","setCors","serverApp","path","cors","originOption","expressCors","origin","optionsSuccessStatus","credentials","getRequestBody","req","body","getRequestHeaders","headers","getRequestQuery","query","onRequestClose","fn","on","setResponseHeader","res","k","v","setHeader","responseWrite","data","write","endResponseJson","endResponse","end","closeConnection","response","code","message","error","status","set","JSON","stringify","setSSEHeaders","writeHead","flushHeaders","get","handler","post","all","listen","port","hostname","httpServer","Promise","rej","ret","ensureNotFalsy","close","getFromMapOrThrow","err","setImmediate","emit","closeAllConnections"],"sources":["../../../../src/plugins/adapter-express/index.ts"],"sourcesContent":["import type { RxServerAdapter } from '../server/types';\nimport type { Express } from 'express';\nimport express, { Request, Response } from 'express';\nimport {\n Server as HttpServer\n} from 'http';\nimport expressCors from 'cors';\nimport { ensureNotFalsy, getFromMapOrThrow } from 'rxdb/plugins/core';\n\nexport const HTTP_SERVER_BY_EXPRESS = new WeakMap<Express, HttpServer>();\n\nexport const RxServerAdapterExpress: RxServerAdapter<Express, Request, Response> = {\n async create() {\n const app = express();\n app.use(express.json());\n return app;\n },\n setCors(serverApp, path, cors) {\n /**\n * Per the CORS spec, `Access-Control-Allow-Origin: *` cannot be combined\n * with `Access-Control-Allow-Credentials: true` - browsers reject such\n * responses on credentialed requests. When the caller opted into the\n * '*' default, reflect the request origin back instead (via `origin: true`),\n * which keeps the \"allow from anywhere\" semantics while staying compatible\n * with credentials.\n */\n const originOption: expressCors.CorsOptions['origin'] = cors === '*' ? true : cors;\n serverApp.use('/' + path + '/*splat', expressCors({\n origin: originOption,\n // some legacy browsers (IE11, various SmartTVs) choke on 204\n optionsSuccessStatus: 200,\n credentials: true\n }));\n },\n\n getRequestBody(req: Request) {\n return req.body;\n },\n getRequestHeaders(req: Request) {\n return req.headers as any;\n },\n getRequestQuery(req: Request) {\n return req.query;\n },\n onRequestClose(req: Request, fn) {\n req.on('close', () => {\n fn();\n });\n },\n\n setResponseHeader(res: Response, k: string, v: string) {\n res.setHeader(k, v);\n },\n responseWrite(res: Response, data: string) {\n res.write(data);\n },\n endResponseJson(res: Response, data: any) {\n res.json(data);\n },\n endResponse(res: Response) {\n res.end();\n },\n async closeConnection(response: Response, code: number, message: string) {\n const responseWrite = {\n code,\n error: true,\n message\n };\n response.status(code);\n response.set(\"Connection\", \"close\");\n await response.write(JSON.stringify(responseWrite));\n response.end();\n },\n setSSEHeaders(res: Response) {\n res.writeHead(200, {\n /**\n * Use exact these headers to make is less likely\n * for people to have problems.\n * @link https://www.youtube.com/watch?v=0PcMuYGJPzM\n */\n 'Content-Type': 'text/event-stream; charset=utf-8',\n 'Connection': 'keep-alive',\n 'Cache-Control': 'no-cache',\n\n /**\n * @link https://github.com/pubkey/rxdb-server/pull/58\n */\n 'access-control-allow-credentials': 'true',\n\n /**\n * Required for nginx\n * @link https://stackoverflow.com/q/61029079/3443137\n */\n 'X-Accel-Buffering': 'no'\n });\n res.flushHeaders();\n },\n\n get(serverApp, path, handler) {\n serverApp.get(path, handler);\n },\n post(serverApp, path, handler) {\n serverApp.post(path, handler);\n },\n all(serverApp, path, handler) {\n serverApp.all(path, handler);\n },\n async listen(serverApp, port, hostname) {\n const httpServer: HttpServer = await new Promise((res, rej) => {\n const ret = ensureNotFalsy(serverApp).listen(port, hostname, () => {\n res(ret);\n });\n });\n HTTP_SERVER_BY_EXPRESS.set(serverApp, httpServer);\n },\n async close(serverApp) {\n const httpServer = getFromMapOrThrow(HTTP_SERVER_BY_EXPRESS, serverApp);\n await new Promise<void>((res, rej) => {\n httpServer.close((err) => {\n if (err) { rej(err); } else { res(); }\n });\n /**\n * By default it will await all ongoing connections\n * before it closes. So we have to close it directly.\n * @link https://stackoverflow.com/a/36830072/3443137\n */\n setImmediate(() => httpServer.emit('close'));\n });\n },\n async closeAllConnections(serverApp) {\n const httpServer = HTTP_SERVER_BY_EXPRESS.get(serverApp);\n if (httpServer) {\n await httpServer.closeAllConnections();\n }\n }\n};\n"],"mappings":"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","ignoreList":[]}
@@ -46,7 +46,12 @@ var RxRestClient = exports.RxRestClient = /*#__PURE__*/function () {
46
46
  };
47
47
  _proto.observeQuery = function observeQuery(query) {
48
48
  var result = new _rxjs.Subject();
49
- var queryAsBase64 = btoa(JSON.stringify(query));
49
+ // Standard base64 uses '+' and '/'; both are URL-reserved and a raw
50
+ // '+' in the query string is decoded as a space by the server's
51
+ // URL parser. Without encoding, the server's atob() then rejects
52
+ // the corrupted string with "Invalid character" and the SSE
53
+ // connection silently never delivers any document.
54
+ var queryAsBase64 = encodeURIComponent(btoa(JSON.stringify(query)));
50
55
  var eventSource = new this.eventSource(this.endpointUrl + '/query/observe?query=' + queryAsBase64, {
51
56
  withCredentials: true,
52
57
  /**
@@ -63,18 +68,18 @@ var RxRestClient = exports.RxRestClient = /*#__PURE__*/function () {
63
68
  };
64
69
  return result.asObservable();
65
70
  };
66
- _proto.get = function get(ids) {
67
- var response = (0, _utils.postRequest)(this.endpointUrl + '/get', ids, this.headers);
71
+ _proto.get = async function get(ids) {
72
+ var response = await (0, _utils.postRequest)(this.endpointUrl + '/get', ids, this.headers);
68
73
  this.handleError(response);
69
74
  return response;
70
75
  };
71
- _proto.set = function set(docs) {
72
- var response = (0, _utils.postRequest)(this.endpointUrl + '/set', docs, this.headers);
76
+ _proto.set = async function set(docs) {
77
+ var response = await (0, _utils.postRequest)(this.endpointUrl + '/set', docs, this.headers);
73
78
  this.handleError(response);
74
79
  return response;
75
80
  };
76
- _proto.delete = function _delete(ids) {
77
- var response = (0, _utils.postRequest)(this.endpointUrl + '/delete', ids, this.headers);
81
+ _proto.delete = async function _delete(ids) {
82
+ var response = await (0, _utils.postRequest)(this.endpointUrl + '/delete', ids, this.headers);
78
83
  this.handleError(response);
79
84
  return response;
80
85
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["_utils","require","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_rxjs","_eventsource","_utils2","RxRestClient","endpointUrl","headers","eventSource","EventSource","_proto","setHeaders","handleError","response","error","Error","JSON","stringify","query","postRequest","observeQuery","result","Subject","queryAsBase64","btoa","withCredentials","fetch","customFetchWithFixedHeaders","onmessage","event","eventData","parse","data","next","asObservable","ids","set","docs","delete","createRestClient"],"sources":["../../../../src/plugins/client-rest/index.ts"],"sourcesContent":["import { ById, MangoQuery } from 'rxdb/plugins/core';\nimport { postRequest } from './utils.ts';\nimport { Observable, Subject } from 'rxjs';\nimport { EventSource } from 'eventsource';\nimport { customFetchWithFixedHeaders } from '../../utils.ts';\n\nexport class RxRestClient<RxDocType> {\n constructor(\n public readonly endpointUrl: string,\n public headers: ById<string> = {},\n public readonly eventSource: typeof EventSource | any = EventSource\n ) { }\n\n setHeaders(headers: ById<string>) {\n this.headers = headers;\n }\n\n handleError(response: any) {\n if (response.error) {\n throw new Error('Server returned an error ' + JSON.stringify(response));\n }\n }\n\n async query(query: MangoQuery<RxDocType>): Promise<{ documents: RxDocType[] }> {\n const response = await postRequest(\n this.endpointUrl + '/query',\n query,\n this.headers\n );\n this.handleError(response);\n return response;\n }\n\n observeQuery(query: MangoQuery<RxDocType>): Observable<RxDocType[]> {\n const result = new Subject<RxDocType[]>;\n const queryAsBase64 = btoa(JSON.stringify(query));\n const eventSource: EventSource = new this.eventSource(\n this.endpointUrl + '/query/observe?query=' + queryAsBase64,\n {\n withCredentials: true,\n /**\n * Sending headers is not supported by the Browser EventSource API,\n * only by the npm module we use. In react-native you might have\n * to set another EventSource implementation.\n * @link https://www.npmjs.com/package/eventsource\n */\n fetch: customFetchWithFixedHeaders(this.headers)\n }\n );\n eventSource.onmessage = event => {\n const eventData = JSON.parse(event.data);\n result.next(eventData);\n };\n return result.asObservable();\n }\n\n get(ids: string[]): Promise<{ documents: RxDocType[] }> {\n const response = postRequest(\n this.endpointUrl + '/get',\n ids,\n this.headers\n );\n this.handleError(response);\n return response;\n }\n\n set(docs: RxDocType[]) {\n const response = postRequest(\n this.endpointUrl + '/set',\n docs,\n this.headers\n );\n this.handleError(response);\n return response;\n }\n\n delete(ids: string[]) {\n const response = postRequest(\n this.endpointUrl + '/delete',\n ids,\n this.headers\n );\n this.handleError(response);\n return response;\n }\n}\n\nexport function createRestClient<RxDocType>(\n endpointUrl: string,\n headers: ById<string>,\n eventSource: typeof EventSource | any = EventSource\n) {\n\n return new RxRestClient<RxDocType>(\n endpointUrl,\n headers,\n eventSource\n );\n}\n\n\nexport * from './utils.ts';\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["_utils","require","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_rxjs","_eventsource","_utils2","RxRestClient","endpointUrl","headers","eventSource","EventSource","_proto","setHeaders","handleError","response","error","Error","JSON","stringify","query","postRequest","observeQuery","result","Subject","queryAsBase64","encodeURIComponent","btoa","withCredentials","fetch","customFetchWithFixedHeaders","onmessage","event","eventData","parse","data","next","asObservable","ids","set","docs","delete","createRestClient"],"sources":["../../../../src/plugins/client-rest/index.ts"],"sourcesContent":["import { ById, MangoQuery } from 'rxdb/plugins/core';\nimport { postRequest } from './utils.ts';\nimport { Observable, Subject } from 'rxjs';\nimport { EventSource } from 'eventsource';\nimport { customFetchWithFixedHeaders } from '../../utils.ts';\n\nexport class RxRestClient<RxDocType> {\n constructor(\n public readonly endpointUrl: string,\n public headers: ById<string> = {},\n public readonly eventSource: typeof EventSource | any = EventSource\n ) { }\n\n setHeaders(headers: ById<string>) {\n this.headers = headers;\n }\n\n handleError(response: any) {\n if (response.error) {\n throw new Error('Server returned an error ' + JSON.stringify(response));\n }\n }\n\n async query(query: MangoQuery<RxDocType>): Promise<{ documents: RxDocType[] }> {\n const response = await postRequest(\n this.endpointUrl + '/query',\n query,\n this.headers\n );\n this.handleError(response);\n return response;\n }\n\n observeQuery(query: MangoQuery<RxDocType>): Observable<RxDocType[]> {\n const result = new Subject<RxDocType[]>;\n // Standard base64 uses '+' and '/'; both are URL-reserved and a raw\n // '+' in the query string is decoded as a space by the server's\n // URL parser. Without encoding, the server's atob() then rejects\n // the corrupted string with \"Invalid character\" and the SSE\n // connection silently never delivers any document.\n const queryAsBase64 = encodeURIComponent(btoa(JSON.stringify(query)));\n const eventSource: EventSource = new this.eventSource(\n this.endpointUrl + '/query/observe?query=' + queryAsBase64,\n {\n withCredentials: true,\n /**\n * Sending headers is not supported by the Browser EventSource API,\n * only by the npm module we use. In react-native you might have\n * to set another EventSource implementation.\n * @link https://www.npmjs.com/package/eventsource\n */\n fetch: customFetchWithFixedHeaders(this.headers)\n }\n );\n eventSource.onmessage = event => {\n const eventData = JSON.parse(event.data);\n result.next(eventData);\n };\n return result.asObservable();\n }\n\n async get(ids: string[]): Promise<{ documents: RxDocType[] }> {\n const response = await postRequest(\n this.endpointUrl + '/get',\n ids,\n this.headers\n );\n this.handleError(response);\n return response;\n }\n\n async set(docs: RxDocType[]) {\n const response = await postRequest(\n this.endpointUrl + '/set',\n docs,\n this.headers\n );\n this.handleError(response);\n return response;\n }\n\n async delete(ids: string[]) {\n const response = await postRequest(\n this.endpointUrl + '/delete',\n ids,\n this.headers\n );\n this.handleError(response);\n return response;\n }\n}\n\nexport function createRestClient<RxDocType>(\n endpointUrl: string,\n headers: ById<string>,\n eventSource: typeof EventSource | any = EventSource\n) {\n\n return new RxRestClient<RxDocType>(\n endpointUrl,\n headers,\n eventSource\n );\n}\n\n\nexport * from './utils.ts';\n"],"mappings":"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","ignoreList":[]}
@@ -79,7 +79,7 @@ function replicateServer(options) {
79
79
  async handler(checkpointOrNull, batchSize) {
80
80
  var lwt = checkpointOrNull && checkpointOrNull.lwt ? checkpointOrNull.lwt : 0;
81
81
  var id = checkpointOrNull && checkpointOrNull.id ? checkpointOrNull.id : '';
82
- var url = options.url + ("/pull?lwt=" + lwt + "&id=" + id + "&limit=" + batchSize);
82
+ var url = options.url + ("/pull?lwt=" + lwt + "&id=" + encodeURIComponent(id) + "&limit=" + batchSize);
83
83
  var response = await fetch(url, {
84
84
  method: 'GET',
85
85
  credentials: 'include',
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["_core","require","_leaderElection","_replication","_rxjs","_helpers","_eventsource","_utils","_types","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","RxServerReplicationState","_RxReplicationState","replicationIdentifier","collection","pull","push","live","retryTime","autoStart","headers","_this","outdatedClient$","Subject","unauthorized$","forbidden$","onCancel","complete","_inheritsLoose2","default","_proto","setHeaders","flatClone","RxReplicationState","replicateServer","options","newRxError","name","args","waitForLeadership","addRxPlugin","RxDBLeaderElectionPlugin","pullStream$","replicationPrimitivesPull","handler","checkpointOrNull","batchSize","lwt","id","url","response","fetch","method","credentials","assign","replicationState","data","parseResponse","documents","checkpoint","ensureNotFalsy","modifier","stream$","asObservable","replicationPrimitivesPush","changeRows","body","JSON","stringify","conflictsArray","startBefore","start","bind","useEventSource","eventSource","EventSource","refreshEventSource","withCredentials","customFetchWithFixedHeaders","onerror","err","code","next","close","promiseWait","then","onopen","x","onmessage","event","eventData","parse","startReplicationOnLeaderShip"],"sources":["../../../../src/plugins/replication-server/index.ts"],"sourcesContent":["import {\n ensureNotFalsy,\n flatClone,\n promiseWait,\n RxCollection,\n ReplicationPullOptions,\n ReplicationPushOptions,\n RxReplicationPullStreamItem,\n ById,\n addRxPlugin,\n newRxError,\n WithDeletedAndAttachments\n} from 'rxdb/plugins/core';\nimport { RxDBLeaderElectionPlugin } from 'rxdb/plugins/leader-election';\nimport {\n RxReplicationState,\n startReplicationOnLeaderShip\n} from 'rxdb/plugins/replication';\n\nimport { Subject } from 'rxjs';\nimport type {\n RxServerCheckpoint,\n ServerSyncOptions\n} from './types.ts';\nimport { parseResponse } from './helpers.ts';\nimport { EventSource } from 'eventsource';\nimport { customFetchWithFixedHeaders } from '../../utils.ts';\n\nexport * from './types.ts';\n\nexport class RxServerReplicationState<RxDocType> extends RxReplicationState<RxDocType, RxServerCheckpoint> {\n public readonly outdatedClient$ = new Subject<void>();\n public readonly unauthorized$ = new Subject<void>();\n public readonly forbidden$ = new Subject<void>();\n\n constructor(\n public readonly replicationIdentifier: string,\n public readonly collection: RxCollection<RxDocType, unknown, unknown, unknown>,\n public readonly pull?: ReplicationPullOptions<RxDocType, RxServerCheckpoint>,\n public readonly push?: ReplicationPushOptions<RxDocType>,\n public readonly live: boolean = true,\n public retryTime: number = 1000 * 5,\n public autoStart: boolean = true,\n public headers: ById<string> = {}\n ) {\n super(\n replicationIdentifier,\n collection,\n '_deleted',\n pull,\n push,\n live,\n retryTime,\n autoStart\n );\n\n this.onCancel.push(() => {\n this.outdatedClient$.complete();\n this.unauthorized$.complete();\n this.forbidden$.complete();\n });\n }\n\n setHeaders(headers: ById<string>): void {\n this.headers = flatClone(headers);\n }\n}\n\nexport function replicateServer<RxDocType>(\n options: ServerSyncOptions<RxDocType>\n): RxServerReplicationState<RxDocType> {\n\n if (!options.pull && !options.push) {\n throw newRxError('UT3', {\n collection: options.collection.name,\n args: {\n replicationIdentifier: options.replicationIdentifier\n }\n });\n }\n\n options.live = typeof options.live === 'undefined' ? true : options.live;\n options.waitForLeadership = typeof options.waitForLeadership === 'undefined' ? true : options.waitForLeadership;\n\n const collection = options.collection;\n addRxPlugin(RxDBLeaderElectionPlugin);\n\n const pullStream$: Subject<RxReplicationPullStreamItem<RxDocType, RxServerCheckpoint>> = new Subject();\n\n let replicationPrimitivesPull: ReplicationPullOptions<RxDocType, RxServerCheckpoint> | undefined;\n if (options.pull) {\n replicationPrimitivesPull = {\n async handler(checkpointOrNull, batchSize) {\n const lwt = checkpointOrNull && checkpointOrNull.lwt ? checkpointOrNull.lwt : 0;\n const id = checkpointOrNull && checkpointOrNull.id ? checkpointOrNull.id : '';\n const url = options.url + `/pull?lwt=${lwt}&id=${id}&limit=${batchSize}`;\n const response = await fetch(url, {\n method: 'GET',\n credentials: 'include',\n headers: Object.assign({\n 'Accept': 'application/json',\n 'Content-Type': 'application/json'\n }, replicationState.headers),\n });\n const data = await parseResponse(replicationState, response);\n return {\n documents: data.documents,\n checkpoint: data.checkpoint\n };\n },\n batchSize: ensureNotFalsy(options.pull).batchSize,\n modifier: ensureNotFalsy(options.pull).modifier,\n stream$: pullStream$.asObservable()\n };\n }\n\n let replicationPrimitivesPush: ReplicationPushOptions<RxDocType> | undefined;\n if (options.push) {\n replicationPrimitivesPush = {\n async handler(changeRows) {\n const response = await fetch(options.url + '/push', {\n method: 'POST',\n credentials: 'include',\n headers: Object.assign({\n 'Accept': 'application/json',\n 'Content-Type': 'application/json'\n }, replicationState.headers),\n body: JSON.stringify(changeRows)\n });\n const conflictsArray = await parseResponse(replicationState, response);\n return conflictsArray;\n },\n batchSize: options.push.batchSize,\n modifier: options.push.modifier\n };\n }\n\n const replicationState = new RxServerReplicationState<RxDocType>(\n options.replicationIdentifier,\n collection,\n replicationPrimitivesPull,\n replicationPrimitivesPush,\n options.live,\n options.retryTime,\n options.autoStart,\n options.headers\n );\n\n /**\n * Use long polling to get live changes for the pull.stream$\n */\n if (options.live && options.pull) {\n const startBefore = replicationState.start.bind(replicationState);\n replicationState.start = async () => {\n const useEventSource: typeof EventSource = options.eventSource ? options.eventSource : EventSource;\n let eventSource: EventSource;\n const refreshEventSource = () => {\n eventSource = new useEventSource(options.url + '/pullStream', {\n withCredentials: true,\n /**\n * Sending headers is not supported by the Browser EventSource API,\n * only by the npm module we use. In react-native you might have\n * to set another EventSource implementation.\n * @link https://www.npmjs.com/package/eventsource\n */\n fetch: customFetchWithFixedHeaders(replicationState.headers)\n });\n // TODO check for 426 errors and handle them\n eventSource.onerror = (err) => {\n if (err.code === 401) {\n replicationState.unauthorized$.next();\n eventSource.close();\n promiseWait(replicationState.retryTime).then(() => refreshEventSource());\n } else {\n pullStream$.next('RESYNC');\n }\n };\n eventSource.onopen = (x) => {\n pullStream$.next('RESYNC');\n }\n eventSource.onmessage = event => {\n const eventData: { documents: WithDeletedAndAttachments<RxDocType>[]; checkpoint: RxServerCheckpoint; } = JSON.parse(event.data);\n pullStream$.next({\n documents: eventData.documents,\n checkpoint: eventData.checkpoint\n });\n };\n }\n refreshEventSource();\n\n replicationState.onCancel.push(() => eventSource && eventSource.close());\n return startBefore();\n };\n }\n\n startReplicationOnLeaderShip(options.waitForLeadership, replicationState);\n\n return replicationState;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["_core","require","_leaderElection","_replication","_rxjs","_helpers","_eventsource","_utils","_types","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","RxServerReplicationState","_RxReplicationState","replicationIdentifier","collection","pull","push","live","retryTime","autoStart","headers","_this","outdatedClient$","Subject","unauthorized$","forbidden$","onCancel","complete","_inheritsLoose2","default","_proto","setHeaders","flatClone","RxReplicationState","replicateServer","options","newRxError","name","args","waitForLeadership","addRxPlugin","RxDBLeaderElectionPlugin","pullStream$","replicationPrimitivesPull","handler","checkpointOrNull","batchSize","lwt","id","url","encodeURIComponent","response","fetch","method","credentials","assign","replicationState","data","parseResponse","documents","checkpoint","ensureNotFalsy","modifier","stream$","asObservable","replicationPrimitivesPush","changeRows","body","JSON","stringify","conflictsArray","startBefore","start","bind","useEventSource","eventSource","EventSource","refreshEventSource","withCredentials","customFetchWithFixedHeaders","onerror","err","code","next","close","promiseWait","then","onopen","x","onmessage","event","eventData","parse","startReplicationOnLeaderShip"],"sources":["../../../../src/plugins/replication-server/index.ts"],"sourcesContent":["import {\n ensureNotFalsy,\n flatClone,\n promiseWait,\n RxCollection,\n ReplicationPullOptions,\n ReplicationPushOptions,\n RxReplicationPullStreamItem,\n ById,\n addRxPlugin,\n newRxError,\n WithDeletedAndAttachments\n} from 'rxdb/plugins/core';\nimport { RxDBLeaderElectionPlugin } from 'rxdb/plugins/leader-election';\nimport {\n RxReplicationState,\n startReplicationOnLeaderShip\n} from 'rxdb/plugins/replication';\n\nimport { Subject } from 'rxjs';\nimport type {\n RxServerCheckpoint,\n ServerSyncOptions\n} from './types.ts';\nimport { parseResponse } from './helpers.ts';\nimport { EventSource } from 'eventsource';\nimport { customFetchWithFixedHeaders } from '../../utils.ts';\n\nexport * from './types.ts';\n\nexport class RxServerReplicationState<RxDocType> extends RxReplicationState<RxDocType, RxServerCheckpoint> {\n public readonly outdatedClient$ = new Subject<void>();\n public readonly unauthorized$ = new Subject<void>();\n public readonly forbidden$ = new Subject<void>();\n\n constructor(\n public readonly replicationIdentifier: string,\n public readonly collection: RxCollection<RxDocType, unknown, unknown, unknown>,\n public readonly pull?: ReplicationPullOptions<RxDocType, RxServerCheckpoint>,\n public readonly push?: ReplicationPushOptions<RxDocType>,\n public readonly live: boolean = true,\n public retryTime: number = 1000 * 5,\n public autoStart: boolean = true,\n public headers: ById<string> = {}\n ) {\n super(\n replicationIdentifier,\n collection,\n '_deleted',\n pull,\n push,\n live,\n retryTime,\n autoStart\n );\n\n this.onCancel.push(() => {\n this.outdatedClient$.complete();\n this.unauthorized$.complete();\n this.forbidden$.complete();\n });\n }\n\n setHeaders(headers: ById<string>): void {\n this.headers = flatClone(headers);\n }\n}\n\nexport function replicateServer<RxDocType>(\n options: ServerSyncOptions<RxDocType>\n): RxServerReplicationState<RxDocType> {\n\n if (!options.pull && !options.push) {\n throw newRxError('UT3', {\n collection: options.collection.name,\n args: {\n replicationIdentifier: options.replicationIdentifier\n }\n });\n }\n\n options.live = typeof options.live === 'undefined' ? true : options.live;\n options.waitForLeadership = typeof options.waitForLeadership === 'undefined' ? true : options.waitForLeadership;\n\n const collection = options.collection;\n addRxPlugin(RxDBLeaderElectionPlugin);\n\n const pullStream$: Subject<RxReplicationPullStreamItem<RxDocType, RxServerCheckpoint>> = new Subject();\n\n let replicationPrimitivesPull: ReplicationPullOptions<RxDocType, RxServerCheckpoint> | undefined;\n if (options.pull) {\n replicationPrimitivesPull = {\n async handler(checkpointOrNull, batchSize) {\n const lwt = checkpointOrNull && checkpointOrNull.lwt ? checkpointOrNull.lwt : 0;\n const id = checkpointOrNull && checkpointOrNull.id ? checkpointOrNull.id : '';\n const url = options.url + `/pull?lwt=${lwt}&id=${encodeURIComponent(id)}&limit=${batchSize}`;\n const response = await fetch(url, {\n method: 'GET',\n credentials: 'include',\n headers: Object.assign({\n 'Accept': 'application/json',\n 'Content-Type': 'application/json'\n }, replicationState.headers),\n });\n const data = await parseResponse(replicationState, response);\n return {\n documents: data.documents,\n checkpoint: data.checkpoint\n };\n },\n batchSize: ensureNotFalsy(options.pull).batchSize,\n modifier: ensureNotFalsy(options.pull).modifier,\n stream$: pullStream$.asObservable()\n };\n }\n\n let replicationPrimitivesPush: ReplicationPushOptions<RxDocType> | undefined;\n if (options.push) {\n replicationPrimitivesPush = {\n async handler(changeRows) {\n const response = await fetch(options.url + '/push', {\n method: 'POST',\n credentials: 'include',\n headers: Object.assign({\n 'Accept': 'application/json',\n 'Content-Type': 'application/json'\n }, replicationState.headers),\n body: JSON.stringify(changeRows)\n });\n const conflictsArray = await parseResponse(replicationState, response);\n return conflictsArray;\n },\n batchSize: options.push.batchSize,\n modifier: options.push.modifier\n };\n }\n\n const replicationState = new RxServerReplicationState<RxDocType>(\n options.replicationIdentifier,\n collection,\n replicationPrimitivesPull,\n replicationPrimitivesPush,\n options.live,\n options.retryTime,\n options.autoStart,\n options.headers\n );\n\n /**\n * Use long polling to get live changes for the pull.stream$\n */\n if (options.live && options.pull) {\n const startBefore = replicationState.start.bind(replicationState);\n replicationState.start = async () => {\n const useEventSource: typeof EventSource = options.eventSource ? options.eventSource : EventSource;\n let eventSource: EventSource;\n const refreshEventSource = () => {\n eventSource = new useEventSource(options.url + '/pullStream', {\n withCredentials: true,\n /**\n * Sending headers is not supported by the Browser EventSource API,\n * only by the npm module we use. In react-native you might have\n * to set another EventSource implementation.\n * @link https://www.npmjs.com/package/eventsource\n */\n fetch: customFetchWithFixedHeaders(replicationState.headers)\n });\n // TODO check for 426 errors and handle them\n eventSource.onerror = (err) => {\n if (err.code === 401) {\n replicationState.unauthorized$.next();\n eventSource.close();\n promiseWait(replicationState.retryTime).then(() => refreshEventSource());\n } else {\n pullStream$.next('RESYNC');\n }\n };\n eventSource.onopen = (x) => {\n pullStream$.next('RESYNC');\n }\n eventSource.onmessage = event => {\n const eventData: { documents: WithDeletedAndAttachments<RxDocType>[]; checkpoint: RxServerCheckpoint; } = JSON.parse(event.data);\n pullStream$.next({\n documents: eventData.documents,\n checkpoint: eventData.checkpoint\n });\n };\n }\n refreshEventSource();\n\n replicationState.onCancel.push(() => eventSource && eventSource.close());\n return startBefore();\n };\n }\n\n startReplicationOnLeaderShip(options.waitForLeadership, replicationState);\n\n return replicationState;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -37,6 +37,7 @@ var RxServerReplicationEndpoint = exports.RxServerReplicationEndpoint = function
37
37
  };
38
38
  var removeServerOnlyFields = (0, _helper.removeServerOnlyFieldsMonad)(this.serverOnlyFields);
39
39
  var mergeServerDocumentFields = (0, _helper.mergeServerDocumentFieldsMonad)(this.serverOnlyFields);
40
+ var stripServerOnlyFields = (0, _helper.stripServerOnlyFieldsMonad)(this.serverOnlyFields);
40
41
  this.server.adapter.get(this.server.serverApp, '/' + this.urlPath + '/pull', async (req, res) => {
41
42
  var authData = await (0, _helper.getAuthDataByRequest)(this.server, req, res);
42
43
  if (!authData) {
@@ -107,8 +108,14 @@ var RxServerReplicationEndpoint = exports.RxServerReplicationEndpoint = function
107
108
  hasInvalidChange = true;
108
109
  }
109
110
  var serverDoc = currentStateDocs.get(id);
111
+ // When the document does not yet exist on the server, strip
112
+ // server-only fields from the new document state so a client
113
+ // cannot populate them on insert. Updates of existing docs
114
+ // are already protected because mergeServerDocumentFields
115
+ // overwrites those fields with the stored server value.
116
+ var newDocumentState = serverDoc ? mergeServerDocumentFields(row.newDocumentState, serverDoc) : stripServerOnlyFields(row.newDocumentState);
110
117
  return {
111
- newDocumentState: mergeServerDocumentFields(row.newDocumentState, serverDoc),
118
+ newDocumentState,
112
119
  assumedMasterState: mergeServerDocumentFields(row.assumedMasterState, serverDoc)
113
120
  };
114
121
  });
@@ -118,7 +125,7 @@ var RxServerReplicationEndpoint = exports.RxServerReplicationEndpoint = function
118
125
  }
119
126
  var conflicts = await replicationHandler.masterWrite(useRows);
120
127
  adapter.setResponseHeader(res, 'Content-Type', 'application/json');
121
- adapter.endResponseJson(res, conflicts);
128
+ adapter.endResponseJson(res, conflicts.map(c => removeServerOnlyFields(c)));
122
129
  });
123
130
  this.server.adapter.get(this.server.serverApp, '/' + this.urlPath + '/pullStream', async (req, res) => {
124
131
  var authData = await (0, _helper.getAuthDataByRequest)(this.server, req, res);
@@ -1 +1 @@
1
- {"version":3,"file":"endpoint-replication.js","names":["_core","require","_replicationWebsocket","_rxjs","_utils","_helper","RxServerReplicationEndpoint","exports","server","name","collection","queryModifier","changeValidator","serverOnlyFields","cors","type","adapter","setCors","join","blockPreviousReplicationVersionPaths","schema","version","urlPath","primaryPath","replicationHandler","getReplicationHandlerByCollection","database","authData","query","doesContainRegexQuerySelector","selector","Error","change","assumedMasterState","docContainsServerOnlyFields","newDocumentState","removeServerOnlyFields","removeServerOnlyFieldsMonad","mergeServerDocumentFields","mergeServerDocumentFieldsMonad","get","serverApp","req","res","getAuthDataByRequest","urlQuery","getRequestQuery","id","lwt","parseFloat","limit","parseInt","plainQuery","getChangedDocumentsSinceQuery","storageInstance","useQueryChanges","ensureNotFalsy","prepared","prepareQuery","jsonSchema","result","newCheckpoint","documents","length","lastOfArray","_meta","responseDocuments","map","d","setResponseHeader","endResponseJson","checkpoint","post","docDataMatcherWrite","getDocAllowedMatcher","rows","getRequestBody","ids","forEach","row","push","nonAllowedRow","find","closeConnection","hasInvalidChange","currentStateDocsArray","findDocumentsById","currentStateDocs","Map","set","useRows","isChangeValid","serverDoc","conflicts","masterWrite","setSSEHeaders","docDataMatcherStream","subscription","masterChangeStream$","pipe","mergeMap","changes","authHandler","getRequestHeaders","err","useDocs","filter","f","subscribe","filteredAndModified","responseWrite","JSON","stringify","onRequestClose","unsubscribe","endResponse","path","currentVersion","v","_loop","subPath","all"],"sources":["../../../../src/plugins/server/endpoint-replication.ts"],"sourcesContent":["import {\n FilledMangoQuery,\n RxCollection,\n RxReplicationHandler,\n RxReplicationWriteToMasterRow,\n StringKeys,\n prepareQuery,\n getChangedDocumentsSinceQuery,\n RxDocumentData\n} from 'rxdb/plugins/core';\nimport { getReplicationHandlerByCollection } from 'rxdb/plugins/replication-websocket';\nimport type { RxServer } from './rx-server.ts';\nimport type {\n RxServerAuthData,\n RxServerChangeValidator,\n RxServerEndpoint,\n RxServerQueryModifier\n} from './types.ts';\nimport { filter, mergeMap } from 'rxjs';\nimport {\n ensureNotFalsy,\n lastOfArray\n} from 'rxdb/plugins/utils';\n\nimport {\n docContainsServerOnlyFields,\n doesContainRegexQuerySelector,\n getAuthDataByRequest,\n getDocAllowedMatcher,\n mergeServerDocumentFieldsMonad,\n removeServerOnlyFieldsMonad,\n setCors\n} from './helper.ts';\nimport type { RxServerCheckpoint } from '../replication-server/types.ts';\n\nexport type RxReplicationEndpointMessageType = {\n id: string;\n method: StringKeys<RxReplicationHandler<any, any>> | 'auth';\n params: any[];\n};\n\nexport class RxServerReplicationEndpoint<ServerAppType, AuthType, RxDocType> implements RxServerEndpoint<AuthType, RxDocType> {\n readonly type = 'replication';\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 = this.server.adapter;\n\n setCors(this.server, [this.name].join('/'), cors);\n blockPreviousReplicationVersionPaths(this.server, [this.name].join('/'), collection.schema.version);\n\n this.urlPath = [this.name, collection.schema.version].join('/');\n\n const primaryPath = this.collection.schema.primaryPath;\n const replicationHandler: RxReplicationHandler<RxDocType, RxServerCheckpoint> = getReplicationHandlerByCollection<RxDocType>(this.server.database, collection.name);\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 (change.assumedMasterState && docContainsServerOnlyFields(serverOnlyFields, change.assumedMasterState)) ||\n docContainsServerOnlyFields(serverOnlyFields, change.newDocumentState)\n ) {\n return false;\n }\n return changeValidator(authData, change);\n }\n const removeServerOnlyFields = removeServerOnlyFieldsMonad<RxDocType>(this.serverOnlyFields);\n const mergeServerDocumentFields = mergeServerDocumentFieldsMonad<RxDocType>(this.serverOnlyFields);\n\n this.server.adapter.get(this.server.serverApp, '/' + this.urlPath + '/pull', async (req: any, res: any) => {\n const authData = await getAuthDataByRequest(this.server, req, res);\n if (!authData) { return; }\n\n const urlQuery = adapter.getRequestQuery(req);\n const id = urlQuery.id ? urlQuery.id as string : '';\n const lwt = urlQuery.lwt ? parseFloat(urlQuery.lwt as string) : 0;\n const limit = urlQuery.limit ? parseInt(urlQuery.limit as string, 10) : 1;\n const plainQuery = getChangedDocumentsSinceQuery<RxDocType, RxServerCheckpoint>(\n this.collection.storageInstance,\n limit,\n { id, lwt }\n );\n const useQueryChanges: FilledMangoQuery<RxDocType> = this.queryModifier(\n ensureNotFalsy(authData),\n plainQuery\n );\n const prepared = prepareQuery<RxDocType>(\n this.collection.schema.jsonSchema,\n useQueryChanges\n );\n const result = await this.collection.storageInstance.query(prepared);\n\n const newCheckpoint: RxServerCheckpoint = result.documents.length === 0 ? { id, lwt } : {\n id: ensureNotFalsy(lastOfArray(result.documents))[primaryPath] as string,\n lwt: ensureNotFalsy(lastOfArray(result.documents))._meta.lwt\n };\n const responseDocuments = result.documents.map(d => removeServerOnlyFields(d));\n adapter.setResponseHeader(res, 'Content-Type', 'application/json');\n adapter.endResponseJson(res, {\n documents: responseDocuments,\n checkpoint: newCheckpoint\n });\n });\n\n this.server.adapter.post(this.server.serverApp, '/' + this.urlPath + '/push', async (req: any, res: any) => {\n const authData = await getAuthDataByRequest(this.server, req, res);\n if (!authData) { return; }\n\n const docDataMatcherWrite = getDocAllowedMatcher(this, ensureNotFalsy(authData as any));\n const rows: RxReplicationWriteToMasterRow<RxDocType>[] = adapter.getRequestBody(req);\n const ids: string[] = [];\n rows.forEach(row => ids.push((row.newDocumentState as any)[primaryPath]));\n\n for (const row of rows) {\n // TODO remove this check\n if (row.assumedMasterState && (row.assumedMasterState as any)._meta) {\n throw new Error('body document contains meta!');\n }\n }\n\n // ensure all writes are allowed\n const nonAllowedRow = rows.find(row => {\n if (\n !docDataMatcherWrite(row.newDocumentState as any) ||\n (row.assumedMasterState && !docDataMatcherWrite(row.assumedMasterState as any))\n ) {\n return true;\n }\n });\n if (nonAllowedRow) {\n adapter.closeConnection(res, 403, 'Forbidden');\n return;\n }\n let hasInvalidChange = false;\n\n const currentStateDocsArray = await this.collection.storageInstance.findDocumentsById(ids, true);\n const currentStateDocs = new Map<string, RxDocumentData<RxDocType>>();\n currentStateDocsArray.forEach(d => currentStateDocs.set((d as any)[primaryPath], d));\n\n const useRows: typeof rows = rows.map((row) => {\n const id = (row.newDocumentState as any)[primaryPath];\n const isChangeValid = this.changeValidator(ensureNotFalsy(authData), {\n newDocumentState: removeServerOnlyFields(row.newDocumentState),\n assumedMasterState: removeServerOnlyFields(row.assumedMasterState)\n });\n if (!isChangeValid) {\n hasInvalidChange = true;\n }\n\n const serverDoc = currentStateDocs.get(id);\n return {\n newDocumentState: mergeServerDocumentFields(row.newDocumentState, serverDoc),\n assumedMasterState: mergeServerDocumentFields(row.assumedMasterState as any, serverDoc)\n } as typeof row;\n });\n if (hasInvalidChange) {\n adapter.closeConnection(res, 403, 'Forbidden');\n return;\n }\n\n const conflicts = await replicationHandler.masterWrite(useRows);\n\n adapter.setResponseHeader(res, 'Content-Type', 'application/json');\n adapter.endResponseJson(res, conflicts);\n });\n this.server.adapter.get(this.server.serverApp, '/' + this.urlPath + '/pullStream', async (req, res) => {\n\n const authData = await getAuthDataByRequest<AuthType, any, any>(this.server, req, res);\n if (!authData) { return; }\n\n adapter.setSSEHeaders(res);\n const docDataMatcherStream = getDocAllowedMatcher(this, ensureNotFalsy(authData));\n const subscription = replicationHandler.masterChangeStream$.pipe(\n mergeMap(async (changes) => {\n /**\n * The auth-data might be expired\n * so we re-run the auth parsing each time\n * before emitting an event.\n */\n let authData: RxServerAuthData<AuthType>;\n try {\n authData = await server.authHandler(adapter.getRequestHeaders(req));\n } catch (err) {\n adapter.closeConnection(res, 401, 'Unauthorized');\n return null;\n }\n\n if (changes === 'RESYNC') {\n return changes;\n } else {\n const useDocs = changes.documents.filter(d => docDataMatcherStream(d as any));\n return {\n documents: useDocs,\n checkpoint: changes.checkpoint\n };\n }\n }),\n filter(f => f !== null && (f === 'RESYNC' || f.documents.length > 0))\n ).subscribe(filteredAndModified => {\n if (filteredAndModified === 'RESYNC') {\n adapter.responseWrite(res, 'data: ' + JSON.stringify(filteredAndModified) + '\\n\\n');\n } else {\n const responseDocuments = ensureNotFalsy(filteredAndModified).documents.map(d => removeServerOnlyFields(d as any));\n adapter.responseWrite(\n res,\n 'data: ' + JSON.stringify({\n documents: responseDocuments,\n checkpoint: ensureNotFalsy(filteredAndModified).checkpoint\n }) + '\\n\\n'\n );\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\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 blockPreviousReplicationVersionPaths(\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 'pull',\n 'push',\n 'pullStream'\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-replication.js","names":["_core","require","_replicationWebsocket","_rxjs","_utils","_helper","RxServerReplicationEndpoint","exports","server","name","collection","queryModifier","changeValidator","serverOnlyFields","cors","type","adapter","setCors","join","blockPreviousReplicationVersionPaths","schema","version","urlPath","primaryPath","replicationHandler","getReplicationHandlerByCollection","database","authData","query","doesContainRegexQuerySelector","selector","Error","change","assumedMasterState","docContainsServerOnlyFields","newDocumentState","removeServerOnlyFields","removeServerOnlyFieldsMonad","mergeServerDocumentFields","mergeServerDocumentFieldsMonad","stripServerOnlyFields","stripServerOnlyFieldsMonad","get","serverApp","req","res","getAuthDataByRequest","urlQuery","getRequestQuery","id","lwt","parseFloat","limit","parseInt","plainQuery","getChangedDocumentsSinceQuery","storageInstance","useQueryChanges","ensureNotFalsy","prepared","prepareQuery","jsonSchema","result","newCheckpoint","documents","length","lastOfArray","_meta","responseDocuments","map","d","setResponseHeader","endResponseJson","checkpoint","post","docDataMatcherWrite","getDocAllowedMatcher","rows","getRequestBody","ids","forEach","row","push","nonAllowedRow","find","closeConnection","hasInvalidChange","currentStateDocsArray","findDocumentsById","currentStateDocs","Map","set","useRows","isChangeValid","serverDoc","conflicts","masterWrite","c","setSSEHeaders","docDataMatcherStream","subscription","masterChangeStream$","pipe","mergeMap","changes","authHandler","getRequestHeaders","err","useDocs","filter","f","subscribe","filteredAndModified","responseWrite","JSON","stringify","onRequestClose","unsubscribe","endResponse","path","currentVersion","v","_loop","subPath","all"],"sources":["../../../../src/plugins/server/endpoint-replication.ts"],"sourcesContent":["import {\n FilledMangoQuery,\n RxCollection,\n RxReplicationHandler,\n RxReplicationWriteToMasterRow,\n StringKeys,\n prepareQuery,\n getChangedDocumentsSinceQuery,\n RxDocumentData\n} from 'rxdb/plugins/core';\nimport { getReplicationHandlerByCollection } from 'rxdb/plugins/replication-websocket';\nimport type { RxServer } from './rx-server.ts';\nimport type {\n RxServerAuthData,\n RxServerChangeValidator,\n RxServerEndpoint,\n RxServerQueryModifier\n} from './types.ts';\nimport { filter, mergeMap } from 'rxjs';\nimport {\n ensureNotFalsy,\n lastOfArray\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';\nimport type { RxServerCheckpoint } from '../replication-server/types.ts';\n\nexport type RxReplicationEndpointMessageType = {\n id: string;\n method: StringKeys<RxReplicationHandler<any, any>> | 'auth';\n params: any[];\n};\n\nexport class RxServerReplicationEndpoint<ServerAppType, AuthType, RxDocType> implements RxServerEndpoint<AuthType, RxDocType> {\n readonly type = 'replication';\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 = this.server.adapter;\n\n setCors(this.server, [this.name].join('/'), cors);\n blockPreviousReplicationVersionPaths(this.server, [this.name].join('/'), collection.schema.version);\n\n this.urlPath = [this.name, collection.schema.version].join('/');\n\n const primaryPath = this.collection.schema.primaryPath;\n const replicationHandler: RxReplicationHandler<RxDocType, RxServerCheckpoint> = getReplicationHandlerByCollection<RxDocType>(this.server.database, collection.name);\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 (change.assumedMasterState && docContainsServerOnlyFields(serverOnlyFields, change.assumedMasterState)) ||\n docContainsServerOnlyFields(serverOnlyFields, change.newDocumentState)\n ) {\n return false;\n }\n return changeValidator(authData, change);\n }\n const removeServerOnlyFields = removeServerOnlyFieldsMonad<RxDocType>(this.serverOnlyFields);\n const mergeServerDocumentFields = mergeServerDocumentFieldsMonad<RxDocType>(this.serverOnlyFields);\n const stripServerOnlyFields = stripServerOnlyFieldsMonad<RxDocType>(this.serverOnlyFields);\n\n this.server.adapter.get(this.server.serverApp, '/' + this.urlPath + '/pull', async (req: any, res: any) => {\n const authData = await getAuthDataByRequest(this.server, req, res);\n if (!authData) { return; }\n\n const urlQuery = adapter.getRequestQuery(req);\n const id = urlQuery.id ? urlQuery.id as string : '';\n const lwt = urlQuery.lwt ? parseFloat(urlQuery.lwt as string) : 0;\n const limit = urlQuery.limit ? parseInt(urlQuery.limit as string, 10) : 1;\n const plainQuery = getChangedDocumentsSinceQuery<RxDocType, RxServerCheckpoint>(\n this.collection.storageInstance,\n limit,\n { id, lwt }\n );\n const useQueryChanges: FilledMangoQuery<RxDocType> = this.queryModifier(\n ensureNotFalsy(authData),\n plainQuery\n );\n const prepared = prepareQuery<RxDocType>(\n this.collection.schema.jsonSchema,\n useQueryChanges\n );\n const result = await this.collection.storageInstance.query(prepared);\n\n const newCheckpoint: RxServerCheckpoint = result.documents.length === 0 ? { id, lwt } : {\n id: ensureNotFalsy(lastOfArray(result.documents))[primaryPath] as string,\n lwt: ensureNotFalsy(lastOfArray(result.documents))._meta.lwt\n };\n const responseDocuments = result.documents.map(d => removeServerOnlyFields(d));\n adapter.setResponseHeader(res, 'Content-Type', 'application/json');\n adapter.endResponseJson(res, {\n documents: responseDocuments,\n checkpoint: newCheckpoint\n });\n });\n\n this.server.adapter.post(this.server.serverApp, '/' + this.urlPath + '/push', async (req: any, res: any) => {\n const authData = await getAuthDataByRequest(this.server, req, res);\n if (!authData) { return; }\n\n const docDataMatcherWrite = getDocAllowedMatcher(this, ensureNotFalsy(authData as any));\n const rows: RxReplicationWriteToMasterRow<RxDocType>[] = adapter.getRequestBody(req);\n const ids: string[] = [];\n rows.forEach(row => ids.push((row.newDocumentState as any)[primaryPath]));\n\n for (const row of rows) {\n // TODO remove this check\n if (row.assumedMasterState && (row.assumedMasterState as any)._meta) {\n throw new Error('body document contains meta!');\n }\n }\n\n // ensure all writes are allowed\n const nonAllowedRow = rows.find(row => {\n if (\n !docDataMatcherWrite(row.newDocumentState as any) ||\n (row.assumedMasterState && !docDataMatcherWrite(row.assumedMasterState as any))\n ) {\n return true;\n }\n });\n if (nonAllowedRow) {\n adapter.closeConnection(res, 403, 'Forbidden');\n return;\n }\n let hasInvalidChange = false;\n\n const currentStateDocsArray = await this.collection.storageInstance.findDocumentsById(ids, true);\n const currentStateDocs = new Map<string, RxDocumentData<RxDocType>>();\n currentStateDocsArray.forEach(d => currentStateDocs.set((d as any)[primaryPath], d));\n\n const useRows: typeof rows = rows.map((row) => {\n const id = (row.newDocumentState as any)[primaryPath];\n const isChangeValid = this.changeValidator(ensureNotFalsy(authData), {\n newDocumentState: removeServerOnlyFields(row.newDocumentState),\n assumedMasterState: removeServerOnlyFields(row.assumedMasterState)\n });\n if (!isChangeValid) {\n hasInvalidChange = true;\n }\n\n const serverDoc = currentStateDocs.get(id);\n // When the document does not yet exist on the server, strip\n // server-only fields from the new document state so a client\n // cannot populate them on insert. Updates of existing docs\n // are already protected because mergeServerDocumentFields\n // overwrites those fields with the stored server value.\n const newDocumentState = serverDoc\n ? mergeServerDocumentFields(row.newDocumentState, serverDoc)\n : stripServerOnlyFields(row.newDocumentState);\n return {\n newDocumentState,\n assumedMasterState: mergeServerDocumentFields(row.assumedMasterState as any, serverDoc)\n } as typeof row;\n });\n if (hasInvalidChange) {\n adapter.closeConnection(res, 403, 'Forbidden');\n return;\n }\n\n const conflicts = await replicationHandler.masterWrite(useRows);\n\n adapter.setResponseHeader(res, 'Content-Type', 'application/json');\n adapter.endResponseJson(res, conflicts.map(c => removeServerOnlyFields(c)));\n });\n this.server.adapter.get(this.server.serverApp, '/' + this.urlPath + '/pullStream', async (req, res) => {\n\n const authData = await getAuthDataByRequest<AuthType, any, any>(this.server, req, res);\n if (!authData) { return; }\n\n adapter.setSSEHeaders(res);\n const docDataMatcherStream = getDocAllowedMatcher(this, ensureNotFalsy(authData));\n const subscription = replicationHandler.masterChangeStream$.pipe(\n mergeMap(async (changes) => {\n /**\n * The auth-data might be expired\n * so we re-run the auth parsing each time\n * before emitting an event.\n */\n let authData: RxServerAuthData<AuthType>;\n try {\n authData = await server.authHandler(adapter.getRequestHeaders(req));\n } catch (err) {\n adapter.closeConnection(res, 401, 'Unauthorized');\n return null;\n }\n\n if (changes === 'RESYNC') {\n return changes;\n } else {\n const useDocs = changes.documents.filter(d => docDataMatcherStream(d as any));\n return {\n documents: useDocs,\n checkpoint: changes.checkpoint\n };\n }\n }),\n filter(f => f !== null && (f === 'RESYNC' || f.documents.length > 0))\n ).subscribe(filteredAndModified => {\n if (filteredAndModified === 'RESYNC') {\n adapter.responseWrite(res, 'data: ' + JSON.stringify(filteredAndModified) + '\\n\\n');\n } else {\n const responseDocuments = ensureNotFalsy(filteredAndModified).documents.map(d => removeServerOnlyFields(d as any));\n adapter.responseWrite(\n res,\n 'data: ' + JSON.stringify({\n documents: responseDocuments,\n checkpoint: ensureNotFalsy(filteredAndModified).checkpoint\n }) + '\\n\\n'\n );\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\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 blockPreviousReplicationVersionPaths(\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 'pull',\n 'push',\n 'pullStream'\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":[]}
@@ -43,6 +43,8 @@ var RxServerRestEndpoint = exports.RxServerRestEndpoint = function RxServerRestE
43
43
  return changeValidator(authData, change);
44
44
  };
45
45
  var removeServerOnlyFields = (0, _helper.removeServerOnlyFieldsMonad)(this.serverOnlyFields);
46
+ var mergeServerDocumentFields = (0, _helper.mergeServerDocumentFieldsMonad)(this.serverOnlyFields);
47
+ var stripServerOnlyFields = (0, _helper.stripServerOnlyFieldsMonad)(this.serverOnlyFields);
46
48
  this.server.adapter.post(this.server.serverApp, '/' + this.urlPath + '/query', async (req, res) => {
47
49
  (0, _utils.ensureNotFalsy)(adapter.getRequestBody(req), 'req body is empty');
48
50
  var authData = await (0, _helper.getAuthDataByRequest)(this.server, req, res);
@@ -74,8 +76,19 @@ var RxServerRestEndpoint = exports.RxServerRestEndpoint = function RxServerRestE
74
76
  if (!authData) {
75
77
  return;
76
78
  }
79
+
80
+ // Run the queryModifier BEFORE setSSEHeaders so that a bad
81
+ // request (e.g. a $regex query that the modifier wrapper
82
+ // rejects to prevent DOS attacks) can still be answered
83
+ // with a proper 400 response, mirroring the /query endpoint.
84
+ var useQuery;
85
+ try {
86
+ useQuery = this.queryModifier((0, _utils.ensureNotFalsy)(authData), (0, _core.normalizeMangoQuery)(this.collection.schema.jsonSchema, JSON.parse(atob(adapter.getRequestQuery(req).query))));
87
+ } catch (err) {
88
+ adapter.closeConnection(res, 400, 'Bad Request');
89
+ return;
90
+ }
77
91
  adapter.setSSEHeaders(res);
78
- var useQuery = this.queryModifier((0, _utils.ensureNotFalsy)(authData), (0, _core.normalizeMangoQuery)(this.collection.schema.jsonSchema, JSON.parse(atob(adapter.getRequestQuery(req).query))));
79
92
  var rxQuery = this.collection.find(useQuery);
80
93
  var subscription = rxQuery.$.pipe((0, _rxjs.mergeMap)(async result => {
81
94
  var resultData = result.map(doc => removeServerOnlyFields(doc.toJSON()));
@@ -150,15 +163,20 @@ var RxServerRestEndpoint = exports.RxServerRestEndpoint = function RxServerRestE
150
163
  var docs = await collection.findByIds(docsData.map(d => d[primaryPath])).exec();
151
164
  var useDocsData = docsData.slice();
152
165
  docsData = [];
153
- var _loop = async function (_docData) {
166
+ var _loop = async function () {
154
167
  var id = _docData[primaryPath];
155
168
  var doc = docs.get(id);
156
169
  if (!doc) {
157
- promises.push(_this.collection.insert(_docData).catch(err => onWriteError(err, _docData)));
158
- } else {
170
+ // Run the changeValidator for new document inserts as
171
+ // well. Previously the validator was only invoked for
172
+ // updates, so a changeValidator that rejected writes
173
+ // had no effect on inserts via /set, which is
174
+ // inconsistent with the replication endpoint and the
175
+ // documented contract that the validator gates all
176
+ // changes.
159
177
  var isAllowed = _this.changeValidator(authData, {
160
178
  newDocumentState: removeServerOnlyFields(_docData),
161
- assumedMasterState: removeServerOnlyFields(doc.toJSON(true))
179
+ assumedMasterState: undefined
162
180
  });
163
181
  if (!isAllowed) {
164
182
  adapter.closeConnection(res, 403, 'Forbidden');
@@ -166,12 +184,43 @@ var RxServerRestEndpoint = exports.RxServerRestEndpoint = function RxServerRestE
166
184
  v: void 0
167
185
  };
168
186
  }
169
- promises.push(doc.patch(_docData).catch(err => onWriteError(err, _docData)));
187
+ // Strip server-only fields from the client doc before
188
+ // inserting. Without this, a client could populate
189
+ // server-only ("readonly") fields when creating a new
190
+ // document via /set, which contradicts the documented
191
+ // behavior that clients cannot do writes where one of
192
+ // the serverOnlyFields is set.
193
+ var mergedDocData = stripServerOnlyFields(mergeServerDocumentFields(_docData, undefined));
194
+ promises.push(_this.collection.insert(mergedDocData).catch(err => onWriteError(err, mergedDocData)));
195
+ } else {
196
+ // The user must also be allowed to access the existing document.
197
+ // Without this check, a client could overwrite arbitrary
198
+ // documents by sending a write whose new state matches the
199
+ // queryModifier while targeting a foreign document's primary.
200
+ var isExistingDocAllowed = docDataMatcherWrite(doc.toJSON(true));
201
+ if (!isExistingDocAllowed) {
202
+ adapter.closeConnection(res, 403, 'Forbidden');
203
+ return {
204
+ v: void 0
205
+ };
206
+ }
207
+ var _isAllowed = _this.changeValidator(authData, {
208
+ newDocumentState: removeServerOnlyFields(_docData),
209
+ assumedMasterState: removeServerOnlyFields(doc.toJSON(true))
210
+ });
211
+ if (!_isAllowed) {
212
+ adapter.closeConnection(res, 403, 'Forbidden');
213
+ return {
214
+ v: void 0
215
+ };
216
+ }
217
+ var _mergedDocData = mergeServerDocumentFields(_docData, doc.toJSON(true));
218
+ promises.push(doc.patch(_mergedDocData).catch(err => onWriteError(err, _mergedDocData)));
170
219
  }
171
220
  },
172
221
  _ret;
173
222
  for (var _docData of useDocsData) {
174
- _ret = await _loop(_docData);
223
+ _ret = await _loop();
175
224
  if (_ret) return _ret.v;
176
225
  }
177
226
  await Promise.all(promises);
@@ -202,8 +251,8 @@ var RxServerRestEndpoint = exports.RxServerRestEndpoint = function RxServerRestE
202
251
  };
203
252
  }
204
253
  var isAllowedChange = _this.changeValidator(authData, {
205
- newDocumentState: doc.toJSON(true),
206
- assumedMasterState: doc.toJSON(true)
254
+ newDocumentState: removeServerOnlyFields(doc.toJSON(true)),
255
+ assumedMasterState: removeServerOnlyFields(doc.toJSON(true))
207
256
  });
208
257
  if (!isAllowedChange) {
209
258
  adapter.closeConnection(res, 403, 'Forbidden');