velocious 1.0.299 → 1.0.301

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 (59) hide show
  1. package/build/src/configuration-types.d.ts +1 -1
  2. package/build/src/configuration.d.ts +1 -1
  3. package/build/src/database/drivers/base.d.ts +29 -0
  4. package/build/src/database/drivers/base.d.ts.map +1 -1
  5. package/build/src/database/drivers/base.js +25 -1
  6. package/build/src/database/drivers/mssql/index.d.ts.map +1 -1
  7. package/build/src/database/drivers/mssql/index.js +10 -1
  8. package/build/src/database/drivers/mssql/sql/upsert.d.ts +5 -0
  9. package/build/src/database/drivers/mssql/sql/upsert.d.ts.map +1 -0
  10. package/build/src/database/drivers/mssql/sql/upsert.js +19 -0
  11. package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
  12. package/build/src/database/drivers/mysql/index.js +10 -1
  13. package/build/src/database/drivers/mysql/sql/upsert.d.ts +5 -0
  14. package/build/src/database/drivers/mysql/sql/upsert.d.ts.map +1 -0
  15. package/build/src/database/drivers/mysql/sql/upsert.js +11 -0
  16. package/build/src/database/drivers/pgsql/index.d.ts.map +1 -1
  17. package/build/src/database/drivers/pgsql/index.js +10 -1
  18. package/build/src/database/drivers/pgsql/sql/upsert.d.ts +5 -0
  19. package/build/src/database/drivers/pgsql/sql/upsert.d.ts.map +1 -0
  20. package/build/src/database/drivers/pgsql/sql/upsert.js +12 -0
  21. package/build/src/database/drivers/sqlite/base.d.ts.map +1 -1
  22. package/build/src/database/drivers/sqlite/base.js +7 -1
  23. package/build/src/database/drivers/sqlite/sql/upsert.d.ts +5 -0
  24. package/build/src/database/drivers/sqlite/sql/upsert.d.ts.map +1 -0
  25. package/build/src/database/drivers/sqlite/sql/upsert.js +12 -0
  26. package/build/src/database/query/upsert-base.d.ts +59 -0
  27. package/build/src/database/query/upsert-base.d.ts.map +1 -0
  28. package/build/src/database/query/upsert-base.js +87 -0
  29. package/build/src/frontend-model-controller.d.ts.map +1 -1
  30. package/build/src/frontend-model-controller.js +8 -3
  31. package/build/src/frontend-models/websocket-channel.d.ts +9 -1
  32. package/build/src/frontend-models/websocket-channel.d.ts.map +1 -1
  33. package/build/src/frontend-models/websocket-channel.js +17 -3
  34. package/build/src/http-client/websocket-client.d.ts +8 -6
  35. package/build/src/http-client/websocket-client.d.ts.map +1 -1
  36. package/build/src/http-client/websocket-client.js +17 -8
  37. package/build/src/http-server/client/websocket-session.d.ts +62 -5
  38. package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
  39. package/build/src/http-server/client/websocket-session.js +129 -14
  40. package/build/src/http-server/websocket-channel.d.ts +18 -3
  41. package/build/src/http-server/websocket-channel.d.ts.map +1 -1
  42. package/build/src/http-server/websocket-channel.js +18 -6
  43. package/build/src/http-server/websocket-event-log-store.d.ts +198 -0
  44. package/build/src/http-server/websocket-event-log-store.d.ts.map +1 -0
  45. package/build/src/http-server/websocket-event-log-store.js +308 -0
  46. package/build/src/http-server/websocket-events-host.d.ts +14 -0
  47. package/build/src/http-server/websocket-events-host.d.ts.map +1 -1
  48. package/build/src/http-server/websocket-events-host.js +38 -4
  49. package/build/src/http-server/worker-handler/in-process.d.ts +5 -1
  50. package/build/src/http-server/worker-handler/in-process.d.ts.map +1 -1
  51. package/build/src/http-server/worker-handler/in-process.js +10 -3
  52. package/build/src/http-server/worker-handler/index.d.ts +5 -1
  53. package/build/src/http-server/worker-handler/index.d.ts.map +1 -1
  54. package/build/src/http-server/worker-handler/index.js +5 -3
  55. package/build/src/http-server/worker-handler/worker-thread.d.ts +9 -1
  56. package/build/src/http-server/worker-handler/worker-thread.d.ts.map +1 -1
  57. package/build/src/http-server/worker-handler/worker-thread.js +16 -6
  58. package/build/tsconfig.tsbuildinfo +1 -1
  59. package/package.json +1 -1
@@ -5,13 +5,17 @@ export default class VelociousHttpServerWebsocketChannel {
5
5
  * @param {import("../configuration.js").default} args.configuration - Configuration instance.
6
6
  * @param {import("./client/request.js").default | import("./client/websocket-request.js").default | undefined} args.request - Request instance.
7
7
  * @param {import("./client/index.js").default} args.client - Client instance.
8
+ * @param {string} [args.lastEventId] - Last received event id.
9
+ * @param {string} [args.subscriptionChannel] - Client-facing subscription channel.
8
10
  * @param {import("./client/websocket-session.js").default} args.websocketSession - Websocket session.
9
11
  * @param {Record<string, unknown>} [args.subscriptionParams] - Params from subscribe message.
10
12
  */
11
- constructor({ configuration, request, client, websocketSession, subscriptionParams }) {
13
+ constructor({ configuration, request, client, lastEventId, subscriptionChannel, websocketSession, subscriptionParams }) {
12
14
  this.configuration = configuration;
13
15
  this.request = request;
14
16
  this.client = client;
17
+ this.lastEventId = lastEventId;
18
+ this.subscriptionChannel = subscriptionChannel;
15
19
  this.websocketSession = websocketSession;
16
20
  this.subscriptionParams = subscriptionParams;
17
21
  this._params = this._buildParams();
@@ -33,24 +37,32 @@ export default class VelociousHttpServerWebsocketChannel {
33
37
  /**
34
38
  * Subscribe this connection to a broadcast channel.
35
39
  * @param {string} channel - Channel name.
36
- * @param {{acknowledge?: boolean}} [options] - Subscription options.
40
+ * @param {{acknowledge?: boolean, lastEventId?: string}} [options] - Subscription options.
37
41
  * @returns {Promise<boolean>} - Whether the subscription succeeded.
38
42
  */
39
43
  async streamFrom(channel, options = {}) {
44
+ const lastEventId = options.lastEventId ?? this.lastEventId;
40
45
  return await this.websocketSession.subscribeToChannel(channel, {
41
46
  acknowledge: options.acknowledge ?? true,
42
- channelHandler: this
47
+ channelHandler: this,
48
+ lastEventId,
49
+ params: this.subscriptionParams,
50
+ subscriptionChannel: this.subscriptionChannel
43
51
  });
44
52
  }
45
53
  /**
46
54
  * Called when a broadcast event is delivered for one of this channel instance's subscriptions.
47
55
  * @param {object} args - Event args.
48
56
  * @param {string} args.channel - Broadcast channel name.
57
+ * @param {string} [args.createdAt] - Event creation timestamp.
58
+ * @param {string} [args.eventId] - Event id.
49
59
  * @param {any} args.payload - Broadcast payload.
60
+ * @param {boolean} [args.replayed] - Whether this event was replayed.
61
+ * @param {number} [args.sequence] - Event sequence.
50
62
  * @returns {Promise<void>} - Resolves when complete.
51
63
  */
52
- async receivedBroadcast({ channel, payload }) {
53
- this.websocketSession.sendJson({ channel, payload, type: "event" });
64
+ async receivedBroadcast({ channel, createdAt, eventId, payload, replayed, sequence }) {
65
+ this.websocketSession.sendJson({ channel, createdAt, eventId, payload, replayed, sequence, type: "event" });
54
66
  }
55
67
  /**
56
68
  * @returns {Record<string, unknown>} - Parsed params.
@@ -80,4 +92,4 @@ export default class VelociousHttpServerWebsocketChannel {
80
92
  return params;
81
93
  }
82
94
  }
83
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2Vic29ja2V0LWNoYW5uZWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvaHR0cC1zZXJ2ZXIvd2Vic29ja2V0LWNoYW5uZWwuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE1BQU0sQ0FBQyxPQUFPLE9BQU8sbUNBQW1DO0lBQ3REOzs7Ozs7O09BT0c7SUFDSCxZQUFZLEVBQUMsYUFBYSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsZ0JBQWdCLEVBQUUsa0JBQWtCLEVBQUM7UUFDaEYsSUFBSSxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUE7UUFDbEMsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUE7UUFDdEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUE7UUFDcEIsSUFBSSxDQUFDLGdCQUFnQixHQUFHLGdCQUFnQixDQUFBO1FBQ3hDLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxrQkFBa0IsQ0FBQTtRQUM1QyxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQTtJQUNwQyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNLEtBQUssT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFBLENBQUMsQ0FBQztJQUVoQzs7O09BR0c7SUFDSCxLQUFLLENBQUMsVUFBVSxLQUFJLENBQUM7SUFFckI7OztPQUdHO0lBQ0gsS0FBSyxDQUFDLFlBQVksS0FBSSxDQUFDO0lBRXZCOzs7OztPQUtHO0lBQ0gsS0FBSyxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsT0FBTyxHQUFHLEVBQUU7UUFDcEMsT0FBTyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxrQkFBa0IsQ0FBQyxPQUFPLEVBQUU7WUFDN0QsV0FBVyxFQUFFLE9BQU8sQ0FBQyxXQUFXLElBQUksSUFBSTtZQUN4QyxjQUFjLEVBQUUsSUFBSTtTQUNyQixDQUFDLENBQUE7SUFDSixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsS0FBSyxDQUFDLGlCQUFpQixDQUFDLEVBQUMsT0FBTyxFQUFFLE9BQU8sRUFBQztRQUN4QyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxDQUFDLEVBQUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFDLENBQUMsQ0FBQTtJQUNuRSxDQUFDO0lBRUQ7O09BRUc7SUFDSCxZQUFZO1FBQ1Ysc0NBQXNDO1FBQ3RDLE1BQU0sTUFBTSxHQUFHLEVBQUUsQ0FBQTtRQUVqQixJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLENBQUM7WUFDekIsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsQ0FBQTtZQUUzQyxJQUFJLGFBQWEsSUFBSSxPQUFPLGFBQWEsS0FBSyxRQUFRLEVBQUUsQ0FBQztnQkFDdkQsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsYUFBYSxDQUFDLENBQUE7WUFDdEMsQ0FBQztRQUNILENBQUM7UUFFRCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksRUFBRSxFQUFFLENBQUE7UUFDeEMsTUFBTSxLQUFLLEdBQUcsU0FBUyxFQUFFLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQTtRQUV0QyxJQUFJLEtBQUssRUFBRSxDQUFDO1lBQ1YsTUFBTSxZQUFZLEdBQUcsSUFBSSxlQUFlLENBQUMsS0FBSyxDQUFDLENBQUE7WUFFL0MsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxJQUFJLFlBQVksQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDO2dCQUNsRCxJQUFJLE1BQU0sQ0FBQyxHQUFHLENBQUMsS0FBSyxTQUFTLEVBQUUsQ0FBQztvQkFDOUIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEtBQUssQ0FBQTtnQkFDckIsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO1FBRUQsSUFBSSxJQUFJLENBQUMsa0JBQWtCLElBQUksT0FBTyxJQUFJLENBQUMsa0JBQWtCLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDM0UsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLGtCQUFrQixDQUFDLENBQUE7UUFDaEQsQ0FBQztRQUVELE9BQU8sTUFBTSxDQUFBO0lBQ2YsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHRzLWNoZWNrXG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFZlbG9jaW91c0h0dHBTZXJ2ZXJXZWJzb2NrZXRDaGFubmVsIHtcbiAgLyoqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucyBvYmplY3QuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi5qc1wiKS5kZWZhdWx0fSBhcmdzLmNvbmZpZ3VyYXRpb24gLSBDb25maWd1cmF0aW9uIGluc3RhbmNlLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vY2xpZW50L3JlcXVlc3QuanNcIikuZGVmYXVsdCB8IGltcG9ydChcIi4vY2xpZW50L3dlYnNvY2tldC1yZXF1ZXN0LmpzXCIpLmRlZmF1bHQgfCB1bmRlZmluZWR9IGFyZ3MucmVxdWVzdCAtIFJlcXVlc3QgaW5zdGFuY2UuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi9jbGllbnQvaW5kZXguanNcIikuZGVmYXVsdH0gYXJncy5jbGllbnQgLSBDbGllbnQgaW5zdGFuY2UuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi9jbGllbnQvd2Vic29ja2V0LXNlc3Npb24uanNcIikuZGVmYXVsdH0gYXJncy53ZWJzb2NrZXRTZXNzaW9uIC0gV2Vic29ja2V0IHNlc3Npb24uXG4gICAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgdW5rbm93bj59IFthcmdzLnN1YnNjcmlwdGlvblBhcmFtc10gLSBQYXJhbXMgZnJvbSBzdWJzY3JpYmUgbWVzc2FnZS5cbiAgICovXG4gIGNvbnN0cnVjdG9yKHtjb25maWd1cmF0aW9uLCByZXF1ZXN0LCBjbGllbnQsIHdlYnNvY2tldFNlc3Npb24sIHN1YnNjcmlwdGlvblBhcmFtc30pIHtcbiAgICB0aGlzLmNvbmZpZ3VyYXRpb24gPSBjb25maWd1cmF0aW9uXG4gICAgdGhpcy5yZXF1ZXN0ID0gcmVxdWVzdFxuICAgIHRoaXMuY2xpZW50ID0gY2xpZW50XG4gICAgdGhpcy53ZWJzb2NrZXRTZXNzaW9uID0gd2Vic29ja2V0U2Vzc2lvblxuICAgIHRoaXMuc3Vic2NyaXB0aW9uUGFyYW1zID0gc3Vic2NyaXB0aW9uUGFyYW1zXG4gICAgdGhpcy5fcGFyYW1zID0gdGhpcy5fYnVpbGRQYXJhbXMoKVxuICB9XG5cbiAgLyoqXG4gICAqIEByZXR1cm5zIHtSZWNvcmQ8c3RyaW5nLCB1bmtub3duPn0gLSBQYXJhbXMgZm9yIHRoZSB3ZWJzb2NrZXQgY29ubmVjdGlvbi5cbiAgICovXG4gIHBhcmFtcygpIHsgcmV0dXJuIHRoaXMuX3BhcmFtcyB9XG5cbiAgLyoqXG4gICAqIENhbGxlZCB3aGVuIHRoZSBjaGFubmVsIGlzIGNyZWF0ZWQgZm9yIGEgd2Vic29ja2V0IGNvbm5lY3Rpb24uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIHdoZW4gY29tcGxldGUuXG4gICAqL1xuICBhc3luYyBzdWJzY3JpYmVkKCkge31cblxuICAvKipcbiAgICogQ2FsbGVkIHdoZW4gdGhlIHdlYnNvY2tldCBkaXNjb25uZWN0cy5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiBjb21wbGV0ZS5cbiAgICovXG4gIGFzeW5jIHVuc3Vic2NyaWJlZCgpIHt9XG5cbiAgLyoqXG4gICAqIFN1YnNjcmliZSB0aGlzIGNvbm5lY3Rpb24gdG8gYSBicm9hZGNhc3QgY2hhbm5lbC5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGNoYW5uZWwgLSBDaGFubmVsIG5hbWUuXG4gICAqIEBwYXJhbSB7e2Fja25vd2xlZGdlPzogYm9vbGVhbn19IFtvcHRpb25zXSAtIFN1YnNjcmlwdGlvbiBvcHRpb25zLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gLSBXaGV0aGVyIHRoZSBzdWJzY3JpcHRpb24gc3VjY2VlZGVkLlxuICAgKi9cbiAgYXN5bmMgc3RyZWFtRnJvbShjaGFubmVsLCBvcHRpb25zID0ge30pIHtcbiAgICByZXR1cm4gYXdhaXQgdGhpcy53ZWJzb2NrZXRTZXNzaW9uLnN1YnNjcmliZVRvQ2hhbm5lbChjaGFubmVsLCB7XG4gICAgICBhY2tub3dsZWRnZTogb3B0aW9ucy5hY2tub3dsZWRnZSA/PyB0cnVlLFxuICAgICAgY2hhbm5lbEhhbmRsZXI6IHRoaXNcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIENhbGxlZCB3aGVuIGEgYnJvYWRjYXN0IGV2ZW50IGlzIGRlbGl2ZXJlZCBmb3Igb25lIG9mIHRoaXMgY2hhbm5lbCBpbnN0YW5jZSdzIHN1YnNjcmlwdGlvbnMuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gRXZlbnQgYXJncy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MuY2hhbm5lbCAtIEJyb2FkY2FzdCBjaGFubmVsIG5hbWUuXG4gICAqIEBwYXJhbSB7YW55fSBhcmdzLnBheWxvYWQgLSBCcm9hZGNhc3QgcGF5bG9hZC5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiBjb21wbGV0ZS5cbiAgICovXG4gIGFzeW5jIHJlY2VpdmVkQnJvYWRjYXN0KHtjaGFubmVsLCBwYXlsb2FkfSkge1xuICAgIHRoaXMud2Vic29ja2V0U2Vzc2lvbi5zZW5kSnNvbih7Y2hhbm5lbCwgcGF5bG9hZCwgdHlwZTogXCJldmVudFwifSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcmV0dXJucyB7UmVjb3JkPHN0cmluZywgdW5rbm93bj59IC0gUGFyc2VkIHBhcmFtcy5cbiAgICovXG4gIF9idWlsZFBhcmFtcygpIHtcbiAgICAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSAqL1xuICAgIGNvbnN0IHBhcmFtcyA9IHt9XG5cbiAgICBpZiAodGhpcy5yZXF1ZXN0Py5wYXJhbXMpIHtcbiAgICAgIGNvbnN0IHJlcXVlc3RQYXJhbXMgPSB0aGlzLnJlcXVlc3QucGFyYW1zKClcblxuICAgICAgaWYgKHJlcXVlc3RQYXJhbXMgJiYgdHlwZW9mIHJlcXVlc3RQYXJhbXMgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgICAgT2JqZWN0LmFzc2lnbihwYXJhbXMsIHJlcXVlc3RQYXJhbXMpXG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgcGF0aFZhbHVlID0gdGhpcy5yZXF1ZXN0Py5wYXRoPy4oKVxuICAgIGNvbnN0IHF1ZXJ5ID0gcGF0aFZhbHVlPy5zcGxpdChcIj9cIilbMV1cblxuICAgIGlmIChxdWVyeSkge1xuICAgICAgY29uc3Qgc2VhcmNoUGFyYW1zID0gbmV3IFVSTFNlYXJjaFBhcmFtcyhxdWVyeSlcblxuICAgICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2Ygc2VhcmNoUGFyYW1zLmVudHJpZXMoKSkge1xuICAgICAgICBpZiAocGFyYW1zW2tleV0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIHBhcmFtc1trZXldID0gdmFsdWVcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIGlmICh0aGlzLnN1YnNjcmlwdGlvblBhcmFtcyAmJiB0eXBlb2YgdGhpcy5zdWJzY3JpcHRpb25QYXJhbXMgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgIE9iamVjdC5hc3NpZ24ocGFyYW1zLCB0aGlzLnN1YnNjcmlwdGlvblBhcmFtcylcbiAgICB9XG5cbiAgICByZXR1cm4gcGFyYW1zXG4gIH1cbn1cbiJdfQ==
95
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2Vic29ja2V0LWNoYW5uZWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvaHR0cC1zZXJ2ZXIvd2Vic29ja2V0LWNoYW5uZWwuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE1BQU0sQ0FBQyxPQUFPLE9BQU8sbUNBQW1DO0lBQ3REOzs7Ozs7Ozs7T0FTRztJQUNILFlBQVksRUFBQyxhQUFhLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsbUJBQW1CLEVBQUUsZ0JBQWdCLEVBQUUsa0JBQWtCLEVBQUM7UUFDbEgsSUFBSSxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUE7UUFDbEMsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUE7UUFDdEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUE7UUFDcEIsSUFBSSxDQUFDLFdBQVcsR0FBRyxXQUFXLENBQUE7UUFDOUIsSUFBSSxDQUFDLG1CQUFtQixHQUFHLG1CQUFtQixDQUFBO1FBQzlDLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxnQkFBZ0IsQ0FBQTtRQUN4QyxJQUFJLENBQUMsa0JBQWtCLEdBQUcsa0JBQWtCLENBQUE7UUFDNUMsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUE7SUFDcEMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsTUFBTSxLQUFLLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQSxDQUFDLENBQUM7SUFFaEM7OztPQUdHO0lBQ0gsS0FBSyxDQUFDLFVBQVUsS0FBSSxDQUFDO0lBRXJCOzs7T0FHRztJQUNILEtBQUssQ0FBQyxZQUFZLEtBQUksQ0FBQztJQUV2Qjs7Ozs7T0FLRztJQUNILEtBQUssQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLE9BQU8sR0FBRyxFQUFFO1FBQ3BDLE1BQU0sV0FBVyxHQUFHLE9BQU8sQ0FBQyxXQUFXLElBQUksSUFBSSxDQUFDLFdBQVcsQ0FBQTtRQUUzRCxPQUFPLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFDLGtCQUFrQixDQUFDLE9BQU8sRUFBRTtZQUM3RCxXQUFXLEVBQUUsT0FBTyxDQUFDLFdBQVcsSUFBSSxJQUFJO1lBQ3hDLGNBQWMsRUFBRSxJQUFJO1lBQ3BCLFdBQVc7WUFDWCxNQUFNLEVBQUUsSUFBSSxDQUFDLGtCQUFrQjtZQUMvQixtQkFBbUIsRUFBRSxJQUFJLENBQUMsbUJBQW1CO1NBQzlDLENBQUMsQ0FBQTtJQUNKLENBQUM7SUFFRDs7Ozs7Ozs7OztPQVVHO0lBQ0gsS0FBSyxDQUFDLGlCQUFpQixDQUFDLEVBQUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUM7UUFDaEYsSUFBSSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsQ0FBQyxFQUFDLE9BQU8sRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUMsQ0FBQyxDQUFBO0lBQzNHLENBQUM7SUFFRDs7T0FFRztJQUNILFlBQVk7UUFDVixzQ0FBc0M7UUFDdEMsTUFBTSxNQUFNLEdBQUcsRUFBRSxDQUFBO1FBRWpCLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsQ0FBQztZQUN6QixNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxDQUFBO1lBRTNDLElBQUksYUFBYSxJQUFJLE9BQU8sYUFBYSxLQUFLLFFBQVEsRUFBRSxDQUFDO2dCQUN2RCxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxhQUFhLENBQUMsQ0FBQTtZQUN0QyxDQUFDO1FBQ0gsQ0FBQztRQUVELE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxFQUFFLEVBQUUsQ0FBQTtRQUN4QyxNQUFNLEtBQUssR0FBRyxTQUFTLEVBQUUsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFBO1FBRXRDLElBQUksS0FBSyxFQUFFLENBQUM7WUFDVixNQUFNLFlBQVksR0FBRyxJQUFJLGVBQWUsQ0FBQyxLQUFLLENBQUMsQ0FBQTtZQUUvQyxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLElBQUksWUFBWSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUM7Z0JBQ2xELElBQUksTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLFNBQVMsRUFBRSxDQUFDO29CQUM5QixNQUFNLENBQUMsR0FBRyxDQUFDLEdBQUcsS0FBSyxDQUFBO2dCQUNyQixDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFFRCxJQUFJLElBQUksQ0FBQyxrQkFBa0IsSUFBSSxPQUFPLElBQUksQ0FBQyxrQkFBa0IsS0FBSyxRQUFRLEVBQUUsQ0FBQztZQUMzRSxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsa0JBQWtCLENBQUMsQ0FBQTtRQUNoRCxDQUFDO1FBRUQsT0FBTyxNQUFNLENBQUE7SUFDZixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgVmVsb2Npb3VzSHR0cFNlcnZlcldlYnNvY2tldENoYW5uZWwge1xuICAvKipcbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zIG9iamVjdC5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLmpzXCIpLmRlZmF1bHR9IGFyZ3MuY29uZmlndXJhdGlvbiAtIENvbmZpZ3VyYXRpb24gaW5zdGFuY2UuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi9jbGllbnQvcmVxdWVzdC5qc1wiKS5kZWZhdWx0IHwgaW1wb3J0KFwiLi9jbGllbnQvd2Vic29ja2V0LXJlcXVlc3QuanNcIikuZGVmYXVsdCB8IHVuZGVmaW5lZH0gYXJncy5yZXF1ZXN0IC0gUmVxdWVzdCBpbnN0YW5jZS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL2NsaWVudC9pbmRleC5qc1wiKS5kZWZhdWx0fSBhcmdzLmNsaWVudCAtIENsaWVudCBpbnN0YW5jZS5cbiAgICogQHBhcmFtIHtzdHJpbmd9IFthcmdzLmxhc3RFdmVudElkXSAtIExhc3QgcmVjZWl2ZWQgZXZlbnQgaWQuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbYXJncy5zdWJzY3JpcHRpb25DaGFubmVsXSAtIENsaWVudC1mYWNpbmcgc3Vic2NyaXB0aW9uIGNoYW5uZWwuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi9jbGllbnQvd2Vic29ja2V0LXNlc3Npb24uanNcIikuZGVmYXVsdH0gYXJncy53ZWJzb2NrZXRTZXNzaW9uIC0gV2Vic29ja2V0IHNlc3Npb24uXG4gICAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgdW5rbm93bj59IFthcmdzLnN1YnNjcmlwdGlvblBhcmFtc10gLSBQYXJhbXMgZnJvbSBzdWJzY3JpYmUgbWVzc2FnZS5cbiAgICovXG4gIGNvbnN0cnVjdG9yKHtjb25maWd1cmF0aW9uLCByZXF1ZXN0LCBjbGllbnQsIGxhc3RFdmVudElkLCBzdWJzY3JpcHRpb25DaGFubmVsLCB3ZWJzb2NrZXRTZXNzaW9uLCBzdWJzY3JpcHRpb25QYXJhbXN9KSB7XG4gICAgdGhpcy5jb25maWd1cmF0aW9uID0gY29uZmlndXJhdGlvblxuICAgIHRoaXMucmVxdWVzdCA9IHJlcXVlc3RcbiAgICB0aGlzLmNsaWVudCA9IGNsaWVudFxuICAgIHRoaXMubGFzdEV2ZW50SWQgPSBsYXN0RXZlbnRJZFxuICAgIHRoaXMuc3Vic2NyaXB0aW9uQ2hhbm5lbCA9IHN1YnNjcmlwdGlvbkNoYW5uZWxcbiAgICB0aGlzLndlYnNvY2tldFNlc3Npb24gPSB3ZWJzb2NrZXRTZXNzaW9uXG4gICAgdGhpcy5zdWJzY3JpcHRpb25QYXJhbXMgPSBzdWJzY3JpcHRpb25QYXJhbXNcbiAgICB0aGlzLl9wYXJhbXMgPSB0aGlzLl9idWlsZFBhcmFtcygpXG4gIH1cblxuICAvKipcbiAgICogQHJldHVybnMge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSAtIFBhcmFtcyBmb3IgdGhlIHdlYnNvY2tldCBjb25uZWN0aW9uLlxuICAgKi9cbiAgcGFyYW1zKCkgeyByZXR1cm4gdGhpcy5fcGFyYW1zIH1cblxuICAvKipcbiAgICogQ2FsbGVkIHdoZW4gdGhlIGNoYW5uZWwgaXMgY3JlYXRlZCBmb3IgYSB3ZWJzb2NrZXQgY29ubmVjdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiBjb21wbGV0ZS5cbiAgICovXG4gIGFzeW5jIHN1YnNjcmliZWQoKSB7fVxuXG4gIC8qKlxuICAgKiBDYWxsZWQgd2hlbiB0aGUgd2Vic29ja2V0IGRpc2Nvbm5lY3RzLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIGNvbXBsZXRlLlxuICAgKi9cbiAgYXN5bmMgdW5zdWJzY3JpYmVkKCkge31cblxuICAvKipcbiAgICogU3Vic2NyaWJlIHRoaXMgY29ubmVjdGlvbiB0byBhIGJyb2FkY2FzdCBjaGFubmVsLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gY2hhbm5lbCAtIENoYW5uZWwgbmFtZS5cbiAgICogQHBhcmFtIHt7YWNrbm93bGVkZ2U/OiBib29sZWFuLCBsYXN0RXZlbnRJZD86IHN0cmluZ319IFtvcHRpb25zXSAtIFN1YnNjcmlwdGlvbiBvcHRpb25zLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gLSBXaGV0aGVyIHRoZSBzdWJzY3JpcHRpb24gc3VjY2VlZGVkLlxuICAgKi9cbiAgYXN5bmMgc3RyZWFtRnJvbShjaGFubmVsLCBvcHRpb25zID0ge30pIHtcbiAgICBjb25zdCBsYXN0RXZlbnRJZCA9IG9wdGlvbnMubGFzdEV2ZW50SWQgPz8gdGhpcy5sYXN0RXZlbnRJZFxuXG4gICAgcmV0dXJuIGF3YWl0IHRoaXMud2Vic29ja2V0U2Vzc2lvbi5zdWJzY3JpYmVUb0NoYW5uZWwoY2hhbm5lbCwge1xuICAgICAgYWNrbm93bGVkZ2U6IG9wdGlvbnMuYWNrbm93bGVkZ2UgPz8gdHJ1ZSxcbiAgICAgIGNoYW5uZWxIYW5kbGVyOiB0aGlzLFxuICAgICAgbGFzdEV2ZW50SWQsXG4gICAgICBwYXJhbXM6IHRoaXMuc3Vic2NyaXB0aW9uUGFyYW1zLFxuICAgICAgc3Vic2NyaXB0aW9uQ2hhbm5lbDogdGhpcy5zdWJzY3JpcHRpb25DaGFubmVsXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBDYWxsZWQgd2hlbiBhIGJyb2FkY2FzdCBldmVudCBpcyBkZWxpdmVyZWQgZm9yIG9uZSBvZiB0aGlzIGNoYW5uZWwgaW5zdGFuY2UncyBzdWJzY3JpcHRpb25zLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIEV2ZW50IGFyZ3MuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmNoYW5uZWwgLSBCcm9hZGNhc3QgY2hhbm5lbCBuYW1lLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2FyZ3MuY3JlYXRlZEF0XSAtIEV2ZW50IGNyZWF0aW9uIHRpbWVzdGFtcC5cbiAgICogQHBhcmFtIHtzdHJpbmd9IFthcmdzLmV2ZW50SWRdIC0gRXZlbnQgaWQuXG4gICAqIEBwYXJhbSB7YW55fSBhcmdzLnBheWxvYWQgLSBCcm9hZGNhc3QgcGF5bG9hZC5cbiAgICogQHBhcmFtIHtib29sZWFufSBbYXJncy5yZXBsYXllZF0gLSBXaGV0aGVyIHRoaXMgZXZlbnQgd2FzIHJlcGxheWVkLlxuICAgKiBAcGFyYW0ge251bWJlcn0gW2FyZ3Muc2VxdWVuY2VdIC0gRXZlbnQgc2VxdWVuY2UuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIHdoZW4gY29tcGxldGUuXG4gICAqL1xuICBhc3luYyByZWNlaXZlZEJyb2FkY2FzdCh7Y2hhbm5lbCwgY3JlYXRlZEF0LCBldmVudElkLCBwYXlsb2FkLCByZXBsYXllZCwgc2VxdWVuY2V9KSB7XG4gICAgdGhpcy53ZWJzb2NrZXRTZXNzaW9uLnNlbmRKc29uKHtjaGFubmVsLCBjcmVhdGVkQXQsIGV2ZW50SWQsIHBheWxvYWQsIHJlcGxheWVkLCBzZXF1ZW5jZSwgdHlwZTogXCJldmVudFwifSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcmV0dXJucyB7UmVjb3JkPHN0cmluZywgdW5rbm93bj59IC0gUGFyc2VkIHBhcmFtcy5cbiAgICovXG4gIF9idWlsZFBhcmFtcygpIHtcbiAgICAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSAqL1xuICAgIGNvbnN0IHBhcmFtcyA9IHt9XG5cbiAgICBpZiAodGhpcy5yZXF1ZXN0Py5wYXJhbXMpIHtcbiAgICAgIGNvbnN0IHJlcXVlc3RQYXJhbXMgPSB0aGlzLnJlcXVlc3QucGFyYW1zKClcblxuICAgICAgaWYgKHJlcXVlc3RQYXJhbXMgJiYgdHlwZW9mIHJlcXVlc3RQYXJhbXMgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgICAgT2JqZWN0LmFzc2lnbihwYXJhbXMsIHJlcXVlc3RQYXJhbXMpXG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgcGF0aFZhbHVlID0gdGhpcy5yZXF1ZXN0Py5wYXRoPy4oKVxuICAgIGNvbnN0IHF1ZXJ5ID0gcGF0aFZhbHVlPy5zcGxpdChcIj9cIilbMV1cblxuICAgIGlmIChxdWVyeSkge1xuICAgICAgY29uc3Qgc2VhcmNoUGFyYW1zID0gbmV3IFVSTFNlYXJjaFBhcmFtcyhxdWVyeSlcblxuICAgICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2Ygc2VhcmNoUGFyYW1zLmVudHJpZXMoKSkge1xuICAgICAgICBpZiAocGFyYW1zW2tleV0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIHBhcmFtc1trZXldID0gdmFsdWVcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIGlmICh0aGlzLnN1YnNjcmlwdGlvblBhcmFtcyAmJiB0eXBlb2YgdGhpcy5zdWJzY3JpcHRpb25QYXJhbXMgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgIE9iamVjdC5hc3NpZ24ocGFyYW1zLCB0aGlzLnN1YnNjcmlwdGlvblBhcmFtcylcbiAgICB9XG5cbiAgICByZXR1cm4gcGFyYW1zXG4gIH1cbn1cbiJdfQ==
@@ -0,0 +1,198 @@
1
+ /**
2
+ * @typedef {object} WebsocketEventRow
3
+ * @property {string} channel - Channel name.
4
+ * @property {Date | string} created_at - Creation time.
5
+ * @property {string} id - Event id.
6
+ * @property {string} payload_json - Serialized payload.
7
+ * @property {number | string} sequence - Sequence number.
8
+ */
9
+ /**
10
+ * @typedef {object} WebsocketReplayChannelRow
11
+ * @property {string} channel - Channel name.
12
+ */
13
+ /**
14
+ * @param {import("../configuration.js").default} configuration - Configuration.
15
+ * @returns {VelociousHttpServerWebsocketEventLogStore} - Shared store instance.
16
+ */
17
+ export function websocketEventLogStoreForConfiguration(configuration: import("../configuration.js").default): VelociousHttpServerWebsocketEventLogStore;
18
+ export default class VelociousHttpServerWebsocketEventLogStore {
19
+ /**
20
+ * @param {object} args - Options.
21
+ * @param {import("../configuration.js").default} args.configuration - Configuration.
22
+ * @param {string} [args.databaseIdentifier] - Database identifier.
23
+ * @param {number} [args.retentionMs] - Event retention in milliseconds.
24
+ */
25
+ constructor({ configuration, databaseIdentifier, retentionMs }: {
26
+ configuration: import("../configuration.js").default;
27
+ databaseIdentifier?: string | undefined;
28
+ retentionMs?: number | undefined;
29
+ });
30
+ configuration: import("../configuration.js").default;
31
+ databaseIdentifier: string;
32
+ retentionMs: number;
33
+ logger: Logger;
34
+ _isReady: boolean;
35
+ _readyPromise: Promise<void> | null;
36
+ /**
37
+ * @returns {Promise<void>} - Resolves when ready.
38
+ */
39
+ ensureReady(): Promise<void>;
40
+ /**
41
+ * @param {object} args - Options.
42
+ * @param {string} args.channel - Channel name.
43
+ * @param {unknown} args.payload - Event payload.
44
+ * @returns {Promise<{channel: string, createdAt: string, id: string, payload: unknown}>} - Persisted event row.
45
+ */
46
+ appendEvent({ channel, payload }: {
47
+ channel: string;
48
+ payload: unknown;
49
+ }): Promise<{
50
+ channel: string;
51
+ createdAt: string;
52
+ id: string;
53
+ payload: unknown;
54
+ }>;
55
+ /**
56
+ * @param {string} channel - Channel name.
57
+ * @returns {Promise<void>} - Resolves when the channel interest was persisted.
58
+ */
59
+ markChannelInterested(channel: string): Promise<void>;
60
+ /**
61
+ * @param {string} channel - Channel name.
62
+ * @returns {Promise<boolean>} - Whether the channel should be persisted for replay.
63
+ */
64
+ shouldPersistChannel(channel: string): Promise<boolean>;
65
+ /**
66
+ * @param {object} args - Options.
67
+ * @param {string} args.channel - Channel name.
68
+ * @param {string} args.id - Event id.
69
+ * @returns {Promise<{channel: string, createdAt: string, id: string, payload: unknown, sequence: number} | null>} - Event row or null.
70
+ */
71
+ getEventById({ channel, id }: {
72
+ channel: string;
73
+ id: string;
74
+ }): Promise<{
75
+ channel: string;
76
+ createdAt: string;
77
+ id: string;
78
+ payload: unknown;
79
+ sequence: number;
80
+ } | null>;
81
+ /**
82
+ * @param {string} channel - Channel name.
83
+ * @returns {Promise<number | null>} - Latest channel sequence.
84
+ */
85
+ latestSequence(channel: string): Promise<number | null>;
86
+ /**
87
+ * @param {object} args - Options.
88
+ * @param {string} args.channel - Channel name.
89
+ * @param {number} args.sequence - Lower bound sequence.
90
+ * @param {number | null | undefined} [args.upToSequence] - Inclusive ceiling sequence.
91
+ * @returns {Promise<Array<{channel: string, createdAt: string, id: string, payload: unknown, sequence: number}>>} - Ordered events.
92
+ */
93
+ getEventsAfter({ channel, sequence, upToSequence }: {
94
+ channel: string;
95
+ sequence: number;
96
+ upToSequence?: number | null | undefined;
97
+ }): Promise<Array<{
98
+ channel: string;
99
+ createdAt: string;
100
+ id: string;
101
+ payload: unknown;
102
+ sequence: number;
103
+ }>>;
104
+ /**
105
+ * @param {object} [args] - Options.
106
+ * @param {Date} [args.now] - Cleanup reference time.
107
+ * @returns {Promise<void>} - Resolves when cleanup completes.
108
+ */
109
+ cleanupExpired({ now }?: {
110
+ now?: Date | undefined;
111
+ }): Promise<void>;
112
+ _ensureSchema(): Promise<void>;
113
+ /**
114
+ * @param {import("../database/drivers/base.js").default} db - Database connection.
115
+ * @returns {Promise<void>} - Resolves when complete.
116
+ */
117
+ _ensureEventsTable(db: import("../database/drivers/base.js").default): Promise<void>;
118
+ /**
119
+ * @param {import("../database/drivers/base.js").default} db - Database connection.
120
+ * @returns {Promise<void>} - Resolves when complete.
121
+ */
122
+ _ensureReplayChannelsTable(db: import("../database/drivers/base.js").default): Promise<void>;
123
+ /**
124
+ * @param {object} args - Options.
125
+ * @param {string} args.channel - Channel name.
126
+ * @param {import("../database/drivers/base.js").default} args.db - Database connection.
127
+ * @param {string} args.id - Event id.
128
+ * @returns {Promise<{channel: string, createdAt: string, id: string, payload: unknown, sequence: number} | null>} - Event row or null.
129
+ */
130
+ _getEventById({ channel, db, id }: {
131
+ channel: string;
132
+ db: import("../database/drivers/base.js").default;
133
+ id: string;
134
+ }): Promise<{
135
+ channel: string;
136
+ createdAt: string;
137
+ id: string;
138
+ payload: unknown;
139
+ sequence: number;
140
+ } | null>;
141
+ /**
142
+ * @param {WebsocketEventRow} row - Raw row.
143
+ * @returns {{channel: string, createdAt: string, id: string, payload: unknown, sequence: number}} - Normalized row.
144
+ */
145
+ _normalizeEventRow(row: WebsocketEventRow): {
146
+ channel: string;
147
+ createdAt: string;
148
+ id: string;
149
+ payload: unknown;
150
+ sequence: number;
151
+ };
152
+ /**
153
+ * @param {import("../database/drivers/base.js").default} db - Database connection.
154
+ * @param {object} args - Options.
155
+ * @param {string} args.channel - Channel name.
156
+ * @param {Date} args.interestedUntil - Retention deadline.
157
+ * @returns {Promise<void>} - Resolves when the replay-channel row was upserted.
158
+ */
159
+ _upsertReplayChannelInterest(db: import("../database/drivers/base.js").default, { channel, interestedUntil }: {
160
+ channel: string;
161
+ interestedUntil: Date;
162
+ }): Promise<void>;
163
+ /**
164
+ * @param {(db: import("../database/drivers/base.js").default) => Promise<any>} callback - Callback.
165
+ * @returns {Promise<any>} - Callback result.
166
+ */
167
+ _withDb(callback: (db: import("../database/drivers/base.js").default) => Promise<any>): Promise<any>;
168
+ }
169
+ export type WebsocketEventRow = {
170
+ /**
171
+ * - Channel name.
172
+ */
173
+ channel: string;
174
+ /**
175
+ * - Creation time.
176
+ */
177
+ created_at: Date | string;
178
+ /**
179
+ * - Event id.
180
+ */
181
+ id: string;
182
+ /**
183
+ * - Serialized payload.
184
+ */
185
+ payload_json: string;
186
+ /**
187
+ * - Sequence number.
188
+ */
189
+ sequence: number | string;
190
+ };
191
+ export type WebsocketReplayChannelRow = {
192
+ /**
193
+ * - Channel name.
194
+ */
195
+ channel: string;
196
+ };
197
+ import Logger from "../logger.js";
198
+ //# sourceMappingURL=websocket-event-log-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket-event-log-store.d.ts","sourceRoot":"","sources":["../../../src/http-server/websocket-event-log-store.js"],"names":[],"mappings":"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"}
@@ -0,0 +1,308 @@
1
+ // @ts-check
2
+ import { randomUUID } from "crypto";
3
+ import TableData from "../database/table-data/index.js";
4
+ import Logger from "../logger.js";
5
+ const EVENTS_TABLE = "websocket_channel_events";
6
+ const REPLAY_CHANNELS_TABLE = "websocket_replay_channels";
7
+ const DEFAULT_RETENTION_MS = 10 * 60 * 1000;
8
+ const stores = new WeakMap();
9
+ /**
10
+ * @typedef {object} WebsocketEventRow
11
+ * @property {string} channel - Channel name.
12
+ * @property {Date | string} created_at - Creation time.
13
+ * @property {string} id - Event id.
14
+ * @property {string} payload_json - Serialized payload.
15
+ * @property {number | string} sequence - Sequence number.
16
+ */
17
+ /**
18
+ * @typedef {object} WebsocketReplayChannelRow
19
+ * @property {string} channel - Channel name.
20
+ */
21
+ /**
22
+ * @param {import("../configuration.js").default} configuration - Configuration.
23
+ * @returns {VelociousHttpServerWebsocketEventLogStore} - Shared store instance.
24
+ */
25
+ export function websocketEventLogStoreForConfiguration(configuration) {
26
+ let store = stores.get(configuration);
27
+ if (!store) {
28
+ store = new VelociousHttpServerWebsocketEventLogStore({ configuration });
29
+ stores.set(configuration, store);
30
+ }
31
+ return store;
32
+ }
33
+ export default class VelociousHttpServerWebsocketEventLogStore {
34
+ /**
35
+ * @param {object} args - Options.
36
+ * @param {import("../configuration.js").default} args.configuration - Configuration.
37
+ * @param {string} [args.databaseIdentifier] - Database identifier.
38
+ * @param {number} [args.retentionMs] - Event retention in milliseconds.
39
+ */
40
+ constructor({ configuration, databaseIdentifier = "default", retentionMs = DEFAULT_RETENTION_MS }) {
41
+ this.configuration = configuration;
42
+ this.databaseIdentifier = databaseIdentifier;
43
+ this.retentionMs = retentionMs;
44
+ this.logger = new Logger(this);
45
+ this._isReady = false;
46
+ this._readyPromise = null;
47
+ }
48
+ /**
49
+ * @returns {Promise<void>} - Resolves when ready.
50
+ */
51
+ async ensureReady() {
52
+ if (this._isReady)
53
+ return;
54
+ if (this._readyPromise)
55
+ return await this._readyPromise;
56
+ this._readyPromise = (async () => {
57
+ this.configuration.setCurrent();
58
+ await this._ensureSchema();
59
+ this._isReady = true;
60
+ })();
61
+ try {
62
+ await this._readyPromise;
63
+ }
64
+ finally {
65
+ if (!this._isReady) {
66
+ this._readyPromise = null;
67
+ }
68
+ }
69
+ }
70
+ /**
71
+ * @param {object} args - Options.
72
+ * @param {string} args.channel - Channel name.
73
+ * @param {unknown} args.payload - Event payload.
74
+ * @returns {Promise<{channel: string, createdAt: string, id: string, payload: unknown}>} - Persisted event row.
75
+ */
76
+ async appendEvent({ channel, payload }) {
77
+ await this.ensureReady();
78
+ const id = randomUUID();
79
+ const createdAt = new Date();
80
+ return await this._withDb(async (db) => {
81
+ await db.insert({
82
+ tableName: EVENTS_TABLE,
83
+ data: {
84
+ channel,
85
+ created_at: createdAt,
86
+ id,
87
+ payload_json: JSON.stringify(payload)
88
+ }
89
+ });
90
+ return { channel, createdAt: createdAt.toISOString(), id, payload };
91
+ });
92
+ }
93
+ /**
94
+ * @param {string} channel - Channel name.
95
+ * @returns {Promise<void>} - Resolves when the channel interest was persisted.
96
+ */
97
+ async markChannelInterested(channel) {
98
+ await this.ensureReady();
99
+ const interestedUntil = new Date(Date.now() + this.retentionMs);
100
+ await this._withDb(async (db) => {
101
+ await this._upsertReplayChannelInterest(db, { channel, interestedUntil });
102
+ });
103
+ }
104
+ /**
105
+ * @param {string} channel - Channel name.
106
+ * @returns {Promise<boolean>} - Whether the channel should be persisted for replay.
107
+ */
108
+ async shouldPersistChannel(channel) {
109
+ await this.ensureReady();
110
+ return await this._withDb(async (db) => {
111
+ const rows = await db
112
+ .newQuery()
113
+ .from(REPLAY_CHANNELS_TABLE)
114
+ .where({ channel })
115
+ .where(`interested_until > ${db.quote(new Date())}`)
116
+ .limit(1)
117
+ .results();
118
+ return rows.length > 0;
119
+ });
120
+ }
121
+ /**
122
+ * @param {object} args - Options.
123
+ * @param {string} args.channel - Channel name.
124
+ * @param {string} args.id - Event id.
125
+ * @returns {Promise<{channel: string, createdAt: string, id: string, payload: unknown, sequence: number} | null>} - Event row or null.
126
+ */
127
+ async getEventById({ channel, id }) {
128
+ await this.ensureReady();
129
+ return await this._withDb(async (db) => {
130
+ return await this._getEventById({ channel, db, id });
131
+ });
132
+ }
133
+ /**
134
+ * @param {string} channel - Channel name.
135
+ * @returns {Promise<number | null>} - Latest channel sequence.
136
+ */
137
+ async latestSequence(channel) {
138
+ await this.ensureReady();
139
+ return await this._withDb(async (db) => {
140
+ const rows = await db
141
+ .newQuery()
142
+ .from(EVENTS_TABLE)
143
+ .where({ channel })
144
+ .order("sequence DESC")
145
+ .limit(1)
146
+ .results();
147
+ const row = /** @type {Record<string, any> | undefined} */ (rows[0]);
148
+ if (!row)
149
+ return null;
150
+ return Number(row.sequence);
151
+ });
152
+ }
153
+ /**
154
+ * @param {object} args - Options.
155
+ * @param {string} args.channel - Channel name.
156
+ * @param {number} args.sequence - Lower bound sequence.
157
+ * @param {number | null | undefined} [args.upToSequence] - Inclusive ceiling sequence.
158
+ * @returns {Promise<Array<{channel: string, createdAt: string, id: string, payload: unknown, sequence: number}>>} - Ordered events.
159
+ */
160
+ async getEventsAfter({ channel, sequence, upToSequence }) {
161
+ await this.ensureReady();
162
+ return await this._withDb(async (db) => {
163
+ const query = db
164
+ .newQuery()
165
+ .from(EVENTS_TABLE)
166
+ .where({ channel })
167
+ .where(`sequence > ${db.quote(sequence)}`)
168
+ .order("sequence ASC");
169
+ if (typeof upToSequence === "number") {
170
+ query.where(`sequence <= ${db.quote(upToSequence)}`);
171
+ }
172
+ const rows = /** @type {WebsocketEventRow[]} */ (await query.results());
173
+ return rows.map((row) => this._normalizeEventRow(row));
174
+ });
175
+ }
176
+ /**
177
+ * @param {object} [args] - Options.
178
+ * @param {Date} [args.now] - Cleanup reference time.
179
+ * @returns {Promise<void>} - Resolves when cleanup completes.
180
+ */
181
+ async cleanupExpired({ now = new Date() } = {}) {
182
+ await this.ensureReady();
183
+ const cutoff = new Date(now.getTime() - this.retentionMs);
184
+ await this._withDb(async (db) => {
185
+ const expiredEventRows = /** @type {Array<{id: string}>} */ (await db
186
+ .newQuery()
187
+ .from(EVENTS_TABLE)
188
+ .where(`created_at <= ${db.quote(cutoff)}`)
189
+ .results());
190
+ const expiredReplayChannelRows = /** @type {WebsocketReplayChannelRow[]} */ (await db
191
+ .newQuery()
192
+ .from(REPLAY_CHANNELS_TABLE)
193
+ .where(`interested_until <= ${db.quote(now)}`)
194
+ .results());
195
+ for (const expiredEventRow of expiredEventRows) {
196
+ await db.delete({
197
+ tableName: EVENTS_TABLE,
198
+ conditions: { id: expiredEventRow.id }
199
+ });
200
+ }
201
+ for (const expiredReplayChannelRow of expiredReplayChannelRows) {
202
+ await db.delete({
203
+ tableName: REPLAY_CHANNELS_TABLE,
204
+ conditions: { channel: expiredReplayChannelRow.channel }
205
+ });
206
+ }
207
+ });
208
+ }
209
+ async _ensureSchema() {
210
+ await this._withDb(async (db) => {
211
+ await this._ensureEventsTable(db);
212
+ await this._ensureReplayChannelsTable(db);
213
+ });
214
+ }
215
+ /**
216
+ * @param {import("../database/drivers/base.js").default} db - Database connection.
217
+ * @returns {Promise<void>} - Resolves when complete.
218
+ */
219
+ async _ensureEventsTable(db) {
220
+ this.logger.info("Applying websocket event-log schema");
221
+ if (await db.tableExists(EVENTS_TABLE)) {
222
+ this.logger.info("Websocket event-log table already exists - skipping create");
223
+ return;
224
+ }
225
+ const eventTable = new TableData(EVENTS_TABLE, { ifNotExists: true });
226
+ eventTable.integer("sequence", { autoIncrement: true, null: false, primaryKey: true });
227
+ eventTable.string("id", { index: true, null: false });
228
+ eventTable.string("channel", { index: true, null: false });
229
+ eventTable.text("payload_json", { null: false });
230
+ eventTable.datetime("created_at", { index: true, null: false });
231
+ await db.createTable(eventTable);
232
+ }
233
+ /**
234
+ * @param {import("../database/drivers/base.js").default} db - Database connection.
235
+ * @returns {Promise<void>} - Resolves when complete.
236
+ */
237
+ async _ensureReplayChannelsTable(db) {
238
+ if (await db.tableExists(REPLAY_CHANNELS_TABLE))
239
+ return;
240
+ const replayChannelTable = new TableData(REPLAY_CHANNELS_TABLE, { ifNotExists: true });
241
+ replayChannelTable.string("channel", { null: false, primaryKey: true });
242
+ replayChannelTable.datetime("interested_until", { index: true, null: false });
243
+ await db.createTable(replayChannelTable);
244
+ }
245
+ /**
246
+ * @param {object} args - Options.
247
+ * @param {string} args.channel - Channel name.
248
+ * @param {import("../database/drivers/base.js").default} args.db - Database connection.
249
+ * @param {string} args.id - Event id.
250
+ * @returns {Promise<{channel: string, createdAt: string, id: string, payload: unknown, sequence: number} | null>} - Event row or null.
251
+ */
252
+ async _getEventById({ channel, db, id }) {
253
+ const rows = /** @type {WebsocketEventRow[]} */ (await db
254
+ .newQuery()
255
+ .from(EVENTS_TABLE)
256
+ .where({ channel, id })
257
+ .limit(1)
258
+ .results());
259
+ if (!rows[0])
260
+ return null;
261
+ return this._normalizeEventRow(rows[0]);
262
+ }
263
+ /**
264
+ * @param {WebsocketEventRow} row - Raw row.
265
+ * @returns {{channel: string, createdAt: string, id: string, payload: unknown, sequence: number}} - Normalized row.
266
+ */
267
+ _normalizeEventRow(row) {
268
+ const createdAtValue = row.created_at;
269
+ return {
270
+ channel: row.channel,
271
+ createdAt: createdAtValue instanceof Date ? createdAtValue.toISOString() : new Date(createdAtValue).toISOString(),
272
+ id: row.id,
273
+ payload: JSON.parse(row.payload_json),
274
+ sequence: Number(row.sequence)
275
+ };
276
+ }
277
+ /**
278
+ * @param {import("../database/drivers/base.js").default} db - Database connection.
279
+ * @param {object} args - Options.
280
+ * @param {string} args.channel - Channel name.
281
+ * @param {Date} args.interestedUntil - Retention deadline.
282
+ * @returns {Promise<void>} - Resolves when the replay-channel row was upserted.
283
+ */
284
+ async _upsertReplayChannelInterest(db, { channel, interestedUntil }) {
285
+ await db.upsert({
286
+ conflictColumns: ["channel"],
287
+ data: {
288
+ channel,
289
+ interested_until: interestedUntil
290
+ },
291
+ tableName: REPLAY_CHANNELS_TABLE,
292
+ updateColumns: ["interested_until"]
293
+ });
294
+ }
295
+ /**
296
+ * @param {(db: import("../database/drivers/base.js").default) => Promise<any>} callback - Callback.
297
+ * @returns {Promise<any>} - Callback result.
298
+ */
299
+ async _withDb(callback) {
300
+ return await this.configuration.ensureConnections(async (dbs) => {
301
+ const db = dbs[this.databaseIdentifier];
302
+ if (!db)
303
+ throw new Error(`No database connection available for identifier: ${this.databaseIdentifier}`);
304
+ return await callback(db);
305
+ });
306
+ }
307
+ }
308
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2Vic29ja2V0LWV2ZW50LWxvZy1zdG9yZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9odHRwLXNlcnZlci93ZWJzb2NrZXQtZXZlbnQtbG9nLXN0b3JlLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWixPQUFPLEVBQUMsVUFBVSxFQUFDLE1BQU0sUUFBUSxDQUFBO0FBQ2pDLE9BQU8sU0FBUyxNQUFNLGlDQUFpQyxDQUFBO0FBQ3ZELE9BQU8sTUFBTSxNQUFNLGNBQWMsQ0FBQTtBQUVqQyxNQUFNLFlBQVksR0FBRywwQkFBMEIsQ0FBQTtBQUMvQyxNQUFNLHFCQUFxQixHQUFHLDJCQUEyQixDQUFBO0FBQ3pELE1BQU0sb0JBQW9CLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUE7QUFDM0MsTUFBTSxNQUFNLEdBQUcsSUFBSSxPQUFPLEVBQUUsQ0FBQTtBQUU1Qjs7Ozs7OztHQU9HO0FBRUg7OztHQUdHO0FBRUg7OztHQUdHO0FBQ0gsTUFBTSxVQUFVLHNDQUFzQyxDQUFDLGFBQWE7SUFDbEUsSUFBSSxLQUFLLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsQ0FBQTtJQUVyQyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDWCxLQUFLLEdBQUcsSUFBSSx5Q0FBeUMsQ0FBQyxFQUFDLGFBQWEsRUFBQyxDQUFDLENBQUE7UUFDdEUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxhQUFhLEVBQUUsS0FBSyxDQUFDLENBQUE7SUFDbEMsQ0FBQztJQUVELE9BQU8sS0FBSyxDQUFBO0FBQ2QsQ0FBQztBQUVELE1BQU0sQ0FBQyxPQUFPLE9BQU8seUNBQXlDO0lBQzVEOzs7OztPQUtHO0lBQ0gsWUFBWSxFQUFDLGFBQWEsRUFBRSxrQkFBa0IsR0FBRyxTQUFTLEVBQUUsV0FBVyxHQUFHLG9CQUFvQixFQUFDO1FBQzdGLElBQUksQ0FBQyxhQUFhLEdBQUcsYUFBYSxDQUFBO1FBQ2xD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QUFJLENBQUMsRUFBRTtnQkFBRSxNQUFNLElBQUksS0FBSyxDQUFDLG9EQUFvRCxJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQyxDQUFBO1lBRXZHLE9BQU8sTUFBTSxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDM0IsQ0FBQyxDQUFDLENBQUE7SUFDSixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuaW1wb3J0IHtyYW5kb21VVUlEfSBmcm9tIFwiY3J5cHRvXCJcbmltcG9ydCBUYWJsZURhdGEgZnJvbSBcIi4uL2RhdGFiYXNlL3RhYmxlLWRhdGEvaW5kZXguanNcIlxuaW1wb3J0IExvZ2dlciBmcm9tIFwiLi4vbG9nZ2VyLmpzXCJcblxuY29uc3QgRVZFTlRTX1RBQkxFID0gXCJ3ZWJzb2NrZXRfY2hhbm5lbF9ldmVudHNcIlxuY29uc3QgUkVQTEFZX0NIQU5ORUxTX1RBQkxFID0gXCJ3ZWJzb2NrZXRfcmVwbGF5X2NoYW5uZWxzXCJcbmNvbnN0IERFRkFVTFRfUkVURU5USU9OX01TID0gMTAgKiA2MCAqIDEwMDBcbmNvbnN0IHN0b3JlcyA9IG5ldyBXZWFrTWFwKClcblxuLyoqXG4gKiBAdHlwZWRlZiB7b2JqZWN0fSBXZWJzb2NrZXRFdmVudFJvd1xuICogQHByb3BlcnR5IHtzdHJpbmd9IGNoYW5uZWwgLSBDaGFubmVsIG5hbWUuXG4gKiBAcHJvcGVydHkge0RhdGUgfCBzdHJpbmd9IGNyZWF0ZWRfYXQgLSBDcmVhdGlvbiB0aW1lLlxuICogQHByb3BlcnR5IHtzdHJpbmd9IGlkIC0gRXZlbnQgaWQuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gcGF5bG9hZF9qc29uIC0gU2VyaWFsaXplZCBwYXlsb2FkLlxuICogQHByb3BlcnR5IHtudW1iZXIgfCBzdHJpbmd9IHNlcXVlbmNlIC0gU2VxdWVuY2UgbnVtYmVyLlxuICovXG5cbi8qKlxuICogQHR5cGVkZWYge29iamVjdH0gV2Vic29ja2V0UmVwbGF5Q2hhbm5lbFJvd1xuICogQHByb3BlcnR5IHtzdHJpbmd9IGNoYW5uZWwgLSBDaGFubmVsIG5hbWUuXG4gKi9cblxuLyoqXG4gKiBAcGFyYW0ge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdH0gY29uZmlndXJhdGlvbiAtIENvbmZpZ3VyYXRpb24uXG4gKiBAcmV0dXJucyB7VmVsb2Npb3VzSHR0cFNlcnZlcldlYnNvY2tldEV2ZW50TG9nU3RvcmV9IC0gU2hhcmVkIHN0b3JlIGluc3RhbmNlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gd2Vic29ja2V0RXZlbnRMb2dTdG9yZUZvckNvbmZpZ3VyYXRpb24oY29uZmlndXJhdGlvbikge1xuICBsZXQgc3RvcmUgPSBzdG9yZXMuZ2V0KGNvbmZpZ3VyYXRpb24pXG5cbiAgaWYgKCFzdG9yZSkge1xuICAgIHN0b3JlID0gbmV3IFZlbG9jaW91c0h0dHBTZXJ2ZXJXZWJzb2NrZXRFdmVudExvZ1N0b3JlKHtjb25maWd1cmF0aW9ufSlcbiAgICBzdG9yZXMuc2V0KGNvbmZpZ3VyYXRpb24sIHN0b3JlKVxuICB9XG5cbiAgcmV0dXJuIHN0b3JlXG59XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFZlbG9jaW91c0h0dHBTZXJ2ZXJXZWJzb2NrZXRFdmVudExvZ1N0b3JlIHtcbiAgLyoqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLmpzXCIpLmRlZmF1bHR9IGFyZ3MuY29uZmlndXJhdGlvbiAtIENvbmZpZ3VyYXRpb24uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbYXJncy5kYXRhYmFzZUlkZW50aWZpZXJdIC0gRGF0YWJhc2UgaWRlbnRpZmllci5cbiAgICogQHBhcmFtIHtudW1iZXJ9IFthcmdzLnJldGVudGlvbk1zXSAtIEV2ZW50IHJldGVudGlvbiBpbiBtaWxsaXNlY29uZHMuXG4gICAqL1xuICBjb25zdHJ1Y3Rvcih7Y29uZmlndXJhdGlvbiwgZGF0YWJhc2VJZGVudGlmaWVyID0gXCJkZWZhdWx0XCIsIHJldGVudGlvbk1zID0gREVGQVVMVF9SRVRFTlRJT05fTVN9KSB7XG4gICAgdGhpcy5jb25maWd1cmF0aW9uID0gY29uZmlndXJhdGlvblxuICAgIHRoaXMuZGF0YWJhc2VJZGVudGlmaWVyID0gZGF0YWJhc2VJZGVudGlmaWVyXG4gICAgdGhpcy5yZXRlbnRpb25NcyA9IHJldGVudGlvbk1zXG4gICAgdGhpcy5sb2dnZXIgPSBuZXcgTG9nZ2VyKHRoaXMpXG4gICAgdGhpcy5faXNSZWFkeSA9IGZhbHNlXG4gICAgdGhpcy5fcmVhZHlQcm9taXNlID0gbnVsbFxuICB9XG5cbiAgLyoqXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIHdoZW4gcmVhZHkuXG4gICAqL1xuICBhc3luYyBlbnN1cmVSZWFkeSgpIHtcbiAgICBpZiAodGhpcy5faXNSZWFkeSkgcmV0dXJuXG4gICAgaWYgKHRoaXMuX3JlYWR5UHJvbWlzZSkgcmV0dXJuIGF3YWl0IHRoaXMuX3JlYWR5UHJvbWlzZVxuXG4gICAgdGhpcy5fcmVhZHlQcm9taXNlID0gKGFzeW5jICgpID0+IHtcbiAgICAgIHRoaXMuY29uZmlndXJhdGlvbi5zZXRDdXJyZW50KClcbiAgICAgIGF3YWl0IHRoaXMuX2Vuc3VyZVNjaGVtYSgpXG4gICAgICB0aGlzLl9pc1JlYWR5ID0gdHJ1ZVxuICAgIH0pKClcblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLl9yZWFkeVByb21pc2VcbiAgICB9IGZpbmFsbHkge1xuICAgICAgaWYgKCF0aGlzLl9pc1JlYWR5KSB7XG4gICAgICAgIHRoaXMuX3JlYWR5UHJvbWlzZSA9IG51bGxcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5jaGFubmVsIC0gQ2hhbm5lbCBuYW1lLlxuICAgKiBAcGFyYW0ge3Vua25vd259IGFyZ3MucGF5bG9hZCAtIEV2ZW50IHBheWxvYWQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHtjaGFubmVsOiBzdHJpbmcsIGNyZWF0ZWRBdDogc3RyaW5nLCBpZDogc3RyaW5nLCBwYXlsb2FkOiB1bmtub3dufT59IC0gUGVyc2lzdGVkIGV2ZW50IHJvdy5cbiAgICovXG4gIGFzeW5jIGFwcGVuZEV2ZW50KHtjaGFubmVsLCBwYXlsb2FkfSkge1xuICAgIGF3YWl0IHRoaXMuZW5zdXJlUmVhZHkoKVxuXG4gICAgY29uc3QgaWQgPSByYW5kb21VVUlEKClcbiAgICBjb25zdCBjcmVhdGVkQXQgPSBuZXcgRGF0ZSgpXG5cbiAgICByZXR1cm4gYXdhaXQgdGhpcy5fd2l0aERiKGFzeW5jIChkYikgPT4ge1xuICAgICAgYXdhaXQgZGIuaW5zZXJ0KHtcbiAgICAgICAgdGFibGVOYW1lOiBFVkVOVFNfVEFCTEUsXG4gICAgICAgIGRhdGE6IHtcbiAgICAgICAgICBjaGFubmVsLFxuICAgICAgICAgIGNyZWF0ZWRfYXQ6IGNyZWF0ZWRBdCxcbiAgICAgICAgICBpZCxcbiAgICAgICAgICBwYXlsb2FkX2pzb246IEpTT04uc3RyaW5naWZ5KHBheWxvYWQpXG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICByZXR1cm4ge2NoYW5uZWwsIGNyZWF0ZWRBdDogY3JlYXRlZEF0LnRvSVNPU3RyaW5nKCksIGlkLCBwYXlsb2FkfVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtzdHJpbmd9IGNoYW5uZWwgLSBDaGFubmVsIG5hbWUuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIHdoZW4gdGhlIGNoYW5uZWwgaW50ZXJlc3Qgd2FzIHBlcnNpc3RlZC5cbiAgICovXG4gIGFzeW5jIG1hcmtDaGFubmVsSW50ZXJlc3RlZChjaGFubmVsKSB7XG4gICAgYXdhaXQgdGhpcy5lbnN1cmVSZWFkeSgpXG5cbiAgICBjb25zdCBpbnRlcmVzdGVkVW50aWwgPSBuZXcgRGF0ZShEYXRlLm5vdygpICsgdGhpcy5yZXRlbnRpb25NcylcblxuICAgIGF3YWl0IHRoaXMuX3dpdGhEYihhc3luYyAoZGIpID0+IHtcbiAgICAgIGF3YWl0IHRoaXMuX3Vwc2VydFJlcGxheUNoYW5uZWxJbnRlcmVzdChkYiwge2NoYW5uZWwsIGludGVyZXN0ZWRVbnRpbH0pXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gY2hhbm5lbCAtIENoYW5uZWwgbmFtZS5cbiAgICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IC0gV2hldGhlciB0aGUgY2hhbm5lbCBzaG91bGQgYmUgcGVyc2lzdGVkIGZvciByZXBsYXkuXG4gICAqL1xuICBhc3luYyBzaG91bGRQZXJzaXN0Q2hhbm5lbChjaGFubmVsKSB7XG4gICAgYXdhaXQgdGhpcy5lbnN1cmVSZWFkeSgpXG5cbiAgICByZXR1cm4gYXdhaXQgdGhpcy5fd2l0aERiKGFzeW5jIChkYikgPT4ge1xuICAgICAgY29uc3Qgcm93cyA9IGF3YWl0IGRiXG4gICAgICAgIC5uZXdRdWVyeSgpXG4gICAgICAgIC5mcm9tKFJFUExBWV9DSEFOTkVMU19UQUJMRSlcbiAgICAgICAgLndoZXJlKHtjaGFubmVsfSlcbiAgICAgICAgLndoZXJlKGBpbnRlcmVzdGVkX3VudGlsID4gJHtkYi5xdW90ZShuZXcgRGF0ZSgpKX1gKVxuICAgICAgICAubGltaXQoMSlcbiAgICAgICAgLnJlc3VsdHMoKVxuXG4gICAgICByZXR1cm4gcm93cy5sZW5ndGggPiAwXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmNoYW5uZWwgLSBDaGFubmVsIG5hbWUuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmlkIC0gRXZlbnQgaWQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHtjaGFubmVsOiBzdHJpbmcsIGNyZWF0ZWRBdDogc3RyaW5nLCBpZDogc3RyaW5nLCBwYXlsb2FkOiB1bmtub3duLCBzZXF1ZW5jZTogbnVtYmVyfSB8IG51bGw+fSAtIEV2ZW50IHJvdyBvciBudWxsLlxuICAgKi9cbiAgYXN5bmMgZ2V0RXZlbnRCeUlkKHtjaGFubmVsLCBpZH0pIHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVJlYWR5KClcblxuICAgIHJldHVybiBhd2FpdCB0aGlzLl93aXRoRGIoYXN5bmMgKGRiKSA9PiB7XG4gICAgICByZXR1cm4gYXdhaXQgdGhpcy5fZ2V0RXZlbnRCeUlkKHtjaGFubmVsLCBkYiwgaWR9KVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtzdHJpbmd9IGNoYW5uZWwgLSBDaGFubmVsIG5hbWUuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPG51bWJlciB8IG51bGw+fSAtIExhdGVzdCBjaGFubmVsIHNlcXVlbmNlLlxuICAgKi9cbiAgYXN5bmMgbGF0ZXN0U2VxdWVuY2UoY2hhbm5lbCkge1xuICAgIGF3YWl0IHRoaXMuZW5zdXJlUmVhZHkoKVxuXG4gICAgcmV0dXJuIGF3YWl0IHRoaXMuX3dpdGhEYihhc3luYyAoZGIpID0+IHtcbiAgICAgIGNvbnN0IHJvd3MgPSBhd2FpdCBkYlxuICAgICAgICAubmV3UXVlcnkoKVxuICAgICAgICAuZnJvbShFVkVOVFNfVEFCTEUpXG4gICAgICAgIC53aGVyZSh7Y2hhbm5lbH0pXG4gICAgICAgIC5vcmRlcihcInNlcXVlbmNlIERFU0NcIilcbiAgICAgICAgLmxpbWl0KDEpXG4gICAgICAgIC5yZXN1bHRzKClcbiAgICAgIGNvbnN0IHJvdyA9IC8qKiBAdHlwZSB7UmVjb3JkPHN0cmluZywgYW55PiB8IHVuZGVmaW5lZH0gKi8gKHJvd3NbMF0pXG5cbiAgICAgIGlmICghcm93KSByZXR1cm4gbnVsbFxuXG4gICAgICByZXR1cm4gTnVtYmVyKHJvdy5zZXF1ZW5jZSlcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MuY2hhbm5lbCAtIENoYW5uZWwgbmFtZS5cbiAgICogQHBhcmFtIHtudW1iZXJ9IGFyZ3Muc2VxdWVuY2UgLSBMb3dlciBib3VuZCBzZXF1ZW5jZS5cbiAgICogQHBhcmFtIHtudW1iZXIgfCBudWxsIHwgdW5kZWZpbmVkfSBbYXJncy51cFRvU2VxdWVuY2VdIC0gSW5jbHVzaXZlIGNlaWxpbmcgc2VxdWVuY2UuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5PHtjaGFubmVsOiBzdHJpbmcsIGNyZWF0ZWRBdDogc3RyaW5nLCBpZDogc3RyaW5nLCBwYXlsb2FkOiB1bmtub3duLCBzZXF1ZW5jZTogbnVtYmVyfT4+fSAtIE9yZGVyZWQgZXZlbnRzLlxuICAgKi9cbiAgYXN5bmMgZ2V0RXZlbnRzQWZ0ZXIoe2NoYW5uZWwsIHNlcXVlbmNlLCB1cFRvU2VxdWVuY2V9KSB7XG4gICAgYXdhaXQgdGhpcy5lbnN1cmVSZWFkeSgpXG5cbiAgICByZXR1cm4gYXdhaXQgdGhpcy5fd2l0aERiKGFzeW5jIChkYikgPT4ge1xuICAgICAgY29uc3QgcXVlcnkgPSBkYlxuICAgICAgICAubmV3UXVlcnkoKVxuICAgICAgICAuZnJvbShFVkVOVFNfVEFCTEUpXG4gICAgICAgIC53aGVyZSh7Y2hhbm5lbH0pXG4gICAgICAgIC53aGVyZShgc2VxdWVuY2UgPiAke2RiLnF1b3RlKHNlcXVlbmNlKX1gKVxuICAgICAgICAub3JkZXIoXCJzZXF1ZW5jZSBBU0NcIilcblxuICAgICAgaWYgKHR5cGVvZiB1cFRvU2VxdWVuY2UgPT09IFwibnVtYmVyXCIpIHtcbiAgICAgICAgcXVlcnkud2hlcmUoYHNlcXVlbmNlIDw9ICR7ZGIucXVvdGUodXBUb1NlcXVlbmNlKX1gKVxuICAgICAgfVxuXG4gICAgICBjb25zdCByb3dzID0gLyoqIEB0eXBlIHtXZWJzb2NrZXRFdmVudFJvd1tdfSAqLyAoYXdhaXQgcXVlcnkucmVzdWx0cygpKVxuXG4gICAgICByZXR1cm4gcm93cy5tYXAoKHJvdykgPT4gdGhpcy5fbm9ybWFsaXplRXZlbnRSb3cocm93KSlcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBbYXJnc10gLSBPcHRpb25zLlxuICAgKiBAcGFyYW0ge0RhdGV9IFthcmdzLm5vd10gLSBDbGVhbnVwIHJlZmVyZW5jZSB0aW1lLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIGNsZWFudXAgY29tcGxldGVzLlxuICAgKi9cbiAgYXN5bmMgY2xlYW51cEV4cGlyZWQoe25vdyA9IG5ldyBEYXRlKCl9ID0ge30pIHtcbiAgICBhd2FpdCB0aGlzLmVuc3VyZVJlYWR5KClcblxuICAgIGNvbnN0IGN1dG9mZiA9IG5ldyBEYXRlKG5vdy5nZXRUaW1lKCkgLSB0aGlzLnJldGVudGlvbk1zKVxuXG4gICAgYXdhaXQgdGhpcy5fd2l0aERiKGFzeW5jIChkYikgPT4ge1xuICAgICAgY29uc3QgZXhwaXJlZEV2ZW50Um93cyA9IC8qKiBAdHlwZSB7QXJyYXk8e2lkOiBzdHJpbmd9Pn0gKi8gKGF3YWl0IGRiXG4gICAgICAgIC5uZXdRdWVyeSgpXG4gICAgICAgIC5mcm9tKEVWRU5UU19UQUJMRSlcbiAgICAgICAgLndoZXJlKGBjcmVhdGVkX2F0IDw9ICR7ZGIucXVvdGUoY3V0b2ZmKX1gKVxuICAgICAgICAucmVzdWx0cygpKVxuICAgICAgY29uc3QgZXhwaXJlZFJlcGxheUNoYW5uZWxSb3dzID0gLyoqIEB0eXBlIHtXZWJzb2NrZXRSZXBsYXlDaGFubmVsUm93W119ICovIChhd2FpdCBkYlxuICAgICAgICAubmV3UXVlcnkoKVxuICAgICAgICAuZnJvbShSRVBMQVlfQ0hBTk5FTFNfVEFCTEUpXG4gICAgICAgIC53aGVyZShgaW50ZXJlc3RlZF91bnRpbCA8PSAke2RiLnF1b3RlKG5vdyl9YClcbiAgICAgICAgLnJlc3VsdHMoKSlcblxuICAgICAgZm9yIChjb25zdCBleHBpcmVkRXZlbnRSb3cgb2YgZXhwaXJlZEV2ZW50Um93cykge1xuICAgICAgICBhd2FpdCBkYi5kZWxldGUoe1xuICAgICAgICAgIHRhYmxlTmFtZTogRVZFTlRTX1RBQkxFLFxuICAgICAgICAgIGNvbmRpdGlvbnM6IHtpZDogZXhwaXJlZEV2ZW50Um93LmlkfVxuICAgICAgICB9KVxuICAgICAgfVxuXG4gICAgICBmb3IgKGNvbnN0IGV4cGlyZWRSZXBsYXlDaGFubmVsUm93IG9mIGV4cGlyZWRSZXBsYXlDaGFubmVsUm93cykge1xuICAgICAgICBhd2FpdCBkYi5kZWxldGUoe1xuICAgICAgICAgIHRhYmxlTmFtZTogUkVQTEFZX0NIQU5ORUxTX1RBQkxFLFxuICAgICAgICAgIGNvbmRpdGlvbnM6IHtjaGFubmVsOiBleHBpcmVkUmVwbGF5Q2hhbm5lbFJvdy5jaGFubmVsfVxuICAgICAgICB9KVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICBhc3luYyBfZW5zdXJlU2NoZW1hKCkge1xuICAgIGF3YWl0IHRoaXMuX3dpdGhEYihhc3luYyAoZGIpID0+IHtcbiAgICAgIGF3YWl0IHRoaXMuX2Vuc3VyZUV2ZW50c1RhYmxlKGRiKVxuICAgICAgYXdhaXQgdGhpcy5fZW5zdXJlUmVwbGF5Q2hhbm5lbHNUYWJsZShkYilcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vZGF0YWJhc2UvZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gRGF0YWJhc2UgY29ubmVjdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiBjb21wbGV0ZS5cbiAgICovXG4gIGFzeW5jIF9lbnN1cmVFdmVudHNUYWJsZShkYikge1xuICAgIHRoaXMubG9nZ2VyLmluZm8oXCJBcHBseWluZyB3ZWJzb2NrZXQgZXZlbnQtbG9nIHNjaGVtYVwiKVxuXG4gICAgaWYgKGF3YWl0IGRiLnRhYmxlRXhpc3RzKEVWRU5UU19UQUJMRSkpIHtcbiAgICAgIHRoaXMubG9nZ2VyLmluZm8oXCJXZWJzb2NrZXQgZXZlbnQtbG9nIHRhYmxlIGFscmVhZHkgZXhpc3RzIC0gc2tpcHBpbmcgY3JlYXRlXCIpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBjb25zdCBldmVudFRhYmxlID0gbmV3IFRhYmxlRGF0YShFVkVOVFNfVEFCTEUsIHtpZk5vdEV4aXN0czogdHJ1ZX0pXG5cbiAgICBldmVudFRhYmxlLmludGVnZXIoXCJzZXF1ZW5jZVwiLCB7YXV0b0luY3JlbWVudDogdHJ1ZSwgbnVsbDogZmFsc2UsIHByaW1hcnlLZXk6IHRydWV9KVxuICAgIGV2ZW50VGFibGUuc3RyaW5nKFwiaWRcIiwge2luZGV4OiB0cnVlLCBudWxsOiBmYWxzZX0pXG4gICAgZXZlbnRUYWJsZS5zdHJpbmcoXCJjaGFubmVsXCIsIHtpbmRleDogdHJ1ZSwgbnVsbDogZmFsc2V9KVxuICAgIGV2ZW50VGFibGUudGV4dChcInBheWxvYWRfanNvblwiLCB7bnVsbDogZmFsc2V9KVxuICAgIGV2ZW50VGFibGUuZGF0ZXRpbWUoXCJjcmVhdGVkX2F0XCIsIHtpbmRleDogdHJ1ZSwgbnVsbDogZmFsc2V9KVxuXG4gICAgYXdhaXQgZGIuY3JlYXRlVGFibGUoZXZlbnRUYWJsZSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2RhdGFiYXNlL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0fSBkYiAtIERhdGFiYXNlIGNvbm5lY3Rpb24uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIHdoZW4gY29tcGxldGUuXG4gICAqL1xuICBhc3luYyBfZW5zdXJlUmVwbGF5Q2hhbm5lbHNUYWJsZShkYikge1xuICAgIGlmIChhd2FpdCBkYi50YWJsZUV4aXN0cyhSRVBMQVlfQ0hBTk5FTFNfVEFCTEUpKSByZXR1cm5cblxuICAgIGNvbnN0IHJlcGxheUNoYW5uZWxUYWJsZSA9IG5ldyBUYWJsZURhdGEoUkVQTEFZX0NIQU5ORUxTX1RBQkxFLCB7aWZOb3RFeGlzdHM6IHRydWV9KVxuXG4gICAgcmVwbGF5Q2hhbm5lbFRhYmxlLnN0cmluZyhcImNoYW5uZWxcIiwge251bGw6IGZhbHNlLCBwcmltYXJ5S2V5OiB0cnVlfSlcbiAgICByZXBsYXlDaGFubmVsVGFibGUuZGF0ZXRpbWUoXCJpbnRlcmVzdGVkX3VudGlsXCIsIHtpbmRleDogdHJ1ZSwgbnVsbDogZmFsc2V9KVxuXG4gICAgYXdhaXQgZGIuY3JlYXRlVGFibGUocmVwbGF5Q2hhbm5lbFRhYmxlKVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MuY2hhbm5lbCAtIENoYW5uZWwgbmFtZS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gYXJncy5kYiAtIERhdGFiYXNlIGNvbm5lY3Rpb24uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmlkIC0gRXZlbnQgaWQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHtjaGFubmVsOiBzdHJpbmcsIGNyZWF0ZWRBdDogc3RyaW5nLCBpZDogc3RyaW5nLCBwYXlsb2FkOiB1bmtub3duLCBzZXF1ZW5jZTogbnVtYmVyfSB8IG51bGw+fSAtIEV2ZW50IHJvdyBvciBudWxsLlxuICAgKi9cbiAgYXN5bmMgX2dldEV2ZW50QnlJZCh7Y2hhbm5lbCwgZGIsIGlkfSkge1xuICAgIGNvbnN0IHJvd3MgPSAvKiogQHR5cGUge1dlYnNvY2tldEV2ZW50Um93W119ICovIChhd2FpdCBkYlxuICAgICAgLm5ld1F1ZXJ5KClcbiAgICAgIC5mcm9tKEVWRU5UU19UQUJMRSlcbiAgICAgIC53aGVyZSh7Y2hhbm5lbCwgaWR9KVxuICAgICAgLmxpbWl0KDEpXG4gICAgICAucmVzdWx0cygpKVxuXG4gICAgaWYgKCFyb3dzWzBdKSByZXR1cm4gbnVsbFxuXG4gICAgcmV0dXJuIHRoaXMuX25vcm1hbGl6ZUV2ZW50Um93KHJvd3NbMF0pXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtXZWJzb2NrZXRFdmVudFJvd30gcm93IC0gUmF3IHJvdy5cbiAgICogQHJldHVybnMge3tjaGFubmVsOiBzdHJpbmcsIGNyZWF0ZWRBdDogc3RyaW5nLCBpZDogc3RyaW5nLCBwYXlsb2FkOiB1bmtub3duLCBzZXF1ZW5jZTogbnVtYmVyfX0gLSBOb3JtYWxpemVkIHJvdy5cbiAgICovXG4gIF9ub3JtYWxpemVFdmVudFJvdyhyb3cpIHtcbiAgICBjb25zdCBjcmVhdGVkQXRWYWx1ZSA9IHJvdy5jcmVhdGVkX2F0XG5cbiAgICByZXR1cm4ge1xuICAgICAgY2hhbm5lbDogcm93LmNoYW5uZWwsXG4gICAgICBjcmVhdGVkQXQ6IGNyZWF0ZWRBdFZhbHVlIGluc3RhbmNlb2YgRGF0ZSA/IGNyZWF0ZWRBdFZhbHVlLnRvSVNPU3RyaW5nKCkgOiBuZXcgRGF0ZShjcmVhdGVkQXRWYWx1ZSkudG9JU09TdHJpbmcoKSxcbiAgICAgIGlkOiByb3cuaWQsXG4gICAgICBwYXlsb2FkOiBKU09OLnBhcnNlKHJvdy5wYXlsb2FkX2pzb24pLFxuICAgICAgc2VxdWVuY2U6IE51bWJlcihyb3cuc2VxdWVuY2UpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vZGF0YWJhc2UvZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gRGF0YWJhc2UgY29ubmVjdGlvbi5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5jaGFubmVsIC0gQ2hhbm5lbCBuYW1lLlxuICAgKiBAcGFyYW0ge0RhdGV9IGFyZ3MuaW50ZXJlc3RlZFVudGlsIC0gUmV0ZW50aW9uIGRlYWRsaW5lLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIHRoZSByZXBsYXktY2hhbm5lbCByb3cgd2FzIHVwc2VydGVkLlxuICAgKi9cbiAgYXN5bmMgX3Vwc2VydFJlcGxheUNoYW5uZWxJbnRlcmVzdChkYiwge2NoYW5uZWwsIGludGVyZXN0ZWRVbnRpbH0pIHtcbiAgICBhd2FpdCBkYi51cHNlcnQoe1xuICAgICAgY29uZmxpY3RDb2x1bW5zOiBbXCJjaGFubmVsXCJdLFxuICAgICAgZGF0YToge1xuICAgICAgICBjaGFubmVsLFxuICAgICAgICBpbnRlcmVzdGVkX3VudGlsOiBpbnRlcmVzdGVkVW50aWxcbiAgICAgIH0sXG4gICAgICB0YWJsZU5hbWU6IFJFUExBWV9DSEFOTkVMU19UQUJMRSxcbiAgICAgIHVwZGF0ZUNvbHVtbnM6IFtcImludGVyZXN0ZWRfdW50aWxcIl1cbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7KGRiOiBpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdCkgPT4gUHJvbWlzZTxhbnk+fSBjYWxsYmFjayAtIENhbGxiYWNrLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxhbnk+fSAtIENhbGxiYWNrIHJlc3VsdC5cbiAgICovXG4gIGFzeW5jIF93aXRoRGIoY2FsbGJhY2spIHtcbiAgICByZXR1cm4gYXdhaXQgdGhpcy5jb25maWd1cmF0aW9uLmVuc3VyZUNvbm5lY3Rpb25zKGFzeW5jIChkYnMpID0+IHtcbiAgICAgIGNvbnN0IGRiID0gZGJzW3RoaXMuZGF0YWJhc2VJZGVudGlmaWVyXVxuXG4gICAgICBpZiAoIWRiKSB0aHJvdyBuZXcgRXJyb3IoYE5vIGRhdGFiYXNlIGNvbm5lY3Rpb24gYXZhaWxhYmxlIGZvciBpZGVudGlmaWVyOiAke3RoaXMuZGF0YWJhc2VJZGVudGlmaWVyfWApXG5cbiAgICAgIHJldHVybiBhd2FpdCBjYWxsYmFjayhkYilcbiAgICB9KVxuICB9XG59XG4iXX0=