@pipeline-builder/api-core 4.1.13 → 4.1.15

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 (187) hide show
  1. package/README.md +3 -2
  2. package/lib/constants/index.d.ts +0 -1
  3. package/lib/constants/index.js +1 -2
  4. package/lib/errors/app-errors.d.ts +8 -3
  5. package/lib/errors/app-errors.js +10 -5
  6. package/lib/helpers/access-helpers.d.ts +4 -6
  7. package/lib/helpers/access-helpers.js +6 -8
  8. package/lib/helpers/index.d.ts +3 -3
  9. package/lib/helpers/index.js +4 -4
  10. package/lib/helpers/org-hierarchy-http.d.ts +11 -10
  11. package/lib/helpers/org-hierarchy-http.js +24 -35
  12. package/lib/helpers/org-hierarchy.d.ts +37 -3
  13. package/lib/helpers/org-hierarchy.js +45 -4
  14. package/lib/helpers/restore-helpers.d.ts +44 -24
  15. package/lib/helpers/restore-helpers.js +41 -52
  16. package/lib/helpers/sse-helpers.d.ts +29 -7
  17. package/lib/helpers/sse-helpers.js +36 -9
  18. package/lib/index.d.ts +16 -14
  19. package/lib/index.js +17 -15
  20. package/lib/middleware/assurance.d.ts +116 -0
  21. package/lib/middleware/assurance.js +176 -0
  22. package/lib/middleware/auth.d.ts +5 -427
  23. package/lib/middleware/auth.js +36 -789
  24. package/lib/middleware/ecosystem-guard.d.ts +47 -0
  25. package/lib/middleware/ecosystem-guard.js +60 -0
  26. package/lib/middleware/index.d.ts +11 -4
  27. package/lib/middleware/index.js +12 -5
  28. package/lib/middleware/jwt-verify.d.ts +52 -0
  29. package/lib/middleware/jwt-verify.js +105 -0
  30. package/lib/middleware/permission-gates.d.ts +133 -0
  31. package/lib/middleware/permission-gates.js +260 -0
  32. package/lib/middleware/revocation.d.ts +96 -0
  33. package/lib/middleware/revocation.js +162 -0
  34. package/lib/middleware/route-table.d.ts +11 -0
  35. package/lib/middleware/route-table.js +4 -1
  36. package/lib/middleware/service-tokens.d.ts +83 -0
  37. package/lib/middleware/service-tokens.js +158 -0
  38. package/lib/middleware/step-up.d.ts +2 -2
  39. package/lib/middleware/step-up.js +8 -7
  40. package/lib/middleware/system-org.d.ts +20 -0
  41. package/lib/middleware/system-org.js +25 -0
  42. package/lib/openapi/routes/plugin-routes.js +133 -10
  43. package/lib/openapi/schema-registry.js +2 -5
  44. package/lib/routes/index.d.ts +1 -1
  45. package/lib/routes/index.js +2 -2
  46. package/lib/services/api-key-exchange.js +7 -5
  47. package/lib/services/audit-spool.d.ts +34 -10
  48. package/lib/services/audit-spool.js +75 -34
  49. package/lib/services/cache-service.d.ts +7 -0
  50. package/lib/services/cache-service.js +1 -1
  51. package/lib/services/circuit-breaker.d.ts +14 -1
  52. package/lib/services/circuit-breaker.js +36 -10
  53. package/lib/services/compliance-client.d.ts +10 -2
  54. package/lib/services/compliance-client.js +17 -10
  55. package/lib/services/compliance-event-subscriber.js +6 -6
  56. package/lib/services/durable-event-bus.js +8 -11
  57. package/lib/services/ecosystem-notify-client.d.ts +32 -0
  58. package/lib/services/ecosystem-notify-client.js +71 -0
  59. package/lib/services/env-redis.d.ts +4 -1
  60. package/lib/services/env-redis.js +32 -3
  61. package/lib/services/http-client.d.ts +19 -0
  62. package/lib/services/http-client.js +98 -19
  63. package/lib/services/index.d.ts +25 -24
  64. package/lib/services/index.js +25 -22
  65. package/lib/services/jwks-cache.js +5 -4
  66. package/lib/services/leader-lock.d.ts +22 -5
  67. package/lib/services/leader-lock.js +61 -6
  68. package/lib/services/quota.d.ts +9 -4
  69. package/lib/services/quota.js +34 -40
  70. package/lib/services/remote-audit-client.d.ts +28 -32
  71. package/lib/services/remote-audit-client.js +166 -49
  72. package/lib/services/retry-forever.js +5 -11
  73. package/lib/services/retry-strategy.d.ts +1 -1
  74. package/lib/services/retry-strategy.js +5 -4
  75. package/lib/services/scheduler.d.ts +8 -3
  76. package/lib/services/scheduler.js +17 -5
  77. package/lib/services/service-boot.d.ts +14 -13
  78. package/lib/services/service-boot.js +18 -15
  79. package/lib/services/service-keys.d.ts +1 -1
  80. package/lib/services/service-keys.js +12 -18
  81. package/lib/services/sse-ticket-store.d.ts +9 -1
  82. package/lib/services/sse-ticket-store.js +24 -9
  83. package/lib/services/system-notification.d.ts +27 -0
  84. package/lib/services/system-notification.js +50 -0
  85. package/lib/services/token-revocation.d.ts +50 -1
  86. package/lib/services/token-revocation.js +103 -3
  87. package/lib/types/audit-events.d.ts +6 -6
  88. package/lib/types/audit-events.js +1 -1
  89. package/lib/types/common.d.ts +32 -13
  90. package/lib/types/common.js +2 -1
  91. package/lib/types/ecosystem-notifications.d.ts +142 -0
  92. package/lib/types/ecosystem-notifications.js +210 -0
  93. package/lib/types/ecosystem.d.ts +77 -0
  94. package/lib/types/ecosystem.js +119 -0
  95. package/lib/types/error-codes.d.ts +81 -0
  96. package/lib/types/error-codes.js +108 -1
  97. package/lib/types/feature-flags.d.ts +21 -1
  98. package/lib/types/feature-flags.js +36 -2
  99. package/lib/types/index.d.ts +14 -9
  100. package/lib/types/index.js +15 -10
  101. package/lib/types/permissions.d.ts +46 -8
  102. package/lib/types/permissions.js +75 -12
  103. package/lib/types/plugin-catalog.d.ts +35 -0
  104. package/lib/types/plugin-catalog.js +39 -0
  105. package/lib/types/plugin-health.d.ts +102 -0
  106. package/lib/types/plugin-health.js +184 -0
  107. package/lib/types/quota-tiers.d.ts +15 -0
  108. package/lib/types/quota-tiers.js +23 -3
  109. package/lib/types/wire-vocabulary.d.ts +102 -0
  110. package/lib/types/wire-vocabulary.js +94 -0
  111. package/lib/utils/alias-resolver.js +2 -2
  112. package/lib/utils/audit.d.ts +5 -2
  113. package/lib/utils/audit.js +6 -3
  114. package/lib/utils/compliance-attributes.js +3 -4
  115. package/lib/utils/concurrency.d.ts +7 -0
  116. package/lib/utils/concurrency.js +13 -1
  117. package/lib/utils/env.d.ts +3 -4
  118. package/lib/utils/env.js +4 -5
  119. package/lib/utils/identity.d.ts +13 -4
  120. package/lib/utils/identity.js +18 -10
  121. package/lib/utils/index.d.ts +13 -10
  122. package/lib/utils/index.js +13 -10
  123. package/lib/utils/jwk.d.ts +4 -0
  124. package/lib/utils/jwk.js +13 -1
  125. package/lib/utils/logger.d.ts +1 -1
  126. package/lib/utils/logger.js +1 -1
  127. package/lib/utils/object.d.ts +11 -0
  128. package/lib/utils/object.js +24 -1
  129. package/lib/utils/params.d.ts +8 -22
  130. package/lib/utils/params.js +15 -43
  131. package/lib/utils/plugin-compliance.d.ts +98 -0
  132. package/lib/utils/plugin-compliance.js +78 -0
  133. package/lib/utils/proof-of-work.d.ts +69 -0
  134. package/lib/utils/proof-of-work.js +139 -0
  135. package/lib/utils/response.d.ts +16 -6
  136. package/lib/utils/response.js +18 -15
  137. package/lib/utils/retention.d.ts +17 -0
  138. package/lib/utils/retention.js +30 -0
  139. package/lib/utils/secret-encryption.d.ts +7 -7
  140. package/lib/utils/secret-encryption.js +9 -10
  141. package/lib/utils/secret-rotation.js +2 -2
  142. package/lib/utils/sensitive-patterns.js +4 -5
  143. package/lib/utils/service-registry.d.ts +15 -0
  144. package/lib/utils/service-registry.js +24 -0
  145. package/lib/utils/ssrf.d.ts +11 -2
  146. package/lib/utils/ssrf.js +112 -33
  147. package/lib/validation/ai-schemas.js +3 -2
  148. package/lib/validation/common-schemas.d.ts +0 -9
  149. package/lib/validation/common-schemas.js +1 -7
  150. package/lib/validation/dockerfile-static.d.ts +26 -0
  151. package/lib/validation/dockerfile-static.js +210 -0
  152. package/lib/validation/index.d.ts +13 -4
  153. package/lib/validation/index.js +14 -5
  154. package/lib/validation/message-schemas.js +6 -4
  155. package/lib/validation/pipeline-schemas.d.ts +8 -2
  156. package/lib/validation/pipeline-schemas.js +30 -4
  157. package/lib/validation/plugin-base-images.d.ts +20 -0
  158. package/lib/validation/plugin-base-images.js +17 -0
  159. package/lib/validation/plugin-catalog-detect.d.ts +86 -0
  160. package/lib/validation/plugin-catalog-detect.js +237 -0
  161. package/lib/validation/plugin-catalog-metadata.d.ts +141 -0
  162. package/lib/validation/plugin-catalog-metadata.js +162 -0
  163. package/lib/validation/plugin-heuristics.d.ts +41 -0
  164. package/lib/validation/plugin-heuristics.js +211 -0
  165. package/lib/validation/plugin-lint.d.ts +18 -0
  166. package/lib/validation/plugin-lint.js +136 -0
  167. package/lib/validation/plugin-name-confusable.d.ts +15 -0
  168. package/lib/validation/plugin-name-confusable.js +81 -0
  169. package/lib/validation/plugin-schemas.d.ts +40 -26
  170. package/lib/validation/plugin-schemas.js +26 -23
  171. package/lib/validation/plugin-spec-schema.d.ts +112 -0
  172. package/lib/validation/plugin-spec-schema.js +163 -0
  173. package/lib/validation/plugin-template-contract.d.ts +64 -0
  174. package/lib/validation/plugin-template-contract.js +113 -0
  175. package/package.json +69 -4
  176. package/lib/testing/mock-api-core.d.ts +0 -150
  177. package/lib/testing/mock-api-core.js +0 -246
  178. package/lib/testing/mock-drizzle.d.ts +0 -6
  179. package/lib/testing/mock-drizzle.js +0 -57
  180. package/lib/testing/route-coverage.d.ts +0 -64
  181. package/lib/testing/route-coverage.js +0 -183
  182. package/lib/testing/service-tokens.d.ts +0 -66
  183. package/lib/testing/service-tokens.js +0 -137
  184. package/lib/testing/tier-mock.d.ts +0 -44
  185. package/lib/testing/tier-mock.js +0 -65
  186. package/lib/testing/user-tokens.d.ts +0 -75
  187. package/lib/testing/user-tokens.js +0 -115
@@ -15,9 +15,8 @@
15
15
  * - {@link isAncestorOrgWith} — UP: is A an ancestor of B?
16
16
  * - {@link expandOrgScopeWith} — DOWN: self + all descendant org ids.
17
17
  *
18
- * Every org is flat today (`parentOrgId` null on all rows), so lineage resolves
19
- * to `{ rootOrgId: self }` and scope to `[self]` — i.e. these are no-ops until
20
- * orgs get parents.
18
+ * A root org (`parentOrgId` null) resolves to `{ rootOrgId: self }` and scope
19
+ * `[self]`.
21
20
  */
22
21
  /** Hard ceiling on ancestry/descendant traversal — cycle + abuse guard. */
23
22
  export const MAX_ORG_DEPTH = 16;
@@ -56,6 +55,27 @@ export function createOrgIdCaster(ObjectIdCtor) {
56
55
  return isObjectIdHex(idStr) ? new ObjectIdCtor(idStr) : idStr;
57
56
  };
58
57
  }
58
+ /**
59
+ * The parent/children lookups over a service's own `organizations` model, for
60
+ * the `*With` traversals below. `getChildOrgIds` returns LIVE children only (a
61
+ * soft-deleted team leaves every downward scope); `getParentOrgId` does not
62
+ * filter, so a deleted team still resolves through its parent.
63
+ */
64
+ export function createMongoOrgHierarchy(Model, toOrgId) {
65
+ return {
66
+ async getParentOrgId(orgId) {
67
+ const org = await Model.findById(toOrgId(orgId)).select('parentOrgId').lean();
68
+ return toOrgIdString(org?.parentOrgId);
69
+ },
70
+ async getChildOrgIds(frontier) {
71
+ // `deletedAt: null` matches both an explicit null and an absent field.
72
+ const children = await Model.find({ parentOrgId: { $in: frontier }, deletedAt: null }).select('_id').lean();
73
+ return children
74
+ .map((c) => toOrgIdString(c._id))
75
+ .filter((id) => !!id);
76
+ },
77
+ };
78
+ }
59
79
  /**
60
80
  * Walk `parentOrgId` from `orgId` up to the root. Returns the direct parent (if
61
81
  * any) and the root org id (the input itself when the org has no parent). On a
@@ -85,6 +105,27 @@ export async function resolveOrgLineageWith(orgId, getParent) {
85
105
  export async function resolveRootOrgIdWith(orgId, getParent) {
86
106
  return (await resolveOrgLineageWith(orgId, getParent)).rootOrgId;
87
107
  }
108
+ /**
109
+ * Fail-CLOSED variant of {@link resolveRootOrgIdWith}: returns `null` when the
110
+ * root can't be proven — a cycle or a chain deeper than {@link MAX_ORG_DEPTH} —
111
+ * instead of treating the last reached id as the root. Lookup errors propagate.
112
+ * For callers where a wrong root is worse than no answer (e.g. a retention purge
113
+ * that would otherwise apply a team's shorter default window).
114
+ */
115
+ export async function resolveRootOrgIdStrict(orgId, getParent) {
116
+ const seen = new Set([orgId]);
117
+ let currentId = orgId;
118
+ for (let depth = 0; depth < MAX_ORG_DEPTH; depth++) {
119
+ const parent = toOrgIdString(await getParent(currentId));
120
+ if (!parent || parent === currentId)
121
+ return currentId;
122
+ if (seen.has(parent))
123
+ return null;
124
+ seen.add(parent);
125
+ currentId = parent;
126
+ }
127
+ return null;
128
+ }
88
129
  /**
89
130
  * True when `ancestorOrgId` is an ancestor of `candidateOrgId` — i.e.
90
131
  * `candidateOrgId` lives somewhere in the subtree rooted at `ancestorOrgId`.
@@ -135,4 +176,4 @@ export async function expandOrgScopeWith(orgId, getChildren) {
135
176
  }
136
177
  return result;
137
178
  }
138
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JnLWhpZXJhcmNoeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9oZWxwZXJzL29yZy1oaWVyYXJjaHkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0JHO0FBRUgsMkVBQTJFO0FBQzNFLE1BQU0sQ0FBQyxNQUFNLGFBQWEsR0FBRyxFQUFFLENBQUM7QUFRaEMsb0ZBQW9GO0FBQ3BGLE1BQU0sVUFBVSxhQUFhLENBQUMsQ0FBVTtJQUN0QyxJQUFJLENBQUMsS0FBSyxJQUFJLElBQUksQ0FBQyxLQUFLLFNBQVM7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUNwRCxNQUFNLENBQUMsR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDcEIsT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLE1BQU0sSUFBSSxDQUFDLEtBQUssV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztBQUNoRSxDQUFDO0FBRUQsa0ZBQWtGO0FBQ2xGLFNBQVMsYUFBYSxDQUFDLEVBQVU7SUFDL0IsT0FBTyxFQUFFLENBQUMsTUFBTSxLQUFLLEVBQUUsSUFBSSxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7QUFDMUQsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7OztHQWlCRztBQUNILE1BQU0sVUFBVSxpQkFBaUIsQ0FDL0IsWUFBb0M7SUFFcEMsT0FBTyxDQUFDLEVBQUUsRUFBRSxFQUFFO1FBQ1osTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDN0MsT0FBTyxhQUFhLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksWUFBWSxDQUFDLEtBQUssQ0FB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YUFBYSxJQUFJLFFBQVEsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUM7UUFDMUUsTUFBTSxRQUFRLEdBQUcsTUFBTSxXQUFXLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDN0MsTUFBTSxJQUFJLEdBQWEsRUFBRSxDQUFDO1FBQzFCLEtBQUssTUFBTSxHQUFHLElBQUksUUFBUSxFQUFFLENBQUM7WUFDM0IsTUFBTSxFQUFFLEdBQUcsYUFBYSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQzlCLElBQUksQ0FBQyxFQUFFLElBQUksSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQUUsU0FBUztZQUNsQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ2IsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUNoQixJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ2hCLENBQUM7UUFDRCxRQUFRLEdBQUcsSUFBSSxDQUFDO0lBQ2xCLENBQUM7SUFFRCxPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogT3JnIOKGkiB0ZWFtIGhpZXJhcmNoeSB0cmF2ZXJzYWwgKG9yZy10ZWFtLWhpZXJhcmNoeSBwcm9wb3NhbCkuXG4gKlxuICogQSBcInRlYW1cIiBpcyBhbiBvcmdhbml6YXRpb24gd2hvc2UgYHBhcmVudE9yZ0lkYCBwb2ludHMgYXQgYW5vdGhlciBvcmc7IGEgcm9vdFxuICogb3JnIGhhcyBgcGFyZW50T3JnSWQgPSBudWxsYC4gVGhlc2UgaGVscGVycyB3YWxrIHRoYXQgc2luZ2xlIHNlbGYtcmVmZXJlbnRpYWxcbiAqIGNvbHVtbiwgYnV0IGhvbGQgKipubyBkYXRhYmFzZSBkZXBlbmRlbmN5Kiogb2YgdGhlaXIgb3duOiBlYWNoIHRha2VzIGEgcXVlcnlcbiAqIGNhbGxiYWNrIHNvIGV2ZXJ5IHNlcnZpY2UgKHBsYXRmb3JtLCBxdW90YSwg4oCmKSBjYW4gc2hhcmUgdGhlIGN5Y2xlLXNhZmUsXG4gKiBkZXB0aC1jYXBwZWQgdHJhdmVyc2FsIGxvZ2ljIHdoaWxlIHJlYWRpbmcgaXRzIG93biBNb25nb29zZSBtb2RlbCB3aXRoIGl0cyBvd25cbiAqIGBfaWRgIGNhc3RpbmcgcnVsZXMuXG4gKlxuICogICAtIHtAbGluayByZXNvbHZlT3JnTGluZWFnZVdpdGh9IOKAlCBVUDogZGlyZWN0IHBhcmVudCArIHJvb3Qgb2YgdGhlIGNoYWluLlxuICogICAtIHtAbGluayByZXNvbHZlUm9vdE9yZ0lkV2l0aH0gICDigJQgVVA6IGp1c3QgdGhlIHJvb3QgaWQuXG4gKiAgIC0ge0BsaW5rIGlzQW5jZXN0b3JPcmdXaXRofSAgICAgIOKAlCBVUDogaXMgQSBhbiBhbmNlc3RvciBvZiBCP1xuICogICAtIHtAbGluayBleHBhbmRPcmdTY29wZVdpdGh9ICAgICDigJQgRE9XTjogc2VsZiArIGFsbCBkZXNjZW5kYW50IG9yZyBpZHMuXG4gKlxuICogRXZlcnkgb3JnIGlzIGZsYXQgdG9kYXkgKGBwYXJlbnRPcmdJZGAgbnVsbCBvbiBhbGwgcm93cyksIHNvIGxpbmVhZ2UgcmVzb2x2ZXNcbiAqIHRvIGB7IHJvb3RPcmdJZDogc2VsZiB9YCBhbmQgc2NvcGUgdG8gYFtzZWxmXWAg4oCUIGkuZS4gdGhlc2UgYXJlIG5vLW9wcyB1bnRpbFxuICogb3JncyBnZXQgcGFyZW50cy5cbiAqL1xuXG4vKiogSGFyZCBjZWlsaW5nIG9uIGFuY2VzdHJ5L2Rlc2NlbmRhbnQgdHJhdmVyc2FsIOKAlCBjeWNsZSArIGFidXNlIGd1YXJkLiAqL1xuZXhwb3J0IGNvbnN0IE1BWF9PUkdfREVQVEggPSAxNjtcblxuLyoqIEZldGNoIGFuIG9yZydzIGRpcmVjdCBgcGFyZW50T3JnSWRgLCBvciB1bmRlZmluZWQgZm9yIGEgcm9vdC9taXNzaW5nIG9yZy4gKi9cbmV4cG9ydCB0eXBlIEdldFBhcmVudE9yZ0lkID0gKG9yZ0lkOiBzdHJpbmcpID0+IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPjtcblxuLyoqIEZldGNoIHRoZSBkaXJlY3QgY2hpbGQgb3JnIGlkcyBvZiBldmVyeSBvcmcgaW4gYGZyb250aWVyYCAob25lIHJvdW5kIG9mIEJGUykuICovXG5leHBvcnQgdHlwZSBHZXRDaGlsZE9yZ0lkcyA9IChmcm9udGllcjogc3RyaW5nW10pID0+IFByb21pc2U8c3RyaW5nW10+O1xuXG4vKiogTm9ybWFsaXplIGEgTWl4ZWQvT2JqZWN0SWQvc3RyaW5nIG9yZyBpZCB0byBhIG5vbi1lbXB0eSBzdHJpbmcsIG9yIHVuZGVmaW5lZC4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0b09yZ0lkU3RyaW5nKHY6IHVua25vd24pOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBpZiAodiA9PT0gbnVsbCB8fCB2ID09PSB1bmRlZmluZWQpIHJldHVybiB1bmRlZmluZWQ7XG4gIGNvbnN0IHMgPSBTdHJpbmcodik7XG4gIHJldHVybiBzICYmIHMgIT09ICdudWxsJyAmJiBzICE9PSAndW5kZWZpbmVkJyA/IHMgOiB1bmRlZmluZWQ7XG59XG5cbi8qKiBUcnVlIHdoZW4gYGlkYCBpcyBhIDI0LWNoYXJhY3RlciBoZXggc3RyaW5nIOKAlCBpLmUuIGNhc3RhYmxlIHRvIGFuIE9iamVjdElkLiAqL1xuZnVuY3Rpb24gaXNPYmplY3RJZEhleChpZDogc3RyaW5nKTogYm9vbGVhbiB7XG4gIHJldHVybiBpZC5sZW5ndGggPT09IDI0ICYmIC9eWzAtOWEtZkEtRl17MjR9JC8udGVzdChpZCk7XG59XG5cbi8qKlxuICogQnVpbGQgdGhlIG9yZy1pZCBjYXN0IHRoZSBNb25nby1iYWNrZWQgc2VydmljZXMgdXNlIG9uIGV2ZXJ5IGBfaWRgIGxvb2t1cC5cbiAqXG4gKiBQbGF0Zm9ybSB3cml0ZXMgT2JqZWN0SWQgYF9pZGBzIGludG8gdGhlIHNoYXJlZCBgb3JnYW5pemF0aW9uc2AgY29sbGVjdGlvbixcbiAqIHdoaWxlIHRoZSB3ZWxsLWtub3duIGAnc3lzdGVtJ2Agb3JnIChhbmQgYHBhcmVudE9yZ0lkYCwgc3RpbGwgYSBTdHJpbmcgY29sdW1uKVxuICogYXJlIHBsYWluIHN0cmluZ3Mg4oCUIHNvIGEgMjQtaGV4IGlkIGFycml2aW5nIGZyb20gYSByb3V0ZSBwYXJhbSAvIEpXVCBjbGFpbSAvXG4gKiBjcm9zcy1zZXJ2aWNlIHBheWxvYWQgbXVzdCBiZSBjYXN0IG9yIGBmaW5kQnlJZCgnPDI0aGV4PicpYCBuZXZlciBtYXRjaGVzLlxuICpcbiAqIFBsYXRmb3JtIGFuZCBxdW90YSBlYWNoIGhhZCB0aGVpciBPV04gYHRvT3JnSWRgIHdpdGggYSBkaWZmZXJlbnQgc2lnbmF0dXJlXG4gKiAoYHN0cmluZyB8IHN0cmluZ1tdYCB2cyBgc3RyaW5nYCksIHdoaWNoIGlzIGV4YWN0bHkgdGhlIGRyaWZ0IHRoaXMgcmVtb3ZlczpcbiAqIHRoZSBsb2dpYyBhbmQgdGhlIHNpZ25hdHVyZSBsaXZlIGhlcmUgb25jZSwgYW5kIGVhY2ggc2VydmljZSBzdXBwbGllcyBvbmx5XG4gKiBpdHMgbW9uZ29vc2UgYFR5cGVzLk9iamVjdElkYCBjb25zdHJ1Y3Rvci4gYXBpLWNvcmUgY2Fubm90IGltcG9ydCBtb25nb29zZVxuICogaXRzZWxmIChpdCBpcyBub3Qg4oCUIGFuZCBzaG91bGQgbm90IGJlY29tZSDigJQgYW4gYXBpLWNvcmUgZGVwZW5kZW5jeSksIHNvIHRoZVxuICogY29uc3RydWN0b3IgaXMgaW5qZWN0ZWQgcmF0aGVyIHRoYW4gaW1wb3J0ZWQuXG4gKlxuICogQGV4YW1wbGVcbiAqIGV4cG9ydCBjb25zdCB0b09yZ0lkID0gY3JlYXRlT3JnSWRDYXN0ZXIobW9uZ29vc2UuVHlwZXMuT2JqZWN0SWQpO1xuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlT3JnSWRDYXN0ZXI8VD4oXG4gIE9iamVjdElkQ3RvcjogbmV3IChoZXg6IHN0cmluZykgPT4gVCxcbik6IChpZDogc3RyaW5nIHwgc3RyaW5nW10pID0+IHN0cmluZyB8IFQge1xuICByZXR1cm4gKGlkKSA9PiB7XG4gICAgY29uc3QgaWRTdHIgPSBBcnJheS5pc0FycmF5KGlkKSA/IGlkWzBdIDogaWQ7XG4gICAgcmV0dXJuIGlzT2JqZWN0SWRIZXgoaWRTdHIpID8gbmV3IE9iamVjdElkQ3RvcihpZFN0cikgOiBpZFN0cjtcbiAgfTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBPcmdMaW5lYWdlIHtcbiAgLyoqIFRoZSBvcmcncyBkaXJlY3QgcGFyZW50IGlkLCBvciBgdW5kZWZpbmVkYCB3aGVuIGl0J3MgYSByb290IG9yZy4gKi9cbiAgcGFyZW50T3JnSWQ/OiBzdHJpbmc7XG4gIC8qKiBUb3Agb2YgdGhlIGFuY2VzdHJ5IGNoYWluLiBFcXVhbHMgdGhlIGlucHV0IGBvcmdJZGAgZm9yIGEgcm9vdCBvcmcuICovXG4gIHJvb3RPcmdJZDogc3RyaW5nO1xufVxuXG4vKipcbiAqIFdhbGsgYHBhcmVudE9yZ0lkYCBmcm9tIGBvcmdJZGAgdXAgdG8gdGhlIHJvb3QuIFJldHVybnMgdGhlIGRpcmVjdCBwYXJlbnQgKGlmXG4gKiBhbnkpIGFuZCB0aGUgcm9vdCBvcmcgaWQgKHRoZSBpbnB1dCBpdHNlbGYgd2hlbiB0aGUgb3JnIGhhcyBubyBwYXJlbnQpLiBPbiBhXG4gKiBkZXRlY3RlZCBjeWNsZSwgYSBtaXNzaW5nIG9yZywgb3IgdGhlIGRlcHRoIGNhcCwgdHJhdmVyc2FsIHN0b3BzIGFuZCB0aGUgbGFzdFxuICogcmVhY2hlZCBpZCBpcyB0cmVhdGVkIGFzIHRoZSByb290LlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVzb2x2ZU9yZ0xpbmVhZ2VXaXRoKG9yZ0lkOiBzdHJpbmcsIGdldFBhcmVudDogR2V0UGFyZW50T3JnSWQpOiBQcm9taXNlPE9yZ0xpbmVhZ2U+IHtcbiAgY29uc3Qgc2VlbiA9IG5ldyBTZXQ8c3RyaW5nPihbb3JnSWRdKTtcbiAgbGV0IGN1cnJlbnRJZCA9IG9yZ0lkO1xuICBsZXQgcGFyZW50T3JnSWQ6IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICBmb3IgKGxldCBkZXB0aCA9IDA7IGRlcHRoIDwgTUFYX09SR19ERVBUSDsgZGVwdGgrKykge1xuICAgIGNvbnN0IHBhcmVudCA9IHRvT3JnSWRTdHJpbmcoYXdhaXQgZ2V0UGFyZW50KGN1cnJlbnRJZCkpO1xuICAgIGlmICghcGFyZW50KSBicmVhazsgLy8gY3VycmVudElkIGlzIHRoZSByb290XG4gICAgLy8gUmVjb3JkIHRoZSBkaXJlY3QgcGFyZW50IG9ubHkgd2hlbiBpdCBpc24ndCBhIHNlbGYtbG9vcCwgc28gYSBtYWxmb3JtZWRcbiAgICAvLyBB4oaSQSBvcmcgaXNuJ3QgcmVwb3J0ZWQgYXMgaXRzIG93biBwYXJlbnQuXG4gICAgaWYgKGRlcHRoID09PSAwICYmIHBhcmVudCAhPT0gb3JnSWQpIHBhcmVudE9yZ0lkID0gcGFyZW50O1xuICAgIGlmIChzZWVuLmhhcyhwYXJlbnQpKSBicmVhazsgLy8gY3ljbGUg4oCUIHRyZWF0IGN1cnJlbnRJZCBhcyB0aGUgcm9vdFxuICAgIHNlZW4uYWRkKHBhcmVudCk7XG4gICAgY3VycmVudElkID0gcGFyZW50O1xuICB9XG5cbiAgcmV0dXJuIHsgcm9vdE9yZ0lkOiBjdXJyZW50SWQsIC4uLihwYXJlbnRPcmdJZCAmJiB7IHBhcmVudE9yZ0lkIH0pIH07XG59XG5cbi8qKiBXYWxrIGBwYXJlbnRPcmdJZGAgdXAgdG8gdGhlIHJvb3QgYW5kIHJldHVybiBqdXN0IHRoZSByb290IGlkLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlc29sdmVSb290T3JnSWRXaXRoKG9yZ0lkOiBzdHJpbmcsIGdldFBhcmVudDogR2V0UGFyZW50T3JnSWQpOiBQcm9taXNlPHN0cmluZz4ge1xuICByZXR1cm4gKGF3YWl0IHJlc29sdmVPcmdMaW5lYWdlV2l0aChvcmdJZCwgZ2V0UGFyZW50KSkucm9vdE9yZ0lkO1xufVxuXG4vKipcbiAqIFRydWUgd2hlbiBgYW5jZXN0b3JPcmdJZGAgaXMgYW4gYW5jZXN0b3Igb2YgYGNhbmRpZGF0ZU9yZ0lkYCDigJQgaS5lLlxuICogYGNhbmRpZGF0ZU9yZ0lkYCBsaXZlcyBzb21ld2hlcmUgaW4gdGhlIHN1YnRyZWUgcm9vdGVkIGF0IGBhbmNlc3Rvck9yZ0lkYC5cbiAqIFdhbGtzIGBjYW5kaWRhdGVPcmdJZGAncyBwYXJlbnQgY2hhaW4gdXB3YXJkLCBkZXB0aC1jYXBwZWQgYW5kIGN5Y2xlLXNhZmUuXG4gKiBSZXR1cm5zIGBmYWxzZWAgd2hlbiB0aGUgdHdvIGFyZSBlcXVhbCAoYW4gb3JnIGlzIG5vdCBpdHMgb3duIGFuY2VzdG9yKSBvclxuICogdW5yZWxhdGVkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaXNBbmNlc3Rvck9yZ1dpdGgoXG4gIGFuY2VzdG9yT3JnSWQ6IHN0cmluZyxcbiAgY2FuZGlkYXRlT3JnSWQ6IHN0cmluZyxcbiAgZ2V0UGFyZW50OiBHZXRQYXJlbnRPcmdJZCxcbik6IFByb21pc2U8Ym9vbGVhbj4ge1xuICBpZiAoYW5jZXN0b3JPcmdJZCA9PT0gY2FuZGlkYXRlT3JnSWQpIHJldHVybiBmYWxzZTtcblxuICBjb25zdCBzZWVuID0gbmV3IFNldDxzdHJpbmc+KFtjYW5kaWRhdGVPcmdJZF0pO1xuICBsZXQgY3VycmVudElkID0gY2FuZGlkYXRlT3JnSWQ7XG5cbiAgZm9yIChsZXQgZGVwdGggPSAwOyBkZXB0aCA8IE1BWF9PUkdfREVQVEg7IGRlcHRoKyspIHtcbiAgICBjb25zdCBwYXJlbnQgPSB0b09yZ0lkU3RyaW5nKGF3YWl0IGdldFBhcmVudChjdXJyZW50SWQpKTtcbiAgICBpZiAoIXBhcmVudCkgcmV0dXJuIGZhbHNlOyAvLyByZWFjaGVkIGEgcm9vdCB3aXRob3V0IG1hdGNoaW5nXG4gICAgaWYgKHBhcmVudCA9PT0gYW5jZXN0b3JPcmdJZCkgcmV0dXJuIHRydWU7XG4gICAgaWYgKHNlZW4uaGFzKHBhcmVudCkpIHJldHVybiBmYWxzZTsgLy8gY3ljbGVcbiAgICBzZWVuLmFkZChwYXJlbnQpO1xuICAgIGN1cnJlbnRJZCA9IHBhcmVudDtcbiAgfVxuXG4gIHJldHVybiBmYWxzZTtcbn1cblxuLyoqXG4gKiBFeHBhbmQgYG9yZ0lkYCB0byBpdHNlbGYgcGx1cyBldmVyeSBkZXNjZW5kYW50IG9yZyBpZCAoYnJlYWR0aC1maXJzdCBvdmVyXG4gKiBgcGFyZW50T3JnSWRgKS4gVGhpcyBpcyB0aGUgc2NvcGUgYSBwYXJlbnQgb3JnIFwic2Vlc1wiIGZvciB2aXNpYmlsaXR5IGFuZFxuICogYW5hbHl0aWNzIHJvbGx1cHM6IGBbc2VsZiwgLi4uZGVzY2VuZGFudHNdYC4gUmV0dXJucyBgW29yZ0lkXWAgd2hlbiB0aGUgb3JnXG4gKiBoYXMgbm8gY2hpbGRyZW4uIEN5Y2xlLXNhZmUgYW5kIGRlcHRoLWNhcHBlZC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGV4cGFuZE9yZ1Njb3BlV2l0aChvcmdJZDogc3RyaW5nLCBnZXRDaGlsZHJlbjogR2V0Q2hpbGRPcmdJZHMpOiBQcm9taXNlPHN0cmluZ1tdPiB7XG4gIGNvbnN0IHJlc3VsdDogc3RyaW5nW10gPSBbb3JnSWRdO1xuICBjb25zdCBzZWVuID0gbmV3IFNldDxzdHJpbmc+KFtvcmdJZF0pO1xuICBsZXQgZnJvbnRpZXI6IHN0cmluZ1tdID0gW29yZ0lkXTtcblxuICBmb3IgKGxldCBkZXB0aCA9IDA7IGRlcHRoIDwgTUFYX09SR19ERVBUSCAmJiBmcm9udGllci5sZW5ndGggPiAwOyBkZXB0aCsrKSB7XG4gICAgY29uc3QgY2hpbGRyZW4gPSBhd2FpdCBnZXRDaGlsZHJlbihmcm9udGllcik7XG4gICAgY29uc3QgbmV4dDogc3RyaW5nW10gPSBbXTtcbiAgICBmb3IgKGNvbnN0IHJhdyBvZiBjaGlsZHJlbikge1xuICAgICAgY29uc3QgaWQgPSB0b09yZ0lkU3RyaW5nKHJhdyk7XG4gICAgICBpZiAoIWlkIHx8IHNlZW4uaGFzKGlkKSkgY29udGludWU7XG4gICAgICBzZWVuLmFkZChpZCk7XG4gICAgICByZXN1bHQucHVzaChpZCk7XG4gICAgICBuZXh0LnB1c2goaWQpO1xuICAgIH1cbiAgICBmcm9udGllciA9IG5leHQ7XG4gIH1cblxuICByZXR1cm4gcmVzdWx0O1xufVxuIl19
179
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JnLWhpZXJhcmNoeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9oZWxwZXJzL29yZy1oaWVyYXJjaHkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpQkc7QUFFSCwyRUFBMkU7QUFDM0UsTUFBTSxDQUFDLE1BQU0sYUFBYSxHQUFHLEVBQUUsQ0FBQztBQVFoQyxvRkFBb0Y7QUFDcEYsTUFBTSxVQUFVLGFBQWEsQ0FBQyxDQUFVO0lBQ3RDLElBQUksQ0FBQyxLQUFLLElBQUksSUFBSSxDQUFDLEtBQUssU0FBUztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ3BELE1BQU0sQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNwQixPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssTUFBTSxJQUFJLENBQUMsS0FBSyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0FBQ2hFLENBQUM7QUFFRCxrRkFBa0Y7QUFDbEYsU0FBUyxhQUFhLENBQUMsRUFBVTtJQUMvQixPQUFPLEVBQUUsQ0FBQyxNQUFNLEtBQUssRUFBRSxJQUFJLG1CQUFtQixDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztBQUMxRCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaUJHO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQixDQUMvQixZQUFvQztJQUVwQyxPQUFPLENBQUMsRUFBRSxFQUFFLEVBQUU7UUFDWixNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUM3QyxPQUFPLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxZQUFZLENBQUMsS0FBSyxDQUFD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Q0FBQztJQUNyQixDQUFDO0lBRUQsT0FBTyxLQUFLLENBQUM7QUFDZixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGtCQUFrQixDQUFDLEtBQWEsRUFBRSxXQUEyQjtJQUNqRixNQUFNLE1BQU0sR0FBYSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ2pDLE1BQU0sSUFBSSxHQUFHLElBQUksR0FBRyxDQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUN0QyxJQUFJLFFBQVEsR0FBYSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBRWpDLEtBQUssSUFBSSxLQUFLLEdBQUcsQ0FBQyxFQUFFLEtBQUssR0FBRyxhQUFhLElBQUksUUFBUSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsS0FBSyxFQUFFLEVBQUUsQ0FBQztRQUMxRSxNQUFNLFFBQVEsR0FBRyxNQUFNLFdBQVcsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUM3QyxNQUFNLElBQUksR0FBYSxFQUFFLENBQUM7UUFDMUIsS0FBSyxNQUFNLEdBQUcsSUFBSSxRQUFRLEVBQUUsQ0FBQztZQUMzQixNQUFNLEVBQUUsR0FBRyxhQUFhLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDOUIsSUFBSSxDQUFDLEVBQUUsSUFBSSxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztnQkFBRSxTQUFTO1lBQ2xDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDYixNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ2hCLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDaEIsQ0FBQztRQUNELFFBQVEsR0FBRyxJQUFJLENBQUM7SUFDbEIsQ0FBQztJQUVELE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBPcmcg4oaSIHRlYW0gaGllcmFyY2h5IHRyYXZlcnNhbCAob3JnLXRlYW0taGllcmFyY2h5IHByb3Bvc2FsKS5cbiAqXG4gKiBBIFwidGVhbVwiIGlzIGFuIG9yZ2FuaXphdGlvbiB3aG9zZSBgcGFyZW50T3JnSWRgIHBvaW50cyBhdCBhbm90aGVyIG9yZzsgYSByb290XG4gKiBvcmcgaGFzIGBwYXJlbnRPcmdJZCA9IG51bGxgLiBUaGVzZSBoZWxwZXJzIHdhbGsgdGhhdCBzaW5nbGUgc2VsZi1yZWZlcmVudGlhbFxuICogY29sdW1uLCBidXQgaG9sZCAqKm5vIGRhdGFiYXNlIGRlcGVuZGVuY3kqKiBvZiB0aGVpciBvd246IGVhY2ggdGFrZXMgYSBxdWVyeVxuICogY2FsbGJhY2sgc28gZXZlcnkgc2VydmljZSAocGxhdGZvcm0sIHF1b3RhLCDigKYpIGNhbiBzaGFyZSB0aGUgY3ljbGUtc2FmZSxcbiAqIGRlcHRoLWNhcHBlZCB0cmF2ZXJzYWwgbG9naWMgd2hpbGUgcmVhZGluZyBpdHMgb3duIE1vbmdvb3NlIG1vZGVsIHdpdGggaXRzIG93blxuICogYF9pZGAgY2FzdGluZyBydWxlcy5cbiAqXG4gKiAgIC0ge0BsaW5rIHJlc29sdmVPcmdMaW5lYWdlV2l0aH0g4oCUIFVQOiBkaXJlY3QgcGFyZW50ICsgcm9vdCBvZiB0aGUgY2hhaW4uXG4gKiAgIC0ge0BsaW5rIHJlc29sdmVSb290T3JnSWRXaXRofSAgIOKAlCBVUDoganVzdCB0aGUgcm9vdCBpZC5cbiAqICAgLSB7QGxpbmsgaXNBbmNlc3Rvck9yZ1dpdGh9ICAgICAg4oCUIFVQOiBpcyBBIGFuIGFuY2VzdG9yIG9mIEI/XG4gKiAgIC0ge0BsaW5rIGV4cGFuZE9yZ1Njb3BlV2l0aH0gICAgIOKAlCBET1dOOiBzZWxmICsgYWxsIGRlc2NlbmRhbnQgb3JnIGlkcy5cbiAqXG4gKiBBIHJvb3Qgb3JnIChgcGFyZW50T3JnSWRgIG51bGwpIHJlc29sdmVzIHRvIGB7IHJvb3RPcmdJZDogc2VsZiB9YCBhbmQgc2NvcGVcbiAqIGBbc2VsZl1gLlxuICovXG5cbi8qKiBIYXJkIGNlaWxpbmcgb24gYW5jZXN0cnkvZGVzY2VuZGFudCB0cmF2ZXJzYWwg4oCUIGN5Y2xlICsgYWJ1c2UgZ3VhcmQuICovXG5leHBvcnQgY29uc3QgTUFYX09SR19ERVBUSCA9IDE2O1xuXG4vKiogRmV0Y2ggYW4gb3JnJ3MgZGlyZWN0IGBwYXJlbnRPcmdJZGAsIG9yIHVuZGVmaW5lZCBmb3IgYSByb290L21pc3Npbmcgb3JnLiAqL1xuZXhwb3J0IHR5cGUgR2V0UGFyZW50T3JnSWQgPSAob3JnSWQ6IHN0cmluZykgPT4gUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+O1xuXG4vKiogRmV0Y2ggdGhlIGRpcmVjdCBjaGlsZCBvcmcgaWRzIG9mIGV2ZXJ5IG9yZyBpbiBgZnJvbnRpZXJgIChvbmUgcm91bmQgb2YgQkZTKS4gKi9cbmV4cG9ydCB0eXBlIEdldENoaWxkT3JnSWRzID0gKGZyb250aWVyOiBzdHJpbmdbXSkgPT4gUHJvbWlzZTxzdHJpbmdbXT47XG5cbi8qKiBOb3JtYWxpemUgYSBNaXhlZC9PYmplY3RJZC9zdHJpbmcgb3JnIGlkIHRvIGEgbm9uLWVtcHR5IHN0cmluZywgb3IgdW5kZWZpbmVkLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHRvT3JnSWRTdHJpbmcodjogdW5rbm93bik6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGlmICh2ID09PSBudWxsIHx8IHYgPT09IHVuZGVmaW5lZCkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgY29uc3QgcyA9IFN0cmluZyh2KTtcbiAgcmV0dXJuIHMgJiYgcyAhPT0gJ251bGwnICYmIHMgIT09ICd1bmRlZmluZWQnID8gcyA6IHVuZGVmaW5lZDtcbn1cblxuLyoqIFRydWUgd2hlbiBgaWRgIGlzIGEgMjQtY2hhcmFjdGVyIGhleCBzdHJpbmcg4oCUIGkuZS4gY2FzdGFibGUgdG8gYW4gT2JqZWN0SWQuICovXG5mdW5jdGlvbiBpc09iamVjdElkSGV4KGlkOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgcmV0dXJuIGlkLmxlbmd0aCA9PT0gMjQgJiYgL15bMC05YS1mQS1GXXsyNH0kLy50ZXN0KGlkKTtcbn1cblxuLyoqXG4gKiBCdWlsZCB0aGUgb3JnLWlkIGNhc3QgdGhlIE1vbmdvLWJhY2tlZCBzZXJ2aWNlcyB1c2Ugb24gZXZlcnkgYF9pZGAgbG9va3VwLlxuICpcbiAqIFBsYXRmb3JtIHdyaXRlcyBPYmplY3RJZCBgX2lkYHMgaW50byB0aGUgc2hhcmVkIGBvcmdhbml6YXRpb25zYCBjb2xsZWN0aW9uLFxuICogd2hpbGUgdGhlIHdlbGwta25vd24gYCdzeXN0ZW0nYCBvcmcgKGFuZCBgcGFyZW50T3JnSWRgLCBzdGlsbCBhIFN0cmluZyBjb2x1bW4pXG4gKiBhcmUgcGxhaW4gc3RyaW5ncyDigJQgc28gYSAyNC1oZXggaWQgYXJyaXZpbmcgZnJvbSBhIHJvdXRlIHBhcmFtIC8gSldUIGNsYWltIC9cbiAqIGNyb3NzLXNlcnZpY2UgcGF5bG9hZCBtdXN0IGJlIGNhc3Qgb3IgYGZpbmRCeUlkKCc8MjRoZXg+JylgIG5ldmVyIG1hdGNoZXMuXG4gKlxuICogUGxhdGZvcm0gYW5kIHF1b3RhIGVhY2ggaGFkIHRoZWlyIE9XTiBgdG9PcmdJZGAgd2l0aCBhIGRpZmZlcmVudCBzaWduYXR1cmVcbiAqIChgc3RyaW5nIHwgc3RyaW5nW11gIHZzIGBzdHJpbmdgKSwgd2hpY2ggaXMgZXhhY3RseSB0aGUgZHJpZnQgdGhpcyByZW1vdmVzOlxuICogdGhlIGxvZ2ljIGFuZCB0aGUgc2lnbmF0dXJlIGxpdmUgaGVyZSBvbmNlLCBhbmQgZWFjaCBzZXJ2aWNlIHN1cHBsaWVzIG9ubHlcbiAqIGl0cyBtb25nb29zZSBgVHlwZXMuT2JqZWN0SWRgIGNvbnN0cnVjdG9yLiBhcGktY29yZSBjYW5ub3QgaW1wb3J0IG1vbmdvb3NlXG4gKiBpdHNlbGYgKGl0IGlzIG5vdCDigJQgYW5kIHNob3VsZCBub3QgYmVjb21lIOKAlCBhbiBhcGktY29yZSBkZXBlbmRlbmN5KSwgc28gdGhlXG4gKiBjb25zdHJ1Y3RvciBpcyBpbmplY3RlZCByYXRoZXIgdGhhbiBpbXBvcnRlZC5cbiAqXG4gKiBAZXhhbXBsZVxuICogZXhwb3J0IGNvbnN0IHRvT3JnSWQgPSBjcmVhdGVPcmdJZENhc3Rlcihtb25nb29zZS5UeXBlcy5PYmplY3RJZCk7XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVPcmdJZENhc3RlcjxUPihcbiAgT2JqZWN0SWRDdG9yOiBuZXcgKGhleDogc3RyaW5nKSA9PiBULFxuKTogKGlkOiBzdHJpbmcgfCBzdHJpbmdbXSkgPT4gc3RyaW5nIHwgVCB7XG4gIHJldHVybiAoaWQpID0+IHtcbiAgICBjb25zdCBpZFN0ciA9IEFycmF5LmlzQXJyYXkoaWQpID8gaWRbMF0gOiBpZDtcbiAgICByZXR1cm4gaXNPYmplY3RJZEhleChpZFN0cikgPyBuZXcgT2JqZWN0SWRDdG9yKGlkU3RyKSA6IGlkU3RyO1xuICB9O1xufVxuXG4vKipcbiAqIFRoZSBzbGljZSBvZiBhIE1vbmdvb3NlIGBPcmdhbml6YXRpb25gIG1vZGVsIHRoZSBoaWVyYXJjaHkgY2FsbGJhY2tzIHJlYWQuXG4gKiBTdHJ1Y3R1cmFsIChhcGktY29yZSBoYXMgbm8gbW9uZ29vc2UgZGVwZW5kZW5jeSk7IGBzZWxlY3QoKS5sZWFuKClgIGNoYWlucyBhc1xuICogbW9uZ29vc2UgcXVlcmllcyBkby5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBPcmdIaWVyYXJjaHlNb2RlbCB7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55XG4gIGZpbmRCeUlkKGlkOiBhbnkpOiB7IHNlbGVjdChmaWVsZHM6IHN0cmluZyk6IHsgbGVhbigpOiBQcm9taXNlTGlrZTx1bmtub3duPiB9IH07XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55XG4gIGZpbmQoZmlsdGVyOiBhbnkpOiB7IHNlbGVjdChmaWVsZHM6IHN0cmluZyk6IHsgbGVhbigpOiBQcm9taXNlTGlrZTx1bmtub3duW10+IH0gfTtcbn1cblxuLyoqXG4gKiBUaGUgcGFyZW50L2NoaWxkcmVuIGxvb2t1cHMgb3ZlciBhIHNlcnZpY2UncyBvd24gYG9yZ2FuaXphdGlvbnNgIG1vZGVsLCBmb3JcbiAqIHRoZSBgKldpdGhgIHRyYXZlcnNhbHMgYmVsb3cuIGBnZXRDaGlsZE9yZ0lkc2AgcmV0dXJucyBMSVZFIGNoaWxkcmVuIG9ubHkgKGFcbiAqIHNvZnQtZGVsZXRlZCB0ZWFtIGxlYXZlcyBldmVyeSBkb3dud2FyZCBzY29wZSk7IGBnZXRQYXJlbnRPcmdJZGAgZG9lcyBub3RcbiAqIGZpbHRlciwgc28gYSBkZWxldGVkIHRlYW0gc3RpbGwgcmVzb2x2ZXMgdGhyb3VnaCBpdHMgcGFyZW50LlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlTW9uZ29PcmdIaWVyYXJjaHkoXG4gIE1vZGVsOiBPcmdIaWVyYXJjaHlNb2RlbCxcbiAgdG9PcmdJZDogKGlkOiBzdHJpbmcpID0+IHVua25vd24sXG4pOiB7IGdldFBhcmVudE9yZ0lkOiBHZXRQYXJlbnRPcmdJZDsgZ2V0Q2hpbGRPcmdJZHM6IEdldENoaWxkT3JnSWRzIH0ge1xuICByZXR1cm4ge1xuICAgIGFzeW5jIGdldFBhcmVudE9yZ0lkKG9yZ0lkKSB7XG4gICAgICBjb25zdCBvcmcgPSBhd2FpdCBNb2RlbC5maW5kQnlJZCh0b09yZ0lkKG9yZ0lkKSkuc2VsZWN0KCdwYXJlbnRPcmdJZCcpLmxlYW4oKTtcbiAgICAgIHJldHVybiB0b09yZ0lkU3RyaW5nKChvcmcgYXMgeyBwYXJlbnRPcmdJZD86IHVua25vd24gfSB8IG51bGwpPy5wYXJlbnRPcmdJZCk7XG4gICAgfSxcbiAgICBhc3luYyBnZXRDaGlsZE9yZ0lkcyhmcm9udGllcikge1xuICAgICAgLy8gYGRlbGV0ZWRBdDogbnVsbGAgbWF0Y2hlcyBib3RoIGFuIGV4cGxpY2l0IG51bGwgYW5kIGFuIGFic2VudCBmaWVsZC5cbiAgICAgIGNvbnN0IGNoaWxkcmVuID0gYXdhaXQgTW9kZWwuZmluZCh7IHBhcmVudE9yZ0lkOiB7ICRpbjogZnJvbnRpZXIgfSwgZGVsZXRlZEF0OiBudWxsIH0pLnNlbGVjdCgnX2lkJykubGVhbigpO1xuICAgICAgcmV0dXJuIGNoaWxkcmVuXG4gICAgICAgIC5tYXAoKGMpID0+IHRvT3JnSWRTdHJpbmcoKGMgYXMgeyBfaWQ/OiB1bmtub3duIH0pLl9pZCkpXG4gICAgICAgIC5maWx0ZXIoKGlkKTogaWQgaXMgc3RyaW5nID0+ICEhaWQpO1xuICAgIH0sXG4gIH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgT3JnTGluZWFnZSB7XG4gIC8qKiBUaGUgb3JnJ3MgZGlyZWN0IHBhcmVudCBpZCwgb3IgYHVuZGVmaW5lZGAgd2hlbiBpdCdzIGEgcm9vdCBvcmcuICovXG4gIHBhcmVudE9yZ0lkPzogc3RyaW5nO1xuICAvKiogVG9wIG9mIHRoZSBhbmNlc3RyeSBjaGFpbi4gRXF1YWxzIHRoZSBpbnB1dCBgb3JnSWRgIGZvciBhIHJvb3Qgb3JnLiAqL1xuICByb290T3JnSWQ6IHN0cmluZztcbn1cblxuLyoqXG4gKiBXYWxrIGBwYXJlbnRPcmdJZGAgZnJvbSBgb3JnSWRgIHVwIHRvIHRoZSByb290LiBSZXR1cm5zIHRoZSBkaXJlY3QgcGFyZW50IChpZlxuICogYW55KSBhbmQgdGhlIHJvb3Qgb3JnIGlkICh0aGUgaW5wdXQgaXRzZWxmIHdoZW4gdGhlIG9yZyBoYXMgbm8gcGFyZW50KS4gT24gYVxuICogZGV0ZWN0ZWQgY3ljbGUsIGEgbWlzc2luZyBvcmcsIG9yIHRoZSBkZXB0aCBjYXAsIHRyYXZlcnNhbCBzdG9wcyBhbmQgdGhlIGxhc3RcbiAqIHJlYWNoZWQgaWQgaXMgdHJlYXRlZCBhcyB0aGUgcm9vdC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlc29sdmVPcmdMaW5lYWdlV2l0aChvcmdJZDogc3RyaW5nLCBnZXRQYXJlbnQ6IEdldFBhcmVudE9yZ0lkKTogUHJvbWlzZTxPcmdMaW5lYWdlPiB7XG4gIGNvbnN0IHNlZW4gPSBuZXcgU2V0PHN0cmluZz4oW29yZ0lkXSk7XG4gIGxldCBjdXJyZW50SWQgPSBvcmdJZDtcbiAgbGV0IHBhcmVudE9yZ0lkOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgZm9yIChsZXQgZGVwdGggPSAwOyBkZXB0aCA8IE1BWF9PUkdfREVQVEg7IGRlcHRoKyspIHtcbiAgICBjb25zdCBwYXJlbnQgPSB0b09yZ0lkU3RyaW5nKGF3YWl0IGdldFBhcmVudChjdXJyZW50SWQpKTtcbiAgICBpZiAoIXBhcmVudCkgYnJlYWs7IC8vIGN1cnJlbnRJZCBpcyB0aGUgcm9vdFxuICAgIC8vIFJlY29yZCB0aGUgZGlyZWN0IHBhcmVudCBvbmx5IHdoZW4gaXQgaXNuJ3QgYSBzZWxmLWxvb3AsIHNvIGEgbWFsZm9ybWVkXG4gICAgLy8gQeKGkkEgb3JnIGlzbid0IHJlcG9ydGVkIGFzIGl0cyBvd24gcGFyZW50LlxuICAgIGlmIChkZXB0aCA9PT0gMCAmJiBwYXJlbnQgIT09IG9yZ0lkKSBwYXJlbnRPcmdJZCA9IHBhcmVudDtcbiAgICBpZiAoc2Vlbi5oYXMocGFyZW50KSkgYnJlYWs7IC8vIGN5Y2xlIOKAlCB0cmVhdCBjdXJyZW50SWQgYXMgdGhlIHJvb3RcbiAgICBzZWVuLmFkZChwYXJlbnQpO1xuICAgIGN1cnJlbnRJZCA9IHBhcmVudDtcbiAgfVxuXG4gIHJldHVybiB7IHJvb3RPcmdJZDogY3VycmVudElkLCAuLi4ocGFyZW50T3JnSWQgJiYgeyBwYXJlbnRPcmdJZCB9KSB9O1xufVxuXG4vKiogV2FsayBgcGFyZW50T3JnSWRgIHVwIHRvIHRoZSByb290IGFuZCByZXR1cm4ganVzdCB0aGUgcm9vdCBpZC4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZXNvbHZlUm9vdE9yZ0lkV2l0aChvcmdJZDogc3RyaW5nLCBnZXRQYXJlbnQ6IEdldFBhcmVudE9yZ0lkKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgcmV0dXJuIChhd2FpdCByZXNvbHZlT3JnTGluZWFnZVdpdGgob3JnSWQsIGdldFBhcmVudCkpLnJvb3RPcmdJZDtcbn1cblxuLyoqXG4gKiBGYWlsLUNMT1NFRCB2YXJpYW50IG9mIHtAbGluayByZXNvbHZlUm9vdE9yZ0lkV2l0aH06IHJldHVybnMgYG51bGxgIHdoZW4gdGhlXG4gKiByb290IGNhbid0IGJlIHByb3ZlbiDigJQgYSBjeWNsZSBvciBhIGNoYWluIGRlZXBlciB0aGFuIHtAbGluayBNQVhfT1JHX0RFUFRIfSDigJRcbiAqIGluc3RlYWQgb2YgdHJlYXRpbmcgdGhlIGxhc3QgcmVhY2hlZCBpZCBhcyB0aGUgcm9vdC4gTG9va3VwIGVycm9ycyBwcm9wYWdhdGUuXG4gKiBGb3IgY2FsbGVycyB3aGVyZSBhIHdyb25nIHJvb3QgaXMgd29yc2UgdGhhbiBubyBhbnN3ZXIgKGUuZy4gYSByZXRlbnRpb24gcHVyZ2VcbiAqIHRoYXQgd291bGQgb3RoZXJ3aXNlIGFwcGx5IGEgdGVhbSdzIHNob3J0ZXIgZGVmYXVsdCB3aW5kb3cpLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVzb2x2ZVJvb3RPcmdJZFN0cmljdChvcmdJZDogc3RyaW5nLCBnZXRQYXJlbnQ6IEdldFBhcmVudE9yZ0lkKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPiB7XG4gIGNvbnN0IHNlZW4gPSBuZXcgU2V0PHN0cmluZz4oW29yZ0lkXSk7XG4gIGxldCBjdXJyZW50SWQgPSBvcmdJZDtcbiAgZm9yIChsZXQgZGVwdGggPSAwOyBkZXB0aCA8IE1BWF9PUkdfREVQVEg7IGRlcHRoKyspIHtcbiAgICBjb25zdCBwYXJlbnQgPSB0b09yZ0lkU3RyaW5nKGF3YWl0IGdldFBhcmVudChjdXJyZW50SWQpKTtcbiAgICBpZiAoIXBhcmVudCB8fCBwYXJlbnQgPT09IGN1cnJlbnRJZCkgcmV0dXJuIGN1cnJlbnRJZDtcbiAgICBpZiAoc2Vlbi5oYXMocGFyZW50KSkgcmV0dXJuIG51bGw7XG4gICAgc2Vlbi5hZGQocGFyZW50KTtcbiAgICBjdXJyZW50SWQgPSBwYXJlbnQ7XG4gIH1cbiAgcmV0dXJuIG51bGw7XG59XG5cbi8qKlxuICogVHJ1ZSB3aGVuIGBhbmNlc3Rvck9yZ0lkYCBpcyBhbiBhbmNlc3RvciBvZiBgY2FuZGlkYXRlT3JnSWRgIOKAlCBpLmUuXG4gKiBgY2FuZGlkYXRlT3JnSWRgIGxpdmVzIHNvbWV3aGVyZSBpbiB0aGUgc3VidHJlZSByb290ZWQgYXQgYGFuY2VzdG9yT3JnSWRgLlxuICogV2Fsa3MgYGNhbmRpZGF0ZU9yZ0lkYCdzIHBhcmVudCBjaGFpbiB1cHdhcmQsIGRlcHRoLWNhcHBlZCBhbmQgY3ljbGUtc2FmZS5cbiAqIFJldHVybnMgYGZhbHNlYCB3aGVuIHRoZSB0d28gYXJlIGVxdWFsIChhbiBvcmcgaXMgbm90IGl0cyBvd24gYW5jZXN0b3IpIG9yXG4gKiB1bnJlbGF0ZWQuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpc0FuY2VzdG9yT3JnV2l0aChcbiAgYW5jZXN0b3JPcmdJZDogc3RyaW5nLFxuICBjYW5kaWRhdGVPcmdJZDogc3RyaW5nLFxuICBnZXRQYXJlbnQ6IEdldFBhcmVudE9yZ0lkLFxuKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIGlmIChhbmNlc3Rvck9yZ0lkID09PSBjYW5kaWRhdGVPcmdJZCkgcmV0dXJuIGZhbHNlO1xuXG4gIGNvbnN0IHNlZW4gPSBuZXcgU2V0PHN0cmluZz4oW2NhbmRpZGF0ZU9yZ0lkXSk7XG4gIGxldCBjdXJyZW50SWQgPSBjYW5kaWRhdGVPcmdJZDtcblxuICBmb3IgKGxldCBkZXB0aCA9IDA7IGRlcHRoIDwgTUFYX09SR19ERVBUSDsgZGVwdGgrKykge1xuICAgIGNvbnN0IHBhcmVudCA9IHRvT3JnSWRTdHJpbmcoYXdhaXQgZ2V0UGFyZW50KGN1cnJlbnRJZCkpO1xuICAgIGlmICghcGFyZW50KSByZXR1cm4gZmFsc2U7IC8vIHJlYWNoZWQgYSByb290IHdpdGhvdXQgbWF0Y2hpbmdcbiAgICBpZiAocGFyZW50ID09PSBhbmNlc3Rvck9yZ0lkKSByZXR1cm4gdHJ1ZTtcbiAgICBpZiAoc2Vlbi5oYXMocGFyZW50KSkgcmV0dXJuIGZhbHNlOyAvLyBjeWNsZVxuICAgIHNlZW4uYWRkKHBhcmVudCk7XG4gICAgY3VycmVudElkID0gcGFyZW50O1xuICB9XG5cbiAgcmV0dXJuIGZhbHNlO1xufVxuXG4vKipcbiAqIEV4cGFuZCBgb3JnSWRgIHRvIGl0c2VsZiBwbHVzIGV2ZXJ5IGRlc2NlbmRhbnQgb3JnIGlkIChicmVhZHRoLWZpcnN0IG92ZXJcbiAqIGBwYXJlbnRPcmdJZGApLiBUaGlzIGlzIHRoZSBzY29wZSBhIHBhcmVudCBvcmcgXCJzZWVzXCIgZm9yIHZpc2liaWxpdHkgYW5kXG4gKiBhbmFseXRpY3Mgcm9sbHVwczogYFtzZWxmLCAuLi5kZXNjZW5kYW50c11gLiBSZXR1cm5zIGBbb3JnSWRdYCB3aGVuIHRoZSBvcmdcbiAqIGhhcyBubyBjaGlsZHJlbi4gQ3ljbGUtc2FmZSBhbmQgZGVwdGgtY2FwcGVkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhwYW5kT3JnU2NvcGVXaXRoKG9yZ0lkOiBzdHJpbmcsIGdldENoaWxkcmVuOiBHZXRDaGlsZE9yZ0lkcyk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgY29uc3QgcmVzdWx0OiBzdHJpbmdbXSA9IFtvcmdJZF07XG4gIGNvbnN0IHNlZW4gPSBuZXcgU2V0PHN0cmluZz4oW29yZ0lkXSk7XG4gIGxldCBmcm9udGllcjogc3RyaW5nW10gPSBbb3JnSWRdO1xuXG4gIGZvciAobGV0IGRlcHRoID0gMDsgZGVwdGggPCBNQVhfT1JHX0RFUFRIICYmIGZyb250aWVyLmxlbmd0aCA+IDA7IGRlcHRoKyspIHtcbiAgICBjb25zdCBjaGlsZHJlbiA9IGF3YWl0IGdldENoaWxkcmVuKGZyb250aWVyKTtcbiAgICBjb25zdCBuZXh0OiBzdHJpbmdbXSA9IFtdO1xuICAgIGZvciAoY29uc3QgcmF3IG9mIGNoaWxkcmVuKSB7XG4gICAgICBjb25zdCBpZCA9IHRvT3JnSWRTdHJpbmcocmF3KTtcbiAgICAgIGlmICghaWQgfHwgc2Vlbi5oYXMoaWQpKSBjb250aW51ZTtcbiAgICAgIHNlZW4uYWRkKGlkKTtcbiAgICAgIHJlc3VsdC5wdXNoKGlkKTtcbiAgICAgIG5leHQucHVzaChpZCk7XG4gICAgfVxuICAgIGZyb250aWVyID0gbmV4dDtcbiAgfVxuXG4gIHJldHVybiByZXN1bHQ7XG59XG4iXX0=
@@ -13,37 +13,57 @@ export interface RestorableService<T extends RestorableEntity> {
13
13
  findDeletedById(id: string, orgId?: string): Promise<T | null>;
14
14
  restore(id: string, orgId: string, userId: string): Promise<T | null>;
15
15
  }
16
- /**
17
- * Shared skeleton for a step-up-gated entity restore route: extract `:id`, load
18
- * the own-org tombstone, gate it on the visibility ladder (parity with delete),
19
- * restore, and 404 on miss. Returns `{ existing, restored }` on success,
20
- * or `null` when it has ALREADY sent a response (400/404/403) — the caller then
21
- * does the entity-specific audit + response. DRYs the near-identical
22
- * pipeline / plugin / pipeline_template restore routes (which differ only in
23
- * service, audit action, and response shaping).
24
- */
25
- export declare function loadAndRestore<T extends RestorableEntity>(req: Request, res: Response, orgId: string, userId: string, service: RestorableService<T>, label: string, publishPermission: Permission): Promise<{
26
- existing: T;
27
- restored: T;
28
- } | null>;
29
16
  /** The slice of a service a purge route uses (satisfied by any CrudService). */
30
17
  export interface PurgeableService<T extends RestorableEntity> {
31
18
  findDeletedById(id: string, orgId?: string): Promise<T | null>;
32
19
  purgeById(id: string, orgId?: string): Promise<string | null>;
33
20
  }
34
21
  /**
35
- * Shared skeleton for an entity PURGE route: extract `:id`, load the own-org
36
- * tombstone, gate it on the visibility ladder (parity with
37
- * delete/restore), then permanently hard-delete it — but ONLY when it is a
38
- * genuine tombstone (a live/active row loads as `null` here and 404s, so purge
39
- * can never hard-delete a live row). Returns `{ existing, purgedId }` on success
40
- * (the caller emits the entity-specific audit off `existing`), or `null` when it
41
- * has ALREADY sent a response (400/404/403). Step-up is the ROUTE's job: the
42
- * pipeline, template and plugin services mount purge behind `requireStepUp`
43
- * (sharing restore's chain so one token isn't consumed twice). DRYs the near-identical
44
- * pipeline / pipeline_template purge routes.
22
+ * Decides whether the caller may restore/purge the loaded tombstone. Returns
23
+ * false after it has ANSWERED the request (403). Load-bearing: the tombstone
24
+ * load is org-scoped, not visibility-scoped, so this is what stops a colleague
25
+ * resurrecting (or destroying) someone else's draft.
26
+ */
27
+ export type TombstoneAuthorizer<T> = (existing: T, req: Request, res: Response) => boolean;
28
+ export type TombstoneRouteOptions<T extends RestorableEntity> = {
29
+ orgId: string;
30
+ userId: string;
31
+ /** User-facing entity noun for the 400/404 messages ('Pipeline', 'Rule', …). */
32
+ label: string;
33
+ /**
34
+ * The org the tombstone load and the mutation are pinned to. Defaults to
35
+ * `orgId`; `undefined` spans orgs (system-admin moderation).
36
+ */
37
+ scopeOrgId?: string | undefined;
38
+ } & (
39
+ /** Same authority as delete on the visibility ladder: `publishPermission`
40
+ * for a public row, authorship for a private one. */
41
+ {
42
+ publishPermission: Permission;
43
+ authorize?: undefined;
44
+ } | {
45
+ authorize: TombstoneAuthorizer<T>;
46
+ publishPermission?: undefined;
47
+ });
48
+ /**
49
+ * Shared skeleton for a step-up-gated entity RESTORE route: extract `:id`, load
50
+ * the tombstone, authorize it, restore, and 404 on miss. Returns
51
+ * `{ existing, restored }` on success, or `null` when it has ALREADY sent a
52
+ * response (400/404/403) — the caller then does the entity-specific audit +
53
+ * response. Step-up is the route's job.
54
+ */
55
+ export declare function loadAndRestore<T extends RestorableEntity>(req: Request, res: Response, service: RestorableService<T>, opts: TombstoneRouteOptions<T>): Promise<{
56
+ existing: T;
57
+ restored: T;
58
+ } | null>;
59
+ /**
60
+ * Shared skeleton for an entity PURGE route: the same load + authorization as
61
+ * restore, then a permanent hard-delete via the retention sweep's single-id
62
+ * purge (same tombstone match + dependent-teardown hooks). Returns
63
+ * `{ existing, purgedId }` (the caller audits off `existing`), or `null` when it
64
+ * has ALREADY sent a response. Step-up is the route's job.
45
65
  */
46
- export declare function loadAndPurge<T extends RestorableEntity>(req: Request, res: Response, orgId: string, service: PurgeableService<T>, label: string, publishPermission: Permission, userId: string): Promise<{
66
+ export declare function loadAndPurge<T extends RestorableEntity>(req: Request, res: Response, service: PurgeableService<T>, opts: TombstoneRouteOptions<T>): Promise<{
47
67
  existing: T;
48
68
  purgedId: string;
49
69
  } | null>;
@@ -5,74 +5,63 @@ import { sendEntityNotFound } from './crud-helpers.js';
5
5
  import { ErrorCode } from '../types/error-codes.js';
6
6
  import { getParam } from '../utils/params.js';
7
7
  import { sendBadRequest } from '../utils/response.js';
8
- /**
9
- * Shared skeleton for a step-up-gated entity restore route: extract `:id`, load
10
- * the own-org tombstone, gate it on the visibility ladder (parity with delete),
11
- * restore, and 404 on miss. Returns `{ existing, restored }` on success,
12
- * or `null` when it has ALREADY sent a response (400/404/403) — the caller then
13
- * does the entity-specific audit + response. DRYs the near-identical
14
- * pipeline / plugin / pipeline_template restore routes (which differ only in
15
- * service, audit action, and response shaping).
16
- */
17
- export async function loadAndRestore(req, res, orgId, userId, service, label, publishPermission) {
8
+ /** Shared prelude: `:id` → own-scope tombstone → authorization. `null` = answered. */
9
+ async function loadTombstone(req, res, service, opts) {
18
10
  const id = getParam(req.params, 'id');
19
11
  if (!id) {
20
- sendBadRequest(res, `${label} ID is required.`, ErrorCode.MISSING_REQUIRED_FIELD);
12
+ sendBadRequest(res, `${opts.label} ID is required.`, ErrorCode.MISSING_REQUIRED_FIELD);
21
13
  return null;
22
14
  }
23
- // Load the TOMBSTONE (own-org). A live row / unknown id / already-purged → 404.
24
- const existing = await service.findDeletedById(id, orgId);
15
+ const scope = 'scopeOrgId' in opts ? opts.scopeOrgId : opts.orgId;
16
+ // A live row / unknown id / already-purged → 404, so a currently-active entity
17
+ // is never restored or hard-deleted through these paths.
18
+ const existing = await service.findDeletedById(id, scope);
25
19
  if (!existing) {
26
- sendEntityNotFound(res, label);
20
+ sendEntityNotFound(res, opts.label);
27
21
  return null;
28
22
  }
29
- // Same ladder authority as delete: `:publish` for a public row, authorship for
30
- // a private one. Load-bearing here — the tombstone load is org-scoped, not
31
- // visibility-scoped, so this is what stops a colleague resurrecting a draft.
32
- if (!requireVisibilityWriteAccess(req, res, existing, userId || '', publishPermission))
33
- return null;
34
- const restored = await service.restore(id, orgId, userId || 'system');
35
- if (!restored) {
36
- sendEntityNotFound(res, label);
23
+ const allowed = opts.authorize
24
+ ? opts.authorize(existing, req, res)
25
+ : requireVisibilityWriteAccess(req, res, existing, opts.userId, opts.publishPermission);
26
+ if (!allowed)
37
27
  return null;
38
- }
39
- return { existing, restored };
28
+ return { id, existing, scope };
40
29
  }
41
30
  /**
42
- * Shared skeleton for an entity PURGE route: extract `:id`, load the own-org
43
- * tombstone, gate it on the visibility ladder (parity with
44
- * delete/restore), then permanently hard-delete it — but ONLY when it is a
45
- * genuine tombstone (a live/active row loads as `null` here and 404s, so purge
46
- * can never hard-delete a live row). Returns `{ existing, purgedId }` on success
47
- * (the caller emits the entity-specific audit off `existing`), or `null` when it
48
- * has ALREADY sent a response (400/404/403). Step-up is the ROUTE's job: the
49
- * pipeline, template and plugin services mount purge behind `requireStepUp`
50
- * (sharing restore's chain so one token isn't consumed twice). DRYs the near-identical
51
- * pipeline / pipeline_template purge routes.
31
+ * Shared skeleton for a step-up-gated entity RESTORE route: extract `:id`, load
32
+ * the tombstone, authorize it, restore, and 404 on miss. Returns
33
+ * `{ existing, restored }` on success, or `null` when it has ALREADY sent a
34
+ * response (400/404/403) — the caller then does the entity-specific audit +
35
+ * response. Step-up is the route's job.
52
36
  */
53
- export async function loadAndPurge(req, res, orgId, service, label, publishPermission, userId) {
54
- const id = getParam(req.params, 'id');
55
- if (!id) {
56
- sendBadRequest(res, `${label} ID is required.`, ErrorCode.MISSING_REQUIRED_FIELD);
37
+ export async function loadAndRestore(req, res, service, opts) {
38
+ const loaded = await loadTombstone(req, res, service, opts);
39
+ if (!loaded)
57
40
  return null;
58
- }
59
- // Load the TOMBSTONE (own-org). A live row / unknown id / already-purged → 404,
60
- // so a currently-active entity is never hard-deleted through this path.
61
- const existing = await service.findDeletedById(id, orgId);
62
- if (!existing) {
63
- sendEntityNotFound(res, label);
41
+ const restored = await service.restore(loaded.id, loaded.scope ?? '', opts.userId);
42
+ if (!restored) {
43
+ sendEntityNotFound(res, opts.label);
64
44
  return null;
65
45
  }
66
- // Same ladder authority as delete/restore (see loadAndRestore).
67
- if (!requireVisibilityWriteAccess(req, res, existing, userId || '', publishPermission))
46
+ return { existing: loaded.existing, restored };
47
+ }
48
+ /**
49
+ * Shared skeleton for an entity PURGE route: the same load + authorization as
50
+ * restore, then a permanent hard-delete via the retention sweep's single-id
51
+ * purge (same tombstone match + dependent-teardown hooks). Returns
52
+ * `{ existing, purgedId }` (the caller audits off `existing`), or `null` when it
53
+ * has ALREADY sent a response. Step-up is the route's job.
54
+ */
55
+ export async function loadAndPurge(req, res, service, opts) {
56
+ const loaded = await loadTombstone(req, res, service, opts);
57
+ if (!loaded)
68
58
  return null;
69
- // Reuse the retention sweep's single-id hard-delete (same tombstone match +
70
- // dependent-teardown hooks). Returns null on a race (already purged) → 404.
71
- const purgedId = await service.purgeById(id, orgId);
59
+ // Null on a race (already purged) → 404.
60
+ const purgedId = await service.purgeById(loaded.id, loaded.scope ?? '');
72
61
  if (!purgedId) {
73
- sendEntityNotFound(res, label);
62
+ sendEntityNotFound(res, opts.label);
74
63
  return null;
75
64
  }
76
- return { existing, purgedId };
65
+ return { existing: loaded.existing, purgedId };
77
66
  }
78
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzdG9yZS1oZWxwZXJzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2hlbHBlcnMvcmVzdG9yZS1oZWxwZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFHdEMsT0FBTyxFQUFFLDRCQUE0QixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDbkUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDdkQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRXBELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFpQnREOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxjQUFjLENBQ2xDLEdBQVksRUFDWixHQUFhLEVBQ2IsS0FBYSxFQUNiLE1BQWMsRUFDZCxPQUE2QixFQUM3QixLQUFhLEVBQ2IsaUJBQTZCO0lBRTdCLE1BQU0sRUFBRSxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3RDLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNSLGNBQWMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLLGtCQUFrQixFQUFFLFNBQVMsQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO1FBQ2xGLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELGdGQUFnRjtJQUNoRixNQUFNLFFBQVEsR0FBRyxNQUFNLE9BQU8sQ0FBQyxlQUFlLENBQUMsRUFBRSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzFELElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNkLGtCQUFrQixDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUMvQixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRCwrRUFBK0U7SUFDL0UsMkVBQTJFO0lBQzNFLDZFQUE2RTtJQUM3RSxJQUFJLENBQUMsNEJBQTRCLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxRQUFRLEVBQUUsTUFBTSxJQUFJLEVBQUUsRUFBRSxpQkFBaUIsQ0FBQztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBRXBHLE1BQU0sUUFBUSxHQUFHLE1BQU0sT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sSUFBSSxRQUFRLENBQUMsQ0FBQztJQUN0RSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDZCxrQkFBa0IsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDL0IsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsT0FBTyxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsQ0FBQztBQUNoQyxDQUFDO0FBUUQ7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLFlBQVksQ0FDaEMsR0FBWSxFQUNaLEdBQWEsRUFDYixLQUFhLEVBQ2IsT0FBNEIsRUFDNUIsS0FBYSxFQUNiLGlCQUE2QixFQUM3QixNQUFjO0lBRWQsTUFBTSxFQUFFLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDdEMsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ1IsY0FBYyxDQUFDLEdBQUcsRUFBRSxHQUFHLEtBQUssa0JBQWtCLEVBQUUsU0FBUyxDQUFDLHNCQUFzQixDQUFDLENBQUM7UUFDbEYsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsZ0ZBQWdGO0lBQ2hGLHdFQUF3RTtJQUN4RSxNQUFNLFFBQVEsR0FBRyxNQUFNLE9BQU8sQ0FBQyxlQUFlLENBQUMsRUFBRSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzFELElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNkLGtCQUFrQixDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUMvQixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRCxnRUFBZ0U7SUFDaEUsSUFBSSxDQUFDLDRCQUE0QixDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLE1BQU0sSUFBSSxFQUFFLEVBQUUsaUJBQWlCLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUVwRyw0RUFBNEU7SUFDNUUsNEVBQTRFO0lBQzVFLE1BQU0sUUFBUSxHQUFHLE1BQU0sT0FBTyxDQUFDLFNBQVMsQ0FBQyxFQUFFLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFDcEQsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ2Qsa0JBQWtCLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQy9CLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELE9BQU8sRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFLENBQUM7QUFDaEMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgdHlwZSB7IFJlcXVlc3QsIFJlc3BvbnNlIH0gZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgeyByZXF1aXJlVmlzaWJpbGl0eVdyaXRlQWNjZXNzIH0gZnJvbSAnLi9hY2Nlc3MtaGVscGVycy5qcyc7XG5pbXBvcnQgeyBzZW5kRW50aXR5Tm90Rm91bmQgfSBmcm9tICcuL2NydWQtaGVscGVycy5qcyc7XG5pbXBvcnQgeyBFcnJvckNvZGUgfSBmcm9tICcuLi90eXBlcy9lcnJvci1jb2Rlcy5qcyc7XG5pbXBvcnQgdHlwZSB7IFBlcm1pc3Npb24gfSBmcm9tICcuLi90eXBlcy9wZXJtaXNzaW9ucy5qcyc7XG5pbXBvcnQgeyBnZXRQYXJhbSB9IGZyb20gJy4uL3V0aWxzL3BhcmFtcy5qcyc7XG5pbXBvcnQgeyBzZW5kQmFkUmVxdWVzdCB9IGZyb20gJy4uL3V0aWxzL3Jlc3BvbnNlLmpzJztcblxuLyoqIE1pbmltYWwgc2hhcGUgYSByZXN0b3JlIHJvdXRlIG5lZWRzIGZyb20gdGhlIGVudGl0eSBpdCByZXN0b3Jlcy4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVzdG9yYWJsZUVudGl0eSB7XG4gIG9yZ0lkOiBzdHJpbmc7XG4gIC8qKiBSdW5nIHRoZSB0b21ic3RvbmUgc2F0IGF0IOKAlCBkcml2ZXMgdGhlIHJlc3RvcmUvcHVyZ2UgYXV0aG9yaXR5IGNoZWNrLiAqL1xuICB2aXNpYmlsaXR5Pzogc3RyaW5nO1xuICAvKiogQXV0aG9yIOKAlCB0aGUgb25seSBub24tYWRtaW4gd2hvIG1heSByZXN0b3JlL3B1cmdlIGEgYHByaXZhdGVgIHJvdy4gKi9cbiAgY3JlYXRlZEJ5Pzogc3RyaW5nO1xufVxuXG4vKiogVGhlIHNsaWNlIG9mIGEgc2VydmljZSBhIHJlc3RvcmUgcm91dGUgdXNlcyAoc2F0aXNmaWVkIGJ5IGFueSBDcnVkU2VydmljZSkuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlc3RvcmFibGVTZXJ2aWNlPFQgZXh0ZW5kcyBSZXN0b3JhYmxlRW50aXR5PiB7XG4gIGZpbmREZWxldGVkQnlJZChpZDogc3RyaW5nLCBvcmdJZD86IHN0cmluZyk6IFByb21pc2U8VCB8IG51bGw+O1xuICByZXN0b3JlKGlkOiBzdHJpbmcsIG9yZ0lkOiBzdHJpbmcsIHVzZXJJZDogc3RyaW5nKTogUHJvbWlzZTxUIHwgbnVsbD47XG59XG5cbi8qKlxuICogU2hhcmVkIHNrZWxldG9uIGZvciBhIHN0ZXAtdXAtZ2F0ZWQgZW50aXR5IHJlc3RvcmUgcm91dGU6IGV4dHJhY3QgYDppZGAsIGxvYWRcbiAqIHRoZSBvd24tb3JnIHRvbWJzdG9uZSwgZ2F0ZSBpdCBvbiB0aGUgdmlzaWJpbGl0eSBsYWRkZXIgKHBhcml0eSB3aXRoIGRlbGV0ZSksXG4gKiByZXN0b3JlLCBhbmQgNDA0IG9uIG1pc3MuIFJldHVybnMgYHsgZXhpc3RpbmcsIHJlc3RvcmVkIH1gIG9uIHN1Y2Nlc3MsXG4gKiBvciBgbnVsbGAgd2hlbiBpdCBoYXMgQUxSRUFEWSBzZW50IGEgcmVzcG9uc2UgKDQwMC80MDQvNDAzKSDigJQgdGhlIGNhbGxlciB0aGVuXG4gKiBkb2VzIHRoZSBlbnRpdHktc3BlY2lmaWMgYXVkaXQgKyByZXNwb25zZS4gRFJZcyB0aGUgbmVhci1pZGVudGljYWxcbiAqIHBpcGVsaW5lIC8gcGx1Z2luIC8gcGlwZWxpbmVfdGVtcGxhdGUgcmVzdG9yZSByb3V0ZXMgKHdoaWNoIGRpZmZlciBvbmx5IGluXG4gKiBzZXJ2aWNlLCBhdWRpdCBhY3Rpb24sIGFuZCByZXNwb25zZSBzaGFwaW5nKS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRBbmRSZXN0b3JlPFQgZXh0ZW5kcyBSZXN0b3JhYmxlRW50aXR5PihcbiAgcmVxOiBSZXF1ZXN0LFxuICByZXM6IFJlc3BvbnNlLFxuICBvcmdJZDogc3RyaW5nLFxuICB1c2VySWQ6IHN0cmluZyxcbiAgc2VydmljZTogUmVzdG9yYWJsZVNlcnZpY2U8VD4sXG4gIGxhYmVsOiBzdHJpbmcsXG4gIHB1Ymxpc2hQZXJtaXNzaW9uOiBQZXJtaXNzaW9uLFxuKTogUHJvbWlzZTx7IGV4aXN0aW5nOiBUOyByZXN0b3JlZDogVCB9IHwgbnVsbD4ge1xuICBjb25zdCBpZCA9IGdldFBhcmFtKHJlcS5wYXJhbXMsICdpZCcpO1xuICBpZiAoIWlkKSB7XG4gICAgc2VuZEJhZFJlcXVlc3QocmVzLCBgJHtsYWJlbH0gSUQgaXMgcmVxdWlyZWQuYCwgRXJyb3JDb2RlLk1JU1NJTkdfUkVRVUlSRURfRklFTEQpO1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLy8gTG9hZCB0aGUgVE9NQlNUT05FIChvd24tb3JnKS4gQSBsaXZlIHJvdyAvIHVua25vd24gaWQgLyBhbHJlYWR5LXB1cmdlZCDihpIgNDA0LlxuICBjb25zdCBleGlzdGluZyA9IGF3YWl0IHNlcnZpY2UuZmluZERlbGV0ZWRCeUlkKGlkLCBvcmdJZCk7XG4gIGlmICghZXhpc3RpbmcpIHtcbiAgICBzZW5kRW50aXR5Tm90Rm91bmQocmVzLCBsYWJlbCk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvLyBTYW1lIGxhZGRlciBhdXRob3JpdHkgYXMgZGVsZXRlOiBgOnB1Ymxpc2hgIGZvciBhIHB1YmxpYyByb3csIGF1dGhvcnNoaXAgZm9yXG4gIC8vIGEgcHJpdmF0ZSBvbmUuIExvYWQtYmVhcmluZyBoZXJlIOKAlCB0aGUgdG9tYnN0b25lIGxvYWQgaXMgb3JnLXNjb3BlZCwgbm90XG4gIC8vIHZpc2liaWxpdHktc2NvcGVkLCBzbyB0aGlzIGlzIHdoYXQgc3RvcHMgYSBjb2xsZWFndWUgcmVzdXJyZWN0aW5nIGEgZHJhZnQuXG4gIGlmICghcmVxdWlyZVZpc2liaWxpdHlXcml0ZUFjY2VzcyhyZXEsIHJlcywgZXhpc3RpbmcsIHVzZXJJZCB8fCAnJywgcHVibGlzaFBlcm1pc3Npb24pKSByZXR1cm4gbnVsbDtcblxuICBjb25zdCByZXN0b3JlZCA9IGF3YWl0IHNlcnZpY2UucmVzdG9yZShpZCwgb3JnSWQsIHVzZXJJZCB8fCAnc3lzdGVtJyk7XG4gIGlmICghcmVzdG9yZWQpIHtcbiAgICBzZW5kRW50aXR5Tm90Rm91bmQocmVzLCBsYWJlbCk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICByZXR1cm4geyBleGlzdGluZywgcmVzdG9yZWQgfTtcbn1cblxuLyoqIFRoZSBzbGljZSBvZiBhIHNlcnZpY2UgYSBwdXJnZSByb3V0ZSB1c2VzIChzYXRpc2ZpZWQgYnkgYW55IENydWRTZXJ2aWNlKS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUHVyZ2VhYmxlU2VydmljZTxUIGV4dGVuZHMgUmVzdG9yYWJsZUVudGl0eT4ge1xuICBmaW5kRGVsZXRlZEJ5SWQoaWQ6IHN0cmluZywgb3JnSWQ/OiBzdHJpbmcpOiBQcm9taXNlPFQgfCBudWxsPjtcbiAgcHVyZ2VCeUlkKGlkOiBzdHJpbmcsIG9yZ0lkPzogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPjtcbn1cblxuLyoqXG4gKiBTaGFyZWQgc2tlbGV0b24gZm9yIGFuIGVudGl0eSBQVVJHRSByb3V0ZTogZXh0cmFjdCBgOmlkYCwgbG9hZCB0aGUgb3duLW9yZ1xuICogdG9tYnN0b25lLCBnYXRlIGl0IG9uIHRoZSB2aXNpYmlsaXR5IGxhZGRlciAocGFyaXR5IHdpdGhcbiAqIGRlbGV0ZS9yZXN0b3JlKSwgdGhlbiBwZXJtYW5lbnRseSBoYXJkLWRlbGV0ZSBpdCDigJQgYnV0IE9OTFkgd2hlbiBpdCBpcyBhXG4gKiBnZW51aW5lIHRvbWJzdG9uZSAoYSBsaXZlL2FjdGl2ZSByb3cgbG9hZHMgYXMgYG51bGxgIGhlcmUgYW5kIDQwNHMsIHNvIHB1cmdlXG4gKiBjYW4gbmV2ZXIgaGFyZC1kZWxldGUgYSBsaXZlIHJvdykuIFJldHVybnMgYHsgZXhpc3RpbmcsIHB1cmdlZElkIH1gIG9uIHN1Y2Nlc3NcbiAqICh0aGUgY2FsbGVyIGVtaXRzIHRoZSBlbnRpdHktc3BlY2lmaWMgYXVkaXQgb2ZmIGBleGlzdGluZ2ApLCBvciBgbnVsbGAgd2hlbiBpdFxuICogaGFzIEFMUkVBRFkgc2VudCBhIHJlc3BvbnNlICg0MDAvNDA0LzQwMykuIFN0ZXAtdXAgaXMgdGhlIFJPVVRFJ3Mgam9iOiB0aGVcbiAqIHBpcGVsaW5lLCB0ZW1wbGF0ZSBhbmQgcGx1Z2luIHNlcnZpY2VzIG1vdW50IHB1cmdlIGJlaGluZCBgcmVxdWlyZVN0ZXBVcGBcbiAqIChzaGFyaW5nIHJlc3RvcmUncyBjaGFpbiBzbyBvbmUgdG9rZW4gaXNuJ3QgY29uc3VtZWQgdHdpY2UpLiBEUllzIHRoZSBuZWFyLWlkZW50aWNhbFxuICogcGlwZWxpbmUgLyBwaXBlbGluZV90ZW1wbGF0ZSBwdXJnZSByb3V0ZXMuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2FkQW5kUHVyZ2U8VCBleHRlbmRzIFJlc3RvcmFibGVFbnRpdHk+KFxuICByZXE6IFJlcXVlc3QsXG4gIHJlczogUmVzcG9uc2UsXG4gIG9yZ0lkOiBzdHJpbmcsXG4gIHNlcnZpY2U6IFB1cmdlYWJsZVNlcnZpY2U8VD4sXG4gIGxhYmVsOiBzdHJpbmcsXG4gIHB1Ymxpc2hQZXJtaXNzaW9uOiBQZXJtaXNzaW9uLFxuICB1c2VySWQ6IHN0cmluZyxcbik6IFByb21pc2U8eyBleGlzdGluZzogVDsgcHVyZ2VkSWQ6IHN0cmluZyB9IHwgbnVsbD4ge1xuICBjb25zdCBpZCA9IGdldFBhcmFtKHJlcS5wYXJhbXMsICdpZCcpO1xuICBpZiAoIWlkKSB7XG4gICAgc2VuZEJhZFJlcXVlc3QocmVzLCBgJHtsYWJlbH0gSUQgaXMgcmVxdWlyZWQuYCwgRXJyb3JDb2RlLk1JU1NJTkdfUkVRVUlSRURfRklFTEQpO1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLy8gTG9hZCB0aGUgVE9NQlNUT05FIChvd24tb3JnKS4gQSBsaXZlIHJvdyAvIHVua25vd24gaWQgLyBhbHJlYWR5LXB1cmdlZCDihpIgNDA0LFxuICAvLyBzbyBhIGN1cnJlbnRseS1hY3RpdmUgZW50aXR5IGlzIG5ldmVyIGhhcmQtZGVsZXRlZCB0aHJvdWdoIHRoaXMgcGF0aC5cbiAgY29uc3QgZXhpc3RpbmcgPSBhd2FpdCBzZXJ2aWNlLmZpbmREZWxldGVkQnlJZChpZCwgb3JnSWQpO1xuICBpZiAoIWV4aXN0aW5nKSB7XG4gICAgc2VuZEVudGl0eU5vdEZvdW5kKHJlcywgbGFiZWwpO1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLy8gU2FtZSBsYWRkZXIgYXV0aG9yaXR5IGFzIGRlbGV0ZS9yZXN0b3JlIChzZWUgbG9hZEFuZFJlc3RvcmUpLlxuICBpZiAoIXJlcXVpcmVWaXNpYmlsaXR5V3JpdGVBY2Nlc3MocmVxLCByZXMsIGV4aXN0aW5nLCB1c2VySWQgfHwgJycsIHB1Ymxpc2hQZXJtaXNzaW9uKSkgcmV0dXJuIG51bGw7XG5cbiAgLy8gUmV1c2UgdGhlIHJldGVudGlvbiBzd2VlcCdzIHNpbmdsZS1pZCBoYXJkLWRlbGV0ZSAoc2FtZSB0b21ic3RvbmUgbWF0Y2ggK1xuICAvLyBkZXBlbmRlbnQtdGVhcmRvd24gaG9va3MpLiBSZXR1cm5zIG51bGwgb24gYSByYWNlIChhbHJlYWR5IHB1cmdlZCkg4oaSIDQwNC5cbiAgY29uc3QgcHVyZ2VkSWQgPSBhd2FpdCBzZXJ2aWNlLnB1cmdlQnlJZChpZCwgb3JnSWQpO1xuICBpZiAoIXB1cmdlZElkKSB7XG4gICAgc2VuZEVudGl0eU5vdEZvdW5kKHJlcywgbGFiZWwpO1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgcmV0dXJuIHsgZXhpc3RpbmcsIHB1cmdlZElkIH07XG59XG4iXX0=
67
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzdG9yZS1oZWxwZXJzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2hlbHBlcnMvcmVzdG9yZS1oZWxwZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFHdEMsT0FBTyxFQUFFLDRCQUE0QixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDbkUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDdkQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRXBELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFnRHRELHNGQUFzRjtBQUN0RixLQUFLLFVBQVUsYUFBYSxDQUMxQixHQUFZLEVBQ1osR0FBYSxFQUNiLE9BQTJFLEVBQzNFLElBQThCO0lBRTlCLE1BQU0sRUFBRSxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3RDLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNSLGNBQWMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsS0FBSyxrQkFBa0IsRUFBRSxTQUFTLENBQUMsc0JBQXNCLENBQUMsQ0FBQztRQUN2RixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFDRCxNQUFNLEtBQUssR0FBRyxZQUFZLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDO0lBRWxFLCtFQUErRTtJQUMvRSx5REFBeUQ7SUFDekQsTUFBTSxRQUFRLEdBQUcsTUFBTSxPQUFPLENBQUMsZUFBZSxDQUFDLEVBQUUsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUMxRCxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDZCxrQkFBa0IsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3BDLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxTQUFTO1FBQzVCLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsRUFBRSxHQUFHLEVBQUUsR0FBRyxDQUFDO1FBQ3BDLENBQUMsQ0FBQyw0QkFBNEIsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLFFBQVEsRUFBRSxJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0lBQzFGLElBQUksQ0FBQyxPQUFPO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDMUIsT0FBTyxFQUFFLEVBQUUsRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLENBQUM7QUFDakMsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsY0FBYyxDQUNsQyxHQUFZLEVBQ1osR0FBYSxFQUNiLE9BQTZCLEVBQzdCLElBQThCO0lBRTlCLE1BQU0sTUFBTSxHQUFHLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQzVELElBQUksQ0FBQyxNQUFNO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFFekIsTUFBTSxRQUFRLEdBQUcsTUFBTSxPQUFPLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUFFLEVBQUUsTUFBTSxDQUFDLEtBQUssSUFBSSxFQUFFLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ25GLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNkLGtCQUFrQixDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDcEMsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBQ0QsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUSxFQUFFLFFBQVEsRUFBRSxDQUFDO0FBQ2pELENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLFlBQVksQ0FDaEMsR0FBWSxFQUNaLEdBQWEsRUFDYixPQUE0QixFQUM1QixJQUE4QjtJQUU5QixNQUFNLE1BQU0sR0FBRyxNQUFNLGFBQWEsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztJQUM1RCxJQUFJLENBQUMsTUFBTTtRQUFFLE9BQU8sSUFBSSxDQUFDO0lBRXpCLHlDQUF5QztJQUN6QyxNQUFNLFFBQVEsR0FBRyxNQUFNLE9BQU8sQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLEVBQUUsRUFBRSxNQUFNLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3hFLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNkLGtCQUFrQixDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDcEMsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBQ0QsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUSxFQUFFLFFBQVEsRUFBRSxDQUFDO0FBQ2pELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHR5cGUgeyBSZXF1ZXN0LCBSZXNwb25zZSB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IHsgcmVxdWlyZVZpc2liaWxpdHlXcml0ZUFjY2VzcyB9IGZyb20gJy4vYWNjZXNzLWhlbHBlcnMuanMnO1xuaW1wb3J0IHsgc2VuZEVudGl0eU5vdEZvdW5kIH0gZnJvbSAnLi9jcnVkLWhlbHBlcnMuanMnO1xuaW1wb3J0IHsgRXJyb3JDb2RlIH0gZnJvbSAnLi4vdHlwZXMvZXJyb3ItY29kZXMuanMnO1xuaW1wb3J0IHR5cGUgeyBQZXJtaXNzaW9uIH0gZnJvbSAnLi4vdHlwZXMvcGVybWlzc2lvbnMuanMnO1xuaW1wb3J0IHsgZ2V0UGFyYW0gfSBmcm9tICcuLi91dGlscy9wYXJhbXMuanMnO1xuaW1wb3J0IHsgc2VuZEJhZFJlcXVlc3QgfSBmcm9tICcuLi91dGlscy9yZXNwb25zZS5qcyc7XG5cbi8qKiBNaW5pbWFsIHNoYXBlIGEgcmVzdG9yZSByb3V0ZSBuZWVkcyBmcm9tIHRoZSBlbnRpdHkgaXQgcmVzdG9yZXMuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlc3RvcmFibGVFbnRpdHkge1xuICBvcmdJZDogc3RyaW5nO1xuICAvKiogUnVuZyB0aGUgdG9tYnN0b25lIHNhdCBhdCDigJQgZHJpdmVzIHRoZSByZXN0b3JlL3B1cmdlIGF1dGhvcml0eSBjaGVjay4gKi9cbiAgdmlzaWJpbGl0eT86IHN0cmluZztcbiAgLyoqIEF1dGhvciDigJQgdGhlIG9ubHkgbm9uLWFkbWluIHdobyBtYXkgcmVzdG9yZS9wdXJnZSBhIGBwcml2YXRlYCByb3cuICovXG4gIGNyZWF0ZWRCeT86IHN0cmluZztcbn1cblxuLyoqIFRoZSBzbGljZSBvZiBhIHNlcnZpY2UgYSByZXN0b3JlIHJvdXRlIHVzZXMgKHNhdGlzZmllZCBieSBhbnkgQ3J1ZFNlcnZpY2UpLiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXN0b3JhYmxlU2VydmljZTxUIGV4dGVuZHMgUmVzdG9yYWJsZUVudGl0eT4ge1xuICBmaW5kRGVsZXRlZEJ5SWQoaWQ6IHN0cmluZywgb3JnSWQ/OiBzdHJpbmcpOiBQcm9taXNlPFQgfCBudWxsPjtcbiAgcmVzdG9yZShpZDogc3RyaW5nLCBvcmdJZDogc3RyaW5nLCB1c2VySWQ6IHN0cmluZyk6IFByb21pc2U8VCB8IG51bGw+O1xufVxuXG4vKiogVGhlIHNsaWNlIG9mIGEgc2VydmljZSBhIHB1cmdlIHJvdXRlIHVzZXMgKHNhdGlzZmllZCBieSBhbnkgQ3J1ZFNlcnZpY2UpLiAqL1xuZXhwb3J0IGludGVyZmFjZSBQdXJnZWFibGVTZXJ2aWNlPFQgZXh0ZW5kcyBSZXN0b3JhYmxlRW50aXR5PiB7XG4gIGZpbmREZWxldGVkQnlJZChpZDogc3RyaW5nLCBvcmdJZD86IHN0cmluZyk6IFByb21pc2U8VCB8IG51bGw+O1xuICBwdXJnZUJ5SWQoaWQ6IHN0cmluZywgb3JnSWQ/OiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZyB8IG51bGw+O1xufVxuXG4vKipcbiAqIERlY2lkZXMgd2hldGhlciB0aGUgY2FsbGVyIG1heSByZXN0b3JlL3B1cmdlIHRoZSBsb2FkZWQgdG9tYnN0b25lLiBSZXR1cm5zXG4gKiBmYWxzZSBhZnRlciBpdCBoYXMgQU5TV0VSRUQgdGhlIHJlcXVlc3QgKDQwMykuIExvYWQtYmVhcmluZzogdGhlIHRvbWJzdG9uZVxuICogbG9hZCBpcyBvcmctc2NvcGVkLCBub3QgdmlzaWJpbGl0eS1zY29wZWQsIHNvIHRoaXMgaXMgd2hhdCBzdG9wcyBhIGNvbGxlYWd1ZVxuICogcmVzdXJyZWN0aW5nIChvciBkZXN0cm95aW5nKSBzb21lb25lIGVsc2UncyBkcmFmdC5cbiAqL1xuZXhwb3J0IHR5cGUgVG9tYnN0b25lQXV0aG9yaXplcjxUPiA9IChleGlzdGluZzogVCwgcmVxOiBSZXF1ZXN0LCByZXM6IFJlc3BvbnNlKSA9PiBib29sZWFuO1xuXG5leHBvcnQgdHlwZSBUb21ic3RvbmVSb3V0ZU9wdGlvbnM8VCBleHRlbmRzIFJlc3RvcmFibGVFbnRpdHk+ID0ge1xuICBvcmdJZDogc3RyaW5nO1xuICB1c2VySWQ6IHN0cmluZztcbiAgLyoqIFVzZXItZmFjaW5nIGVudGl0eSBub3VuIGZvciB0aGUgNDAwLzQwNCBtZXNzYWdlcyAoJ1BpcGVsaW5lJywgJ1J1bGUnLCDigKYpLiAqL1xuICBsYWJlbDogc3RyaW5nO1xuICAvKipcbiAgICogVGhlIG9yZyB0aGUgdG9tYnN0b25lIGxvYWQgYW5kIHRoZSBtdXRhdGlvbiBhcmUgcGlubmVkIHRvLiBEZWZhdWx0cyB0b1xuICAgKiBgb3JnSWRgOyBgdW5kZWZpbmVkYCBzcGFucyBvcmdzIChzeXN0ZW0tYWRtaW4gbW9kZXJhdGlvbikuXG4gICAqL1xuICBzY29wZU9yZ0lkPzogc3RyaW5nIHwgdW5kZWZpbmVkO1xufSAmIChcbiAgLyoqIFNhbWUgYXV0aG9yaXR5IGFzIGRlbGV0ZSBvbiB0aGUgdmlzaWJpbGl0eSBsYWRkZXI6IGBwdWJsaXNoUGVybWlzc2lvbmBcbiAgICogIGZvciBhIHB1YmxpYyByb3csIGF1dGhvcnNoaXAgZm9yIGEgcHJpdmF0ZSBvbmUuICovXG4gIHwgeyBwdWJsaXNoUGVybWlzc2lvbjogUGVybWlzc2lvbjsgYXV0aG9yaXplPzogdW5kZWZpbmVkIH1cbiAgfCB7IGF1dGhvcml6ZTogVG9tYnN0b25lQXV0aG9yaXplcjxUPjsgcHVibGlzaFBlcm1pc3Npb24/OiB1bmRlZmluZWQgfVxuKTtcblxuLyoqIFNoYXJlZCBwcmVsdWRlOiBgOmlkYCDihpIgb3duLXNjb3BlIHRvbWJzdG9uZSDihpIgYXV0aG9yaXphdGlvbi4gYG51bGxgID0gYW5zd2VyZWQuICovXG5hc3luYyBmdW5jdGlvbiBsb2FkVG9tYnN0b25lPFQgZXh0ZW5kcyBSZXN0b3JhYmxlRW50aXR5PihcbiAgcmVxOiBSZXF1ZXN0LFxuICByZXM6IFJlc3BvbnNlLFxuICBzZXJ2aWNlOiB7IGZpbmREZWxldGVkQnlJZChpZDogc3RyaW5nLCBvcmdJZD86IHN0cmluZyk6IFByb21pc2U8VCB8IG51bGw+IH0sXG4gIG9wdHM6IFRvbWJzdG9uZVJvdXRlT3B0aW9uczxUPixcbik6IFByb21pc2U8eyBpZDogc3RyaW5nOyBleGlzdGluZzogVDsgc2NvcGU6IHN0cmluZyB8IHVuZGVmaW5lZCB9IHwgbnVsbD4ge1xuICBjb25zdCBpZCA9IGdldFBhcmFtKHJlcS5wYXJhbXMsICdpZCcpO1xuICBpZiAoIWlkKSB7XG4gICAgc2VuZEJhZFJlcXVlc3QocmVzLCBgJHtvcHRzLmxhYmVsfSBJRCBpcyByZXF1aXJlZC5gLCBFcnJvckNvZGUuTUlTU0lOR19SRVFVSVJFRF9GSUVMRCk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgY29uc3Qgc2NvcGUgPSAnc2NvcGVPcmdJZCcgaW4gb3B0cyA/IG9wdHMuc2NvcGVPcmdJZCA6IG9wdHMub3JnSWQ7XG5cbiAgLy8gQSBsaXZlIHJvdyAvIHVua25vd24gaWQgLyBhbHJlYWR5LXB1cmdlZCDihpIgNDA0LCBzbyBhIGN1cnJlbnRseS1hY3RpdmUgZW50aXR5XG4gIC8vIGlzIG5ldmVyIHJlc3RvcmVkIG9yIGhhcmQtZGVsZXRlZCB0aHJvdWdoIHRoZXNlIHBhdGhzLlxuICBjb25zdCBleGlzdGluZyA9IGF3YWl0IHNlcnZpY2UuZmluZERlbGV0ZWRCeUlkKGlkLCBzY29wZSk7XG4gIGlmICghZXhpc3RpbmcpIHtcbiAgICBzZW5kRW50aXR5Tm90Rm91bmQocmVzLCBvcHRzLmxhYmVsKTtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIGNvbnN0IGFsbG93ZWQgPSBvcHRzLmF1dGhvcml6ZVxuICAgID8gb3B0cy5hdXRob3JpemUoZXhpc3RpbmcsIHJlcSwgcmVzKVxuICAgIDogcmVxdWlyZVZpc2liaWxpdHlXcml0ZUFjY2VzcyhyZXEsIHJlcywgZXhpc3RpbmcsIG9wdHMudXNlcklkLCBvcHRzLnB1Ymxpc2hQZXJtaXNzaW9uKTtcbiAgaWYgKCFhbGxvd2VkKSByZXR1cm4gbnVsbDtcbiAgcmV0dXJuIHsgaWQsIGV4aXN0aW5nLCBzY29wZSB9O1xufVxuXG4vKipcbiAqIFNoYXJlZCBza2VsZXRvbiBmb3IgYSBzdGVwLXVwLWdhdGVkIGVudGl0eSBSRVNUT1JFIHJvdXRlOiBleHRyYWN0IGA6aWRgLCBsb2FkXG4gKiB0aGUgdG9tYnN0b25lLCBhdXRob3JpemUgaXQsIHJlc3RvcmUsIGFuZCA0MDQgb24gbWlzcy4gUmV0dXJuc1xuICogYHsgZXhpc3RpbmcsIHJlc3RvcmVkIH1gIG9uIHN1Y2Nlc3MsIG9yIGBudWxsYCB3aGVuIGl0IGhhcyBBTFJFQURZIHNlbnQgYVxuICogcmVzcG9uc2UgKDQwMC80MDQvNDAzKSDigJQgdGhlIGNhbGxlciB0aGVuIGRvZXMgdGhlIGVudGl0eS1zcGVjaWZpYyBhdWRpdCArXG4gKiByZXNwb25zZS4gU3RlcC11cCBpcyB0aGUgcm91dGUncyBqb2IuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2FkQW5kUmVzdG9yZTxUIGV4dGVuZHMgUmVzdG9yYWJsZUVudGl0eT4oXG4gIHJlcTogUmVxdWVzdCxcbiAgcmVzOiBSZXNwb25zZSxcbiAgc2VydmljZTogUmVzdG9yYWJsZVNlcnZpY2U8VD4sXG4gIG9wdHM6IFRvbWJzdG9uZVJvdXRlT3B0aW9uczxUPixcbik6IFByb21pc2U8eyBleGlzdGluZzogVDsgcmVzdG9yZWQ6IFQgfSB8IG51bGw+IHtcbiAgY29uc3QgbG9hZGVkID0gYXdhaXQgbG9hZFRvbWJzdG9uZShyZXEsIHJlcywgc2VydmljZSwgb3B0cyk7XG4gIGlmICghbG9hZGVkKSByZXR1cm4gbnVsbDtcblxuICBjb25zdCByZXN0b3JlZCA9IGF3YWl0IHNlcnZpY2UucmVzdG9yZShsb2FkZWQuaWQsIGxvYWRlZC5zY29wZSA/PyAnJywgb3B0cy51c2VySWQpO1xuICBpZiAoIXJlc3RvcmVkKSB7XG4gICAgc2VuZEVudGl0eU5vdEZvdW5kKHJlcywgb3B0cy5sYWJlbCk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgcmV0dXJuIHsgZXhpc3Rpbmc6IGxvYWRlZC5leGlzdGluZywgcmVzdG9yZWQgfTtcbn1cblxuLyoqXG4gKiBTaGFyZWQgc2tlbGV0b24gZm9yIGFuIGVudGl0eSBQVVJHRSByb3V0ZTogdGhlIHNhbWUgbG9hZCArIGF1dGhvcml6YXRpb24gYXNcbiAqIHJlc3RvcmUsIHRoZW4gYSBwZXJtYW5lbnQgaGFyZC1kZWxldGUgdmlhIHRoZSByZXRlbnRpb24gc3dlZXAncyBzaW5nbGUtaWRcbiAqIHB1cmdlIChzYW1lIHRvbWJzdG9uZSBtYXRjaCArIGRlcGVuZGVudC10ZWFyZG93biBob29rcykuIFJldHVybnNcbiAqIGB7IGV4aXN0aW5nLCBwdXJnZWRJZCB9YCAodGhlIGNhbGxlciBhdWRpdHMgb2ZmIGBleGlzdGluZ2ApLCBvciBgbnVsbGAgd2hlbiBpdFxuICogaGFzIEFMUkVBRFkgc2VudCBhIHJlc3BvbnNlLiBTdGVwLXVwIGlzIHRoZSByb3V0ZSdzIGpvYi5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRBbmRQdXJnZTxUIGV4dGVuZHMgUmVzdG9yYWJsZUVudGl0eT4oXG4gIHJlcTogUmVxdWVzdCxcbiAgcmVzOiBSZXNwb25zZSxcbiAgc2VydmljZTogUHVyZ2VhYmxlU2VydmljZTxUPixcbiAgb3B0czogVG9tYnN0b25lUm91dGVPcHRpb25zPFQ+LFxuKTogUHJvbWlzZTx7IGV4aXN0aW5nOiBUOyBwdXJnZWRJZDogc3RyaW5nIH0gfCBudWxsPiB7XG4gIGNvbnN0IGxvYWRlZCA9IGF3YWl0IGxvYWRUb21ic3RvbmUocmVxLCByZXMsIHNlcnZpY2UsIG9wdHMpO1xuICBpZiAoIWxvYWRlZCkgcmV0dXJuIG51bGw7XG5cbiAgLy8gTnVsbCBvbiBhIHJhY2UgKGFscmVhZHkgcHVyZ2VkKSDihpIgNDA0LlxuICBjb25zdCBwdXJnZWRJZCA9IGF3YWl0IHNlcnZpY2UucHVyZ2VCeUlkKGxvYWRlZC5pZCwgbG9hZGVkLnNjb3BlID8/ICcnKTtcbiAgaWYgKCFwdXJnZWRJZCkge1xuICAgIHNlbmRFbnRpdHlOb3RGb3VuZChyZXMsIG9wdHMubGFiZWwpO1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIHJldHVybiB7IGV4aXN0aW5nOiBsb2FkZWQuZXhpc3RpbmcsIHB1cmdlZElkIH07XG59XG4iXX0=
@@ -6,22 +6,44 @@ import type { Request, Response } from 'express';
6
6
  * `X-Accel-Buffering: no` (disable nginx buffering) block.
7
7
  */
8
8
  export declare function writeSseHeaders(res: Response): void;
9
+ /** The slice of an Express/Node response needed to observe a client disconnect. */
10
+ interface ClosableResponse {
11
+ on(event: 'close', listener: () => void): unknown;
12
+ readonly writableFinished: boolean;
13
+ }
9
14
  /**
10
- * Set SSE response headers and flush.
11
- * Returns an `aborted()` function to check if the client disconnected.
15
+ * An AbortSignal that fires when the client disconnects before the response
16
+ * finished — cancels a provider call (avoids wasted spend) and tells the route
17
+ * to stop writing.
12
18
  *
13
19
  * Disconnect is detected on the RESPONSE: `res` emits 'close' when the
14
20
  * underlying connection goes away, and `writableFinished` tells an early close
15
21
  * (client gone) from the normal close after `res.end()`. The request's own
16
22
  * 'close' is not a disconnect signal — on current Node it fires once the request
17
- * BODY has been consumed, which for a POST stream happens before this listener
18
- * is attached, so a disconnect was never observed.
23
+ * BODY has been consumed (express.json() does that before the handler runs), so
24
+ * a disconnect would never be observed.
19
25
  */
20
- export declare function initSSEStream(_req: Request, res: Response, timeoutMs: number): {
21
- aborted: () => boolean;
22
- };
26
+ export declare function clientAbortSignal(res: ClosableResponse): AbortSignal;
27
+ /** An open SSE response. */
28
+ export interface SseStream {
29
+ /** Aborted when the client disconnects before the stream finished. */
30
+ readonly signal: AbortSignal;
31
+ /** True once the client has disconnected. */
32
+ aborted(): boolean;
33
+ /** Write one `data:` frame carrying `event` as JSON. A no-op after a disconnect. */
34
+ send(event: unknown): void;
35
+ /** Write the terminal `data: [DONE]` frame (preceded by `event`, when given).
36
+ * A no-op after a disconnect. The caller still ends the response. */
37
+ done(event?: unknown): void;
38
+ }
39
+ /**
40
+ * Set SSE response headers, flush, and return the stream's writer plus its
41
+ * client-disconnect signal (see {@link clientAbortSignal}).
42
+ */
43
+ export declare function initSSEStream(_req: Request, res: Response, timeoutMs: number): SseStream;
23
44
  /**
24
45
  * Classify AI generation errors and send the appropriate HTTP response or SSE event.
25
46
  * If headers are already sent (streaming), writes an SSE error event and ends the response.
26
47
  */
27
48
  export declare function handleAIError(res: Response, message: string, fallbackMessage: string): void;
49
+ export {};
@@ -14,24 +14,51 @@ export function writeSseHeaders(res) {
14
14
  res.setHeader('X-Accel-Buffering', 'no');
15
15
  }
16
16
  /**
17
- * Set SSE response headers and flush.
18
- * Returns an `aborted()` function to check if the client disconnected.
17
+ * An AbortSignal that fires when the client disconnects before the response
18
+ * finished — cancels a provider call (avoids wasted spend) and tells the route
19
+ * to stop writing.
19
20
  *
20
21
  * Disconnect is detected on the RESPONSE: `res` emits 'close' when the
21
22
  * underlying connection goes away, and `writableFinished` tells an early close
22
23
  * (client gone) from the normal close after `res.end()`. The request's own
23
24
  * 'close' is not a disconnect signal — on current Node it fires once the request
24
- * BODY has been consumed, which for a POST stream happens before this listener
25
- * is attached, so a disconnect was never observed.
25
+ * BODY has been consumed (express.json() does that before the handler runs), so
26
+ * a disconnect would never be observed.
27
+ */
28
+ export function clientAbortSignal(res) {
29
+ const controller = new AbortController();
30
+ res.on('close', () => {
31
+ if (!res.writableFinished)
32
+ controller.abort();
33
+ });
34
+ return controller.signal;
35
+ }
36
+ /**
37
+ * Set SSE response headers, flush, and return the stream's writer plus its
38
+ * client-disconnect signal (see {@link clientAbortSignal}).
26
39
  */
27
40
  export function initSSEStream(_req, res, timeoutMs) {
28
41
  writeSseHeaders(res);
29
42
  res.setTimeout(timeoutMs);
30
43
  res.flushHeaders();
31
- let _aborted = false;
32
- res.on('close', () => { if (!res.writableFinished)
33
- _aborted = true; });
34
- return { aborted: () => _aborted };
44
+ const signal = clientAbortSignal(res);
45
+ const send = (event) => {
46
+ if (signal.aborted)
47
+ return;
48
+ res.write(`data: ${JSON.stringify(event)}\n\n`);
49
+ };
50
+ return {
51
+ signal,
52
+ aborted: () => signal.aborted,
53
+ send,
54
+ done: (event) => {
55
+ if (signal.aborted)
56
+ return;
57
+ if (event !== undefined)
58
+ send(event);
59
+ res.write('data: [DONE]\n\n');
60
+ },
61
+ };
35
62
  }
36
63
  /**
37
64
  * Classify AI generation errors and send the appropriate HTTP response or SSE event.
@@ -57,4 +84,4 @@ export function handleAIError(res, message, fallbackMessage) {
57
84
  res.write(`data: ${JSON.stringify({ type: 'error', message })}\n\n`);
58
85
  res.end();
59
86
  }
60
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3NlLWhlbHBlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaGVscGVycy9zc2UtaGVscGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBR3RDLE9BQU8sRUFBRSxjQUFjLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUV6RTs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxlQUFlLENBQUMsR0FBYTtJQUMzQyxHQUFHLENBQUMsU0FBUyxDQUFDLGNBQWMsRUFBRSxtQkFBbUIsQ0FBQyxDQUFDO0lBQ25ELEdBQUcsQ0FBQyxTQUFTLENBQUMsZUFBZSxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBQzNDLEdBQUcsQ0FBQyxTQUFTLENBQUMsWUFBWSxFQUFFLFlBQVksQ0FBQyxDQUFDO0lBQzFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsbUJBQW1CLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFDM0MsQ0FBQztBQUVEOzs7Ozs7Ozs7O0dBVUc7QUFDSCxNQUFNLFVBQVUsYUFBYSxDQUFDLElBQWEsRUFBRSxHQUFhLEVBQUUsU0FBaUI7SUFDM0UsZUFBZSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3JCLEdBQUcsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDMUIsR0FBRyxDQUFDLFlBQVksRUFBRSxDQUFDO0lBQ25CLElBQUksUUFBUSxHQUFHLEtBQUssQ0FBQztJQUNyQixHQUFHLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLGdCQUFnQjtRQUFFLFFBQVEsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN2RSxPQUFPLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxDQUFDLFFBQVEsRUFBRSxDQUFDO0FBQ3JDLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsYUFBYSxDQUFDLEdBQWEsRUFBRSxPQUFlLEVBQUUsZUFBdUI7SUFDbkYsSUFBSSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUNyQixJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7WUFDdEUscUVBQXFFO1lBQ3JFLHdFQUF3RTtZQUN4RSx3RUFBd0U7WUFDeEUsaUVBQWlFO1lBQ2pFLDBFQUEwRTtZQUMxRSx5RUFBeUU7WUFDekUseUVBQXlFO1lBQ3pFLE9BQU8saUJBQWlCLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ3pDLENBQUM7UUFDRCxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsNEJBQTRCLENBQUMsRUFBRSxDQUFDO1lBQ25ELE9BQU8sY0FBYyxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUN0QyxDQUFDO1FBQ0QsT0FBTyxpQkFBaUIsQ0FBQyxHQUFHLEVBQUUsZUFBZSxFQUFFLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7SUFDdkUsQ0FBQztJQUNELEdBQUcsQ0FBQyxLQUFLLENBQUMsU0FBUyxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNyRSxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUM7QUFDWixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB0eXBlIHsgUmVxdWVzdCwgUmVzcG9uc2UgfSBmcm9tICdleHByZXNzJztcbmltcG9ydCB7IHNlbmRCYWRSZXF1ZXN0LCBzZW5kSW50ZXJuYWxFcnJvciB9IGZyb20gJy4uL3V0aWxzL3Jlc3BvbnNlLmpzJztcblxuLyoqXG4gKiBTZXQgdGhlIGZvdXIgc3RhbmRhcmQgU1NFIHJlc3BvbnNlIGhlYWRlcnMgKGRvZXMgTk9UIGZsdXNoIOKAlCB0aGUgY2FsbGVyIGZsdXNoZXNcbiAqIGFmdGVyIGFueSBhZGRpdGlvbmFsIHBlci1lbmRwb2ludCBzZXR1cCBsaWtlIGByZXMuc2V0VGltZW91dGApLiBUaGUgc2luZ2xlXG4gKiBzb3VyY2Ugb2YgdHJ1dGggZm9yIHRoZSBgdGV4dC9ldmVudC1zdHJlYW1gICsgbm8tY2FjaGUgKyBrZWVwLWFsaXZlICtcbiAqIGBYLUFjY2VsLUJ1ZmZlcmluZzogbm9gIChkaXNhYmxlIG5naW54IGJ1ZmZlcmluZykgYmxvY2suXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3cml0ZVNzZUhlYWRlcnMocmVzOiBSZXNwb25zZSk6IHZvaWQge1xuICByZXMuc2V0SGVhZGVyKCdDb250ZW50LVR5cGUnLCAndGV4dC9ldmVudC1zdHJlYW0nKTtcbiAgcmVzLnNldEhlYWRlcignQ2FjaGUtQ29udHJvbCcsICduby1jYWNoZScpO1xuICByZXMuc2V0SGVhZGVyKCdDb25uZWN0aW9uJywgJ2tlZXAtYWxpdmUnKTtcbiAgcmVzLnNldEhlYWRlcignWC1BY2NlbC1CdWZmZXJpbmcnLCAnbm8nKTtcbn1cblxuLyoqXG4gKiBTZXQgU1NFIHJlc3BvbnNlIGhlYWRlcnMgYW5kIGZsdXNoLlxuICogUmV0dXJucyBhbiBgYWJvcnRlZCgpYCBmdW5jdGlvbiB0byBjaGVjayBpZiB0aGUgY2xpZW50IGRpc2Nvbm5lY3RlZC5cbiAqXG4gKiBEaXNjb25uZWN0IGlzIGRldGVjdGVkIG9uIHRoZSBSRVNQT05TRTogYHJlc2AgZW1pdHMgJ2Nsb3NlJyB3aGVuIHRoZVxuICogdW5kZXJseWluZyBjb25uZWN0aW9uIGdvZXMgYXdheSwgYW5kIGB3cml0YWJsZUZpbmlzaGVkYCB0ZWxscyBhbiBlYXJseSBjbG9zZVxuICogKGNsaWVudCBnb25lKSBmcm9tIHRoZSBub3JtYWwgY2xvc2UgYWZ0ZXIgYHJlcy5lbmQoKWAuIFRoZSByZXF1ZXN0J3Mgb3duXG4gKiAnY2xvc2UnIGlzIG5vdCBhIGRpc2Nvbm5lY3Qgc2lnbmFsIOKAlCBvbiBjdXJyZW50IE5vZGUgaXQgZmlyZXMgb25jZSB0aGUgcmVxdWVzdFxuICogQk9EWSBoYXMgYmVlbiBjb25zdW1lZCwgd2hpY2ggZm9yIGEgUE9TVCBzdHJlYW0gaGFwcGVucyBiZWZvcmUgdGhpcyBsaXN0ZW5lclxuICogaXMgYXR0YWNoZWQsIHNvIGEgZGlzY29ubmVjdCB3YXMgbmV2ZXIgb2JzZXJ2ZWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbml0U1NFU3RyZWFtKF9yZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UsIHRpbWVvdXRNczogbnVtYmVyKTogeyBhYm9ydGVkOiAoKSA9PiBib29sZWFuIH0ge1xuICB3cml0ZVNzZUhlYWRlcnMocmVzKTtcbiAgcmVzLnNldFRpbWVvdXQodGltZW91dE1zKTtcbiAgcmVzLmZsdXNoSGVhZGVycygpO1xuICBsZXQgX2Fib3J0ZWQgPSBmYWxzZTtcbiAgcmVzLm9uKCdjbG9zZScsICgpID0+IHsgaWYgKCFyZXMud3JpdGFibGVGaW5pc2hlZCkgX2Fib3J0ZWQgPSB0cnVlOyB9KTtcbiAgcmV0dXJuIHsgYWJvcnRlZDogKCkgPT4gX2Fib3J0ZWQgfTtcbn1cblxuLyoqXG4gKiBDbGFzc2lmeSBBSSBnZW5lcmF0aW9uIGVycm9ycyBhbmQgc2VuZCB0aGUgYXBwcm9wcmlhdGUgSFRUUCByZXNwb25zZSBvciBTU0UgZXZlbnQuXG4gKiBJZiBoZWFkZXJzIGFyZSBhbHJlYWR5IHNlbnQgKHN0cmVhbWluZyksIHdyaXRlcyBhbiBTU0UgZXJyb3IgZXZlbnQgYW5kIGVuZHMgdGhlIHJlc3BvbnNlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaGFuZGxlQUlFcnJvcihyZXM6IFJlc3BvbnNlLCBtZXNzYWdlOiBzdHJpbmcsIGZhbGxiYWNrTWVzc2FnZTogc3RyaW5nKTogdm9pZCB7XG4gIGlmICghcmVzLmhlYWRlcnNTZW50KSB7XG4gICAgaWYgKG1lc3NhZ2UuaW5jbHVkZXMoJ25vdCBjb25maWd1cmVkJykgfHwgbWVzc2FnZS5pbmNsdWRlcygnQVBJIGtleScpKSB7XG4gICAgICAvLyBTdXJmYWNlIHRoZSBPUklHSU5BTCBtZXNzYWdlLiBJdCBuYW1lcyB0aGUgTUlTU0lORyBDT05GSUdVUkFUSU9OIOKAlFxuICAgICAgLy8gXCJubyBwcm92aWRlciBBUEkga2V5IGlzIHNldCBhbmQgT1BFTkFJX0NPTVBBVElCTEVfQkFTRV9VUkwgaXMgdW5zZXRcIixcbiAgICAgIC8vIG9yIHdoaWNoIGVudiB2YXIgYSBuYW1lZCBwcm92aWRlciB3YW50cyDigJQgbmV2ZXIgYSBzZWNyZXQgVkFMVUUsIHNvIGl0XG4gICAgICAvLyBpcyBzYWZlIHRvIHNob3cgYW5kIGl0IGlzIHRoZSBvbmx5IHRoaW5nIHRoYXQgbWFrZXMgdGhpcyBlcnJvclxuICAgICAgLy8gYWN0aW9uYWJsZS4gVGhlIGdlbmVyaWMgcmVwbGFjZW1lbnQgYWxzbyBtaXMtZGVzY3JpYmVkIHRoZSBjb21tb24gY2FzZTpcbiAgICAgIC8vIGl0IHNhaWQgXCJmb3IgdGhlIHJlcXVlc3RlZCBwcm92aWRlclwiIHdoZW4gbm8gcHJvdmlkZXIgd2FzIHJlcXVlc3RlZCBhdFxuICAgICAgLy8gYWxsLCBwb2ludGluZyBvcGVyYXRvcnMgYXQgYSBwcm92aWRlciBidWcgaW5zdGVhZCBvZiBhdCBjb25maWd1cmF0aW9uLlxuICAgICAgcmV0dXJuIHNlbmRJbnRlcm5hbEVycm9yKHJlcywgbWVzc2FnZSk7XG4gICAgfVxuICAgIGlmIChtZXNzYWdlLmluY2x1ZGVzKCdub3QgYXZhaWxhYmxlIGZvciBwcm92aWRlcicpKSB7XG4gICAgICByZXR1cm4gc2VuZEJhZFJlcXVlc3QocmVzLCBtZXNzYWdlKTtcbiAgICB9XG4gICAgcmV0dXJuIHNlbmRJbnRlcm5hbEVycm9yKHJlcywgZmFsbGJhY2tNZXNzYWdlLCB7IGRldGFpbHM6IG1lc3NhZ2UgfSk7XG4gIH1cbiAgcmVzLndyaXRlKGBkYXRhOiAke0pTT04uc3RyaW5naWZ5KHsgdHlwZTogJ2Vycm9yJywgbWVzc2FnZSB9KX1cXG5cXG5gKTtcbiAgcmVzLmVuZCgpO1xufVxuIl19
87
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3NlLWhlbHBlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaGVscGVycy9zc2UtaGVscGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBR3RDLE9BQU8sRUFBRSxjQUFjLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUV6RTs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxlQUFlLENBQUMsR0FBYTtJQUMzQyxHQUFHLENBQUMsU0FBUyxDQUFDLGNBQWMsRUFBRSxtQkFBbUIsQ0FBQyxDQUFDO0lBQ25ELEdBQUcsQ0FBQyxTQUFTLENBQUMsZUFBZSxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBQzNDLEdBQUcsQ0FBQyxTQUFTLENBQUMsWUFBWSxFQUFFLFlBQVksQ0FBQyxDQUFDO0lBQzFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsbUJBQW1CLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFDM0MsQ0FBQztBQVFEOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQixDQUFDLEdBQXFCO0lBQ3JELE1BQU0sVUFBVSxHQUFHLElBQUksZUFBZSxFQUFFLENBQUM7SUFDekMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsR0FBRyxFQUFFO1FBQ25CLElBQUksQ0FBQyxHQUFHLENBQUMsZ0JBQWdCO1lBQUUsVUFBVSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ2hELENBQUMsQ0FBQyxDQUFDO0lBQ0gsT0FBTyxVQUFVLENBQUMsTUFBTSxDQUFDO0FBQzNCLENBQUM7QUFlRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsYUFBYSxDQUFDLElBQWEsRUFBRSxHQUFhLEVBQUUsU0FBaUI7SUFDM0UsZUFBZSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3JCLEdBQUcsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDMUIsR0FBRyxDQUFDLFlBQVksRUFBRSxDQUFDO0lBQ25CLE1BQU0sTUFBTSxHQUFHLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3RDLE1BQU0sSUFBSSxHQUFHLENBQUMsS0FBYyxFQUFRLEVBQUU7UUFDcEMsSUFBSSxNQUFNLENBQUMsT0FBTztZQUFFLE9BQU87UUFDM0IsR0FBRyxDQUFDLEtBQUssQ0FBQyxTQUFTLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ2xELENBQUMsQ0FBQztJQUNGLE9BQU87UUFDTCxNQUFNO1FBQ04sT0FBTyxFQUFFLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxPQUFPO1FBQzdCLElBQUk7UUFDSixJQUFJLEVBQUUsQ0FBQyxLQUFlLEVBQVEsRUFBRTtZQUM5QixJQUFJLE1BQU0sQ0FBQyxPQUFPO2dCQUFFLE9BQU87WUFDM0IsSUFBSSxLQUFLLEtBQUssU0FBUztnQkFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDckMsR0FBRyxDQUFDLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO1FBQ2hDLENBQUM7S0FDRixDQUFDO0FBQ0osQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0sVUFBVSxhQUFhLENBQUMsR0FBYSxFQUFFLE9BQWUsRUFBRSxlQUF1QjtJQUNuRixJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ3JCLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztZQUN0RSxxRUFBcUU7WUFDckUsd0VBQXdFO1lBQ3hFLHdFQUF3RTtZQUN4RSxpRUFBaUU7WUFDakUsMEVBQTBFO1lBQzFFLHlFQUF5RTtZQUN6RSx5RUFBeUU7WUFDekUsT0FBTyxpQkFBaUIsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDekMsQ0FBQztRQUNELElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyw0QkFBNEIsQ0FBQyxFQUFFLENBQUM7WUFDbkQsT0FBTyxjQUFjLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ3RDLENBQUM7UUFDRCxPQUFPLGlCQUFpQixDQUFDLEdBQUcsRUFBRSxlQUFlLEVBQUUsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztJQUN2RSxDQUFDO0lBQ0QsR0FBRyxDQUFDLEtBQUssQ0FBQyxTQUFTLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3JFLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztBQUNaLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHR5cGUgeyBSZXF1ZXN0LCBSZXNwb25zZSB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IHsgc2VuZEJhZFJlcXVlc3QsIHNlbmRJbnRlcm5hbEVycm9yIH0gZnJvbSAnLi4vdXRpbHMvcmVzcG9uc2UuanMnO1xuXG4vKipcbiAqIFNldCB0aGUgZm91ciBzdGFuZGFyZCBTU0UgcmVzcG9uc2UgaGVhZGVycyAoZG9lcyBOT1QgZmx1c2gg4oCUIHRoZSBjYWxsZXIgZmx1c2hlc1xuICogYWZ0ZXIgYW55IGFkZGl0aW9uYWwgcGVyLWVuZHBvaW50IHNldHVwIGxpa2UgYHJlcy5zZXRUaW1lb3V0YCkuIFRoZSBzaW5nbGVcbiAqIHNvdXJjZSBvZiB0cnV0aCBmb3IgdGhlIGB0ZXh0L2V2ZW50LXN0cmVhbWAgKyBuby1jYWNoZSArIGtlZXAtYWxpdmUgK1xuICogYFgtQWNjZWwtQnVmZmVyaW5nOiBub2AgKGRpc2FibGUgbmdpbnggYnVmZmVyaW5nKSBibG9jay5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHdyaXRlU3NlSGVhZGVycyhyZXM6IFJlc3BvbnNlKTogdm9pZCB7XG4gIHJlcy5zZXRIZWFkZXIoJ0NvbnRlbnQtVHlwZScsICd0ZXh0L2V2ZW50LXN0cmVhbScpO1xuICByZXMuc2V0SGVhZGVyKCdDYWNoZS1Db250cm9sJywgJ25vLWNhY2hlJyk7XG4gIHJlcy5zZXRIZWFkZXIoJ0Nvbm5lY3Rpb24nLCAna2VlcC1hbGl2ZScpO1xuICByZXMuc2V0SGVhZGVyKCdYLUFjY2VsLUJ1ZmZlcmluZycsICdubycpO1xufVxuXG4vKiogVGhlIHNsaWNlIG9mIGFuIEV4cHJlc3MvTm9kZSByZXNwb25zZSBuZWVkZWQgdG8gb2JzZXJ2ZSBhIGNsaWVudCBkaXNjb25uZWN0LiAqL1xuaW50ZXJmYWNlIENsb3NhYmxlUmVzcG9uc2Uge1xuICBvbihldmVudDogJ2Nsb3NlJywgbGlzdGVuZXI6ICgpID0+IHZvaWQpOiB1bmtub3duO1xuICByZWFkb25seSB3cml0YWJsZUZpbmlzaGVkOiBib29sZWFuO1xufVxuXG4vKipcbiAqIEFuIEFib3J0U2lnbmFsIHRoYXQgZmlyZXMgd2hlbiB0aGUgY2xpZW50IGRpc2Nvbm5lY3RzIGJlZm9yZSB0aGUgcmVzcG9uc2VcbiAqIGZpbmlzaGVkIOKAlCBjYW5jZWxzIGEgcHJvdmlkZXIgY2FsbCAoYXZvaWRzIHdhc3RlZCBzcGVuZCkgYW5kIHRlbGxzIHRoZSByb3V0ZVxuICogdG8gc3RvcCB3cml0aW5nLlxuICpcbiAqIERpc2Nvbm5lY3QgaXMgZGV0ZWN0ZWQgb24gdGhlIFJFU1BPTlNFOiBgcmVzYCBlbWl0cyAnY2xvc2UnIHdoZW4gdGhlXG4gKiB1bmRlcmx5aW5nIGNvbm5lY3Rpb24gZ29lcyBhd2F5LCBhbmQgYHdyaXRhYmxlRmluaXNoZWRgIHRlbGxzIGFuIGVhcmx5IGNsb3NlXG4gKiAoY2xpZW50IGdvbmUpIGZyb20gdGhlIG5vcm1hbCBjbG9zZSBhZnRlciBgcmVzLmVuZCgpYC4gVGhlIHJlcXVlc3QncyBvd25cbiAqICdjbG9zZScgaXMgbm90IGEgZGlzY29ubmVjdCBzaWduYWwg4oCUIG9uIGN1cnJlbnQgTm9kZSBpdCBmaXJlcyBvbmNlIHRoZSByZXF1ZXN0XG4gKiBCT0RZIGhhcyBiZWVuIGNvbnN1bWVkIChleHByZXNzLmpzb24oKSBkb2VzIHRoYXQgYmVmb3JlIHRoZSBoYW5kbGVyIHJ1bnMpLCBzb1xuICogYSBkaXNjb25uZWN0IHdvdWxkIG5ldmVyIGJlIG9ic2VydmVkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY2xpZW50QWJvcnRTaWduYWwocmVzOiBDbG9zYWJsZVJlc3BvbnNlKTogQWJvcnRTaWduYWwge1xuICBjb25zdCBjb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICByZXMub24oJ2Nsb3NlJywgKCkgPT4ge1xuICAgIGlmICghcmVzLndyaXRhYmxlRmluaXNoZWQpIGNvbnRyb2xsZXIuYWJvcnQoKTtcbiAgfSk7XG4gIHJldHVybiBjb250cm9sbGVyLnNpZ25hbDtcbn1cblxuLyoqIEFuIG9wZW4gU1NFIHJlc3BvbnNlLiAqL1xuZXhwb3J0IGludGVyZmFjZSBTc2VTdHJlYW0ge1xuICAvKiogQWJvcnRlZCB3aGVuIHRoZSBjbGllbnQgZGlzY29ubmVjdHMgYmVmb3JlIHRoZSBzdHJlYW0gZmluaXNoZWQuICovXG4gIHJlYWRvbmx5IHNpZ25hbDogQWJvcnRTaWduYWw7XG4gIC8qKiBUcnVlIG9uY2UgdGhlIGNsaWVudCBoYXMgZGlzY29ubmVjdGVkLiAqL1xuICBhYm9ydGVkKCk6IGJvb2xlYW47XG4gIC8qKiBXcml0ZSBvbmUgYGRhdGE6YCBmcmFtZSBjYXJyeWluZyBgZXZlbnRgIGFzIEpTT04uIEEgbm8tb3AgYWZ0ZXIgYSBkaXNjb25uZWN0LiAqL1xuICBzZW5kKGV2ZW50OiB1bmtub3duKTogdm9pZDtcbiAgLyoqIFdyaXRlIHRoZSB0ZXJtaW5hbCBgZGF0YTogW0RPTkVdYCBmcmFtZSAocHJlY2VkZWQgYnkgYGV2ZW50YCwgd2hlbiBnaXZlbikuXG4gICAqICBBIG5vLW9wIGFmdGVyIGEgZGlzY29ubmVjdC4gVGhlIGNhbGxlciBzdGlsbCBlbmRzIHRoZSByZXNwb25zZS4gKi9cbiAgZG9uZShldmVudD86IHVua25vd24pOiB2b2lkO1xufVxuXG4vKipcbiAqIFNldCBTU0UgcmVzcG9uc2UgaGVhZGVycywgZmx1c2gsIGFuZCByZXR1cm4gdGhlIHN0cmVhbSdzIHdyaXRlciBwbHVzIGl0c1xuICogY2xpZW50LWRpc2Nvbm5lY3Qgc2lnbmFsIChzZWUge0BsaW5rIGNsaWVudEFib3J0U2lnbmFsfSkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbml0U1NFU3RyZWFtKF9yZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UsIHRpbWVvdXRNczogbnVtYmVyKTogU3NlU3RyZWFtIHtcbiAgd3JpdGVTc2VIZWFkZXJzKHJlcyk7XG4gIHJlcy5zZXRUaW1lb3V0KHRpbWVvdXRNcyk7XG4gIHJlcy5mbHVzaEhlYWRlcnMoKTtcbiAgY29uc3Qgc2lnbmFsID0gY2xpZW50QWJvcnRTaWduYWwocmVzKTtcbiAgY29uc3Qgc2VuZCA9IChldmVudDogdW5rbm93bik6IHZvaWQgPT4ge1xuICAgIGlmIChzaWduYWwuYWJvcnRlZCkgcmV0dXJuO1xuICAgIHJlcy53cml0ZShgZGF0YTogJHtKU09OLnN0cmluZ2lmeShldmVudCl9XFxuXFxuYCk7XG4gIH07XG4gIHJldHVybiB7XG4gICAgc2lnbmFsLFxuICAgIGFib3J0ZWQ6ICgpID0+IHNpZ25hbC5hYm9ydGVkLFxuICAgIHNlbmQsXG4gICAgZG9uZTogKGV2ZW50PzogdW5rbm93bik6IHZvaWQgPT4ge1xuICAgICAgaWYgKHNpZ25hbC5hYm9ydGVkKSByZXR1cm47XG4gICAgICBpZiAoZXZlbnQgIT09IHVuZGVmaW5lZCkgc2VuZChldmVudCk7XG4gICAgICByZXMud3JpdGUoJ2RhdGE6IFtET05FXVxcblxcbicpO1xuICAgIH0sXG4gIH07XG59XG5cbi8qKlxuICogQ2xhc3NpZnkgQUkgZ2VuZXJhdGlvbiBlcnJvcnMgYW5kIHNlbmQgdGhlIGFwcHJvcHJpYXRlIEhUVFAgcmVzcG9uc2Ugb3IgU1NFIGV2ZW50LlxuICogSWYgaGVhZGVycyBhcmUgYWxyZWFkeSBzZW50IChzdHJlYW1pbmcpLCB3cml0ZXMgYW4gU1NFIGVycm9yIGV2ZW50IGFuZCBlbmRzIHRoZSByZXNwb25zZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGhhbmRsZUFJRXJyb3IocmVzOiBSZXNwb25zZSwgbWVzc2FnZTogc3RyaW5nLCBmYWxsYmFja01lc3NhZ2U6IHN0cmluZyk6IHZvaWQge1xuICBpZiAoIXJlcy5oZWFkZXJzU2VudCkge1xuICAgIGlmIChtZXNzYWdlLmluY2x1ZGVzKCdub3QgY29uZmlndXJlZCcpIHx8IG1lc3NhZ2UuaW5jbHVkZXMoJ0FQSSBrZXknKSkge1xuICAgICAgLy8gU3VyZmFjZSB0aGUgT1JJR0lOQUwgbWVzc2FnZS4gSXQgbmFtZXMgdGhlIE1JU1NJTkcgQ09ORklHVVJBVElPTiDigJRcbiAgICAgIC8vIFwibm8gcHJvdmlkZXIgQVBJIGtleSBpcyBzZXQgYW5kIE9QRU5BSV9DT01QQVRJQkxFX0JBU0VfVVJMIGlzIHVuc2V0XCIsXG4gICAgICAvLyBvciB3aGljaCBlbnYgdmFyIGEgbmFtZWQgcHJvdmlkZXIgd2FudHMg4oCUIG5ldmVyIGEgc2VjcmV0IFZBTFVFLCBzbyBpdFxuICAgICAgLy8gaXMgc2FmZSB0byBzaG93IGFuZCBpdCBpcyB0aGUgb25seSB0aGluZyB0aGF0IG1ha2VzIHRoaXMgZXJyb3JcbiAgICAgIC8vIGFjdGlvbmFibGUuIFRoZSBnZW5lcmljIHJlcGxhY2VtZW50IGFsc28gbWlzLWRlc2NyaWJlZCB0aGUgY29tbW9uIGNhc2U6XG4gICAgICAvLyBpdCBzYWlkIFwiZm9yIHRoZSByZXF1ZXN0ZWQgcHJvdmlkZXJcIiB3aGVuIG5vIHByb3ZpZGVyIHdhcyByZXF1ZXN0ZWQgYXRcbiAgICAgIC8vIGFsbCwgcG9pbnRpbmcgb3BlcmF0b3JzIGF0IGEgcHJvdmlkZXIgYnVnIGluc3RlYWQgb2YgYXQgY29uZmlndXJhdGlvbi5cbiAgICAgIHJldHVybiBzZW5kSW50ZXJuYWxFcnJvcihyZXMsIG1lc3NhZ2UpO1xuICAgIH1cbiAgICBpZiAobWVzc2FnZS5pbmNsdWRlcygnbm90IGF2YWlsYWJsZSBmb3IgcHJvdmlkZXInKSkge1xuICAgICAgcmV0dXJuIHNlbmRCYWRSZXF1ZXN0KHJlcywgbWVzc2FnZSk7XG4gICAgfVxuICAgIHJldHVybiBzZW5kSW50ZXJuYWxFcnJvcihyZXMsIGZhbGxiYWNrTWVzc2FnZSwgeyBkZXRhaWxzOiBtZXNzYWdlIH0pO1xuICB9XG4gIHJlcy53cml0ZShgZGF0YTogJHtKU09OLnN0cmluZ2lmeSh7IHR5cGU6ICdlcnJvcicsIG1lc3NhZ2UgfSl9XFxuXFxuYCk7XG4gIHJlcy5lbmQoKTtcbn1cbiJdfQ==
package/lib/index.d.ts CHANGED
@@ -19,7 +19,7 @@
19
19
  * **Utilities**
20
20
  * - createLogger — Winston-based structured logger factory
21
21
  * - sendSuccess, sendError, sendPaginatedNested, sendBadRequest, sendInternalError — HTTP response helpers
22
- * - getParam, getOrgId — request parameter extraction
22
+ * - getParam — request parameter extraction
23
23
  * - parseQueryInt, parseQueryString — query string parsing
24
24
  * - getIdentity — identity extraction from requests
25
25
  * - actorId — the audit actor for a route context (one `system` sentinel)
@@ -52,25 +52,27 @@
52
52
  * PUBLIC SURFACE POLICY
53
53
  *
54
54
  * The root re-exports the ten sub-barrels, and each SUB-BARREL is the allow-list
55
- * for its own area — that is where a module is either `export *`'d or narrowed
56
- * to the names services may use. Internals are excluded there rather than here,
57
- * so the exclusion sits next to the code that defines them and cannot be missed
58
- * by someone adding a module.
55
+ * for its own area: every re-export NAMES what it exports (no `export *`), and
56
+ * a name is on the list only because some other package — a service, the
57
+ * frontend, a test, a deploy script — actually imports it. Internals are
58
+ * excluded there rather than here, so the exclusion sits next to the code that
59
+ * defines them. Adding an export to a module does NOT publish it; add it to
60
+ * the sub-barrel when a consumer needs it.
59
61
  *
60
- * Currently narrowed (the rest is the intended surface):
61
- * - `services/retry-strategy` — decision functions only; `parseRetryAfter` and
62
- * `addJitter` are backoff internals.
63
- * - `services/circuit-breaker` — `CircuitOpenError` + `resetCircuitBreakers`;
64
- * the `CircuitBreaker` class is wired by the shared HTTP client, not by
65
- * services.
62
+ * Deliberately internal, for example:
63
+ * - `services/retry-strategy` — decision functions only; `parseRetryAfter`,
64
+ * `addJitter` and the DEFAULT_* constants are backoff internals.
65
+ * - `services/circuit-breaker` — `resetCircuitBreakers` only; the
66
+ * `CircuitBreaker` class is wired by the shared HTTP client, not by services.
66
67
  * - `services/service-keys` — verify/inspect only; signing, the key bundle and
67
68
  * `_resetServiceKeysForTests` stay inside.
68
69
  * - `utils/jwk` — constants, shapes, decoders and the two signer-facing helpers
69
70
  * platform needs; `jwkThumbprint` / `publicKeyFromJwk` stay inside.
70
71
  *
71
- * api-core's own modules and its `testing/` helpers reach the excluded symbols
72
- * by DEEP import (`@pipeline-builder/api-core/lib/services/service-keys.js`),
73
- * which is also how the unit tests reach them.
72
+ * api-core's own modules reach the excluded symbols by relative import; unit
73
+ * tests reach them by DEEP import
74
+ * (`@pipeline-builder/api-core/lib/services/service-keys.js`). Test helpers are
75
+ * the separate `@pipeline-builder/api-core/testing` entry, never this barrel.
74
76
  */
75
77
  export * from './types/index.js';
76
78
  export * from './constants/index.js';