@nanonets/graft 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +9 -2
  2. package/dist/app/brain-build.d.ts +66 -0
  3. package/dist/app/brain-build.d.ts.map +1 -0
  4. package/dist/app/brain-build.js +169 -0
  5. package/dist/app/brain-build.js.map +1 -0
  6. package/dist/app/checkout.d.ts +53 -3
  7. package/dist/app/checkout.d.ts.map +1 -1
  8. package/dist/app/checkout.js +195 -27
  9. package/dist/app/checkout.js.map +1 -1
  10. package/dist/app/events.d.ts +7 -0
  11. package/dist/app/events.d.ts.map +1 -1
  12. package/dist/app/events.js +7 -2
  13. package/dist/app/events.js.map +1 -1
  14. package/dist/app/history.d.ts +128 -0
  15. package/dist/app/history.d.ts.map +1 -0
  16. package/dist/app/history.js +269 -0
  17. package/dist/app/history.js.map +1 -0
  18. package/dist/app/identity.d.ts +33 -0
  19. package/dist/app/identity.d.ts.map +1 -1
  20. package/dist/app/identity.js +72 -0
  21. package/dist/app/identity.js.map +1 -1
  22. package/dist/app/main.js +12 -0
  23. package/dist/app/main.js.map +1 -1
  24. package/dist/app/pages.d.ts +17 -0
  25. package/dist/app/pages.d.ts.map +1 -1
  26. package/dist/app/pages.js +112 -5
  27. package/dist/app/pages.js.map +1 -1
  28. package/dist/app/review-process.d.ts +66 -0
  29. package/dist/app/review-process.d.ts.map +1 -0
  30. package/dist/app/review-process.js +199 -0
  31. package/dist/app/review-process.js.map +1 -0
  32. package/dist/app/review-worker.d.ts +2 -0
  33. package/dist/app/review-worker.d.ts.map +1 -0
  34. package/dist/app/review-worker.js +108 -0
  35. package/dist/app/review-worker.js.map +1 -0
  36. package/dist/app/review.d.ts.map +1 -1
  37. package/dist/app/review.js +4 -1
  38. package/dist/app/review.js.map +1 -1
  39. package/dist/app/server.d.ts +22 -2
  40. package/dist/app/server.d.ts.map +1 -1
  41. package/dist/app/server.js +77 -3
  42. package/dist/app/server.js.map +1 -1
  43. package/dist/app/sources.d.ts +68 -0
  44. package/dist/app/sources.d.ts.map +1 -0
  45. package/dist/app/sources.js +341 -0
  46. package/dist/app/sources.js.map +1 -0
  47. package/dist/ask/ask.d.ts +6 -0
  48. package/dist/ask/ask.d.ts.map +1 -1
  49. package/dist/ask/ask.js +30 -2
  50. package/dist/ask/ask.js.map +1 -1
  51. package/dist/brain/attach.d.ts +44 -0
  52. package/dist/brain/attach.d.ts.map +1 -0
  53. package/dist/brain/attach.js +63 -0
  54. package/dist/brain/attach.js.map +1 -0
  55. package/dist/brain/connect.d.ts +59 -0
  56. package/dist/brain/connect.d.ts.map +1 -0
  57. package/dist/brain/connect.js +80 -0
  58. package/dist/brain/connect.js.map +1 -0
  59. package/dist/brain/link.d.ts +82 -0
  60. package/dist/brain/link.d.ts.map +1 -0
  61. package/dist/brain/link.js +136 -0
  62. package/dist/brain/link.js.map +1 -0
  63. package/dist/brain/push.d.ts +72 -0
  64. package/dist/brain/push.d.ts.map +1 -0
  65. package/dist/brain/push.js +191 -0
  66. package/dist/brain/push.js.map +1 -0
  67. package/dist/brain/wire.d.ts +38 -0
  68. package/dist/brain/wire.d.ts.map +1 -0
  69. package/dist/brain/wire.js +133 -0
  70. package/dist/brain/wire.js.map +1 -0
  71. package/dist/claude/format.d.ts.map +1 -1
  72. package/dist/claude/format.js +10 -3
  73. package/dist/claude/format.js.map +1 -1
  74. package/dist/claude/hooks.d.ts.map +1 -1
  75. package/dist/claude/hooks.js +32 -1
  76. package/dist/claude/hooks.js.map +1 -1
  77. package/dist/claude/init.d.ts +5 -0
  78. package/dist/claude/init.d.ts.map +1 -1
  79. package/dist/claude/init.js +9 -1
  80. package/dist/claude/init.js.map +1 -1
  81. package/dist/claude/session-metrics.d.ts +10 -0
  82. package/dist/claude/session-metrics.d.ts.map +1 -1
  83. package/dist/claude/session-metrics.js +25 -0
  84. package/dist/claude/session-metrics.js.map +1 -1
  85. package/dist/claude/settings-merge.d.ts +18 -0
  86. package/dist/claude/settings-merge.d.ts.map +1 -1
  87. package/dist/claude/settings-merge.js +42 -8
  88. package/dist/claude/settings-merge.js.map +1 -1
  89. package/dist/claude/skill-template.d.ts.map +1 -1
  90. package/dist/claude/skill-template.js +6 -0
  91. package/dist/claude/skill-template.js.map +1 -1
  92. package/dist/claude/state.d.ts +14 -0
  93. package/dist/claude/state.d.ts.map +1 -1
  94. package/dist/claude/state.js.map +1 -1
  95. package/dist/claude/tally.d.ts +27 -0
  96. package/dist/claude/tally.d.ts.map +1 -1
  97. package/dist/claude/tally.js +74 -1
  98. package/dist/claude/tally.js.map +1 -1
  99. package/dist/cli.js +210 -3
  100. package/dist/cli.js.map +1 -1
  101. package/dist/context/price.d.ts +49 -0
  102. package/dist/context/price.d.ts.map +1 -0
  103. package/dist/context/price.js +83 -0
  104. package/dist/context/price.js.map +1 -0
  105. package/dist/context/savings.d.ts +13 -3
  106. package/dist/context/savings.d.ts.map +1 -1
  107. package/dist/context/savings.js +44 -5
  108. package/dist/context/savings.js.map +1 -1
  109. package/dist/hosts/claude-global.d.ts +33 -0
  110. package/dist/hosts/claude-global.d.ts.map +1 -0
  111. package/dist/hosts/claude-global.js +118 -0
  112. package/dist/hosts/claude-global.js.map +1 -0
  113. package/dist/hosts/plan.d.ts.map +1 -1
  114. package/dist/hosts/plan.js +5 -1
  115. package/dist/hosts/plan.js.map +1 -1
  116. package/dist/hosts/retract.d.ts.map +1 -1
  117. package/dist/hosts/retract.js +33 -13
  118. package/dist/hosts/retract.js.map +1 -1
  119. package/dist/hosts/sections.d.ts +23 -2
  120. package/dist/hosts/sections.d.ts.map +1 -1
  121. package/dist/hosts/sections.js +25 -9
  122. package/dist/hosts/sections.js.map +1 -1
  123. package/dist/mcp/server.d.ts.map +1 -1
  124. package/dist/mcp/server.js +33 -4
  125. package/dist/mcp/server.js.map +1 -1
  126. package/dist/mcp/tools.d.ts.map +1 -1
  127. package/dist/mcp/tools.js +5 -1
  128. package/dist/mcp/tools.js.map +1 -1
  129. package/dist/telemetry/gate.d.ts +14 -0
  130. package/dist/telemetry/gate.d.ts.map +1 -1
  131. package/dist/telemetry/gate.js +18 -2
  132. package/dist/telemetry/gate.js.map +1 -1
  133. package/dist/upkeep-run.d.ts.map +1 -1
  134. package/dist/upkeep-run.js +12 -2
  135. package/dist/upkeep-run.js.map +1 -1
  136. package/dist/upkeep.d.ts +18 -0
  137. package/dist/upkeep.d.ts.map +1 -1
  138. package/dist/upkeep.js +38 -0
  139. package/dist/upkeep.js.map +1 -1
  140. package/dist/util/state.d.ts +9 -0
  141. package/dist/util/state.d.ts.map +1 -1
  142. package/dist/util/state.js.map +1 -1
  143. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/app/identity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/app/identity.ts"],"names":[],"mappings":"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"}
package/dist/app/main.js CHANGED
@@ -37,6 +37,18 @@ const { server, queue } = createApp({
37
37
  publicUrl: required("GRAFT_PUBLIC_URL"),
38
38
  port,
39
39
  concurrency: Number(process.env.GRAFT_CONCURRENCY ?? 2),
40
+ // Optional, unlike the four above, and deliberately so: point it at a mounted
41
+ // volume and the pages already linked from pull requests survive a restart;
42
+ // leave it unset and they do not. Neither is worth refusing to boot over — a
43
+ // server that will not start reviews nothing at all, which is strictly worse
44
+ // than one whose old links go stale.
45
+ pageDir: process.env.GRAFT_PAGE_DIR,
46
+ // Optional for the same reason as pageDir: unset simply disables
47
+ // `POST /brain/build`, and a deployment that only reviews pull requests wants
48
+ // exactly that. The route is the one place the app writes to the platform, so
49
+ // it stays off until someone deliberately turns it on.
50
+ brainBuildSecret: process.env.GRAFT_BRAIN_BUILD_SECRET,
51
+ brainBaseUrl: process.env.GRAFT_BRAIN_URL,
40
52
  });
41
53
  /**
42
54
  * Finish what is in flight before dying.
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/app/main.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,cAAc,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU;IACjB,MAAM,GAAG,GAAG,QAAQ,CAAC,uBAAuB,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAClE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QAC3D,OAAO,CAAC,KAAK,CAAC,8DAA8D,CAAC,CAAC;QAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAC9C,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;IAClC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC;IAC/B,UAAU,EAAE,UAAU,EAAE;IACxB,aAAa,EAAE,QAAQ,CAAC,sBAAsB,CAAC;IAC/C,SAAS,EAAE,QAAQ,CAAC,kBAAkB,CAAC;IACvC,IAAI;IACJ,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,CAAC;CACxD,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,KAAK,MAAM,MAAM,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAU,EAAE,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACtB,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,cAAc,KAAK,CAAC,IAAI,SAAS,CAAC,CAAC;QACxD,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/C,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;IACpD,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/app/main.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,cAAc,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU;IACjB,MAAM,GAAG,GAAG,QAAQ,CAAC,uBAAuB,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAClE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QAC3D,OAAO,CAAC,KAAK,CAAC,8DAA8D,CAAC,CAAC;QAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAC9C,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;IAClC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC;IAC/B,UAAU,EAAE,UAAU,EAAE;IACxB,aAAa,EAAE,QAAQ,CAAC,sBAAsB,CAAC;IAC/C,SAAS,EAAE,QAAQ,CAAC,kBAAkB,CAAC;IACvC,IAAI;IACJ,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,CAAC;IACvD,8EAA8E;IAC9E,4EAA4E;IAC5E,6EAA6E;IAC7E,6EAA6E;IAC7E,qCAAqC;IACrC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc;IACnC,iEAAiE;IACjE,8EAA8E;IAC9E,8EAA8E;IAC9E,uDAAuD;IACvD,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB;IACtD,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe;CAC1C,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,KAAK,MAAM,MAAM,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAU,EAAE,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACtB,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,cAAc,KAAK,CAAC,IAAI,SAAS,CAAC,CAAC;QACxD,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/C,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;IACpD,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -7,6 +7,8 @@ export interface PageStoreOptions {
7
7
  ttlMs?: number;
8
8
  maxPages?: number;
9
9
  now?: () => number;
10
+ /** Kept across restarts here. Absent: pages live only as long as the process. */
11
+ dir?: string;
10
12
  }
11
13
  export declare class PageStore {
12
14
  private readonly pages;
@@ -14,6 +16,7 @@ export declare class PageStore {
14
16
  private readonly ttlMs;
15
17
  private readonly maxPages;
16
18
  private readonly now;
19
+ private readonly dir;
17
20
  constructor(opts: PageStoreOptions);
18
21
  /** `owner/repo#number` → a path segment safe to put in a URL. */
19
22
  static idFor(owner: string, repo: string, number: number): string;
@@ -28,6 +31,9 @@ export declare class PageStore {
28
31
  * Expiry is checked against the STORED time rather than encoded in the token,
29
32
  * so a link cannot outlive the data it points at — and re-reviewing a PR
30
33
  * refreshes both together.
34
+ *
35
+ * Answered from memory even when there is a directory: `id` comes off a URL,
36
+ * and an id that never reaches the filesystem cannot be walked out of it.
31
37
  */
32
38
  get(id: string, token: string | undefined): string | null;
33
39
  get size(): number;
@@ -35,5 +41,16 @@ export declare class PageStore {
35
41
  private valid;
36
42
  /** Oldest out first, plus anything past its TTL. */
37
43
  private evict;
44
+ /** Take back what a previous process left on disk. */
45
+ private hydrate;
46
+ /** A stored page, or nothing: truncated, corrupt and gone are one answer here. */
47
+ private read;
48
+ /**
49
+ * Temp file, then rename: a reader only ever sees a whole page, and two reviews
50
+ * of the same pull request racing each other leave one of them intact rather
51
+ * than the two interleaved.
52
+ */
53
+ private write;
54
+ private remove;
38
55
  }
39
56
  //# sourceMappingURL=pages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../src/app/pages.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,IAAI,EAAE,gBAAgB;IAOlC,iEAAiE;IACjE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAIjE,qFAAqF;IACrF,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAO7F;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI;IAUzD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,KAAK;IAQb,oDAAoD;IACpD,OAAO,CAAC,KAAK;CASd"}
1
+ {"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../src/app/pages.ts"],"names":[],"mappings":"AAgCA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,iFAAiF;IACjF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqB;gBAE7B,IAAI,EAAE,gBAAgB;IASlC,iEAAiE;IACjE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAIjE,qFAAqF;IACrF,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAS7F;;;;;;;;;OASG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI;IAUzD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,KAAK;IAQb,oDAAoD;IACpD,OAAO,CAAC,KAAK;IAUb,sDAAsD;IACtD,OAAO,CAAC,OAAO;IA+Bf,kFAAkF;IAClF,OAAO,CAAC,IAAI;IAWZ;;;;OAIG;IACH,OAAO,CAAC,KAAK;IAsBb,OAAO,CAAC,MAAM;CASf"}
package/dist/app/pages.js CHANGED
@@ -10,23 +10,38 @@
10
10
  * capability, not a login. Anyone with the link can read that one page until it
11
11
  * expires, which is the same property a GitHub artifact link has, and the link
12
12
  * only ever appears in a comment on the pull request it describes.
13
+ *
14
+ * Pages are mirrored to `dir`, when one is given, because the token is DERIVED
15
+ * from the id rather than stored: a link keeps passing the signature check for as
16
+ * long as the secret lives, so a process that lost its pages answers 404 to a
17
+ * link that looks perfectly valid. That failure is silent at both ends — the
18
+ * comment still sits on the pull request, the reviewer just sees "not found" —
19
+ * and one restart of a container with nothing mounted stranded 29 of them. Disk
20
+ * is the fix; without a directory this behaves exactly as it did before.
13
21
  */
14
- import { createHmac, timingSafeEqual } from "node:crypto";
22
+ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
23
+ import { mkdirSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
24
+ import { join } from "node:path";
15
25
  /** Long enough to review a PR at leisure, short enough that a leaked link dies. */
16
26
  const DEFAULT_TTL_MS = 14 * 24 * 60 * 60 * 1000;
17
27
  /** A page is ~50 kB; a few hundred of them is nothing, and eviction is by age. */
18
28
  const DEFAULT_MAX_PAGES = 500;
29
+ /** One file per page, named by its id. The extension also means "fully written". */
30
+ const SUFFIX = ".json";
19
31
  export class PageStore {
20
32
  pages = new Map();
21
33
  secret;
22
34
  ttlMs;
23
35
  maxPages;
24
36
  now;
37
+ dir;
25
38
  constructor(opts) {
26
39
  this.secret = opts.secret;
27
40
  this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
28
41
  this.maxPages = opts.maxPages ?? DEFAULT_MAX_PAGES;
29
42
  this.now = opts.now ?? Date.now;
43
+ this.dir = opts.dir;
44
+ this.hydrate();
30
45
  }
31
46
  /** `owner/repo#number` → a path segment safe to put in a URL. */
32
47
  static idFor(owner, repo, number) {
@@ -35,7 +50,9 @@ export class PageStore {
35
50
  /** Store (replacing any previous page for the same PR) and return `id` + `token`. */
36
51
  put(owner, repo, number, html) {
37
52
  const id = PageStore.idFor(owner, repo, number);
38
- this.pages.set(id, { html, storedMs: this.now() });
53
+ const page = { html, storedMs: this.now() };
54
+ this.pages.set(id, page);
55
+ this.write(id, page);
39
56
  this.evict();
40
57
  return { id, token: this.sign(id) };
41
58
  }
@@ -45,13 +62,16 @@ export class PageStore {
45
62
  * Expiry is checked against the STORED time rather than encoded in the token,
46
63
  * so a link cannot outlive the data it points at — and re-reviewing a PR
47
64
  * refreshes both together.
65
+ *
66
+ * Answered from memory even when there is a directory: `id` comes off a URL,
67
+ * and an id that never reaches the filesystem cannot be walked out of it.
48
68
  */
49
69
  get(id, token) {
50
70
  const page = this.pages.get(id);
51
71
  if (!page || !token || !this.valid(id, token))
52
72
  return null;
53
73
  if (this.now() - page.storedMs > this.ttlMs) {
54
- this.pages.delete(id);
74
+ this.remove(id);
55
75
  return null;
56
76
  }
57
77
  return page.html;
@@ -74,13 +94,100 @@ export class PageStore {
74
94
  const cutoff = this.now() - this.ttlMs;
75
95
  for (const [id, page] of this.pages)
76
96
  if (page.storedMs < cutoff)
77
- this.pages.delete(id);
97
+ this.remove(id);
78
98
  while (this.pages.size > this.maxPages) {
79
99
  const oldest = this.pages.keys().next();
80
100
  if (oldest.done)
81
101
  break;
82
- this.pages.delete(oldest.value);
102
+ this.remove(oldest.value);
103
+ }
104
+ }
105
+ /** Take back what a previous process left on disk. */
106
+ hydrate() {
107
+ const dir = this.dir;
108
+ if (!dir)
109
+ return;
110
+ let names;
111
+ try {
112
+ mkdirSync(dir, { recursive: true });
113
+ names = readdirSync(dir);
114
+ }
115
+ catch (err) {
116
+ // Nothing usable there: carry on in memory, which is exactly what every
117
+ // deployment without a directory does anyway.
118
+ console.warn(`page store: ${dir} is unusable, pages will not survive a restart (${reason(err)})`);
119
+ return;
120
+ }
121
+ const loaded = [];
122
+ for (const name of names) {
123
+ // The suffix also skips the temp files a write leaves behind when it dies
124
+ // partway, so a half-written page is never a candidate to begin with.
125
+ if (!name.endsWith(SUFFIX))
126
+ continue;
127
+ const page = this.read(join(dir, name));
128
+ if (page)
129
+ loaded.push([name.slice(0, -SUFFIX.length), page]);
130
+ }
131
+ // Oldest first, because eviction takes the front of the Map and readdir order
132
+ // is the filesystem's business rather than a record of what happened when.
133
+ loaded.sort((a, b) => a[1].storedMs - b[1].storedMs);
134
+ for (const [id, page] of loaded)
135
+ this.pages.set(id, page);
136
+ // A restart is also when pages that expired while the process was down go.
137
+ this.evict();
138
+ }
139
+ /** A stored page, or nothing: truncated, corrupt and gone are one answer here. */
140
+ read(file) {
141
+ try {
142
+ const parsed = JSON.parse(readFileSync(file, "utf8"));
143
+ const { html, storedMs } = (parsed ?? {});
144
+ if (typeof html !== "string" || typeof storedMs !== "number" || !Number.isFinite(storedMs))
145
+ return null;
146
+ return { html, storedMs };
147
+ }
148
+ catch {
149
+ return null;
150
+ }
151
+ }
152
+ /**
153
+ * Temp file, then rename: a reader only ever sees a whole page, and two reviews
154
+ * of the same pull request racing each other leave one of them intact rather
155
+ * than the two interleaved.
156
+ */
157
+ write(id, page) {
158
+ const dir = this.dir;
159
+ if (!dir)
160
+ return;
161
+ const tmp = join(dir, `${id}.${randomBytes(6).toString("hex")}.tmp`);
162
+ try {
163
+ mkdirSync(dir, { recursive: true });
164
+ writeFileSync(tmp, JSON.stringify(page));
165
+ renameSync(tmp, join(dir, `${id}${SUFFIX}`));
166
+ }
167
+ catch (err) {
168
+ // The page is in memory and its link works; it just will not outlive this
169
+ // process. Worth a line in the log and nothing more: throwing here would
170
+ // fail a review that has already done all of its work.
171
+ console.warn(`page store: could not write ${id} (${reason(err)})`);
172
+ try {
173
+ unlinkSync(tmp);
174
+ }
175
+ catch {
176
+ // Never created, or already renamed into place.
177
+ }
178
+ }
179
+ }
180
+ remove(id) {
181
+ this.pages.delete(id);
182
+ if (!this.dir)
183
+ return;
184
+ try {
185
+ unlinkSync(join(this.dir, `${id}${SUFFIX}`));
186
+ }
187
+ catch {
188
+ // Already gone: deleted underneath us, or it never reached the disk.
83
189
  }
84
190
  }
85
191
  }
192
+ const reason = (err) => (err instanceof Error ? err.message : String(err));
86
193
  //# sourceMappingURL=pages.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pages.js","sourceRoot":"","sources":["../../src/app/pages.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pages.js","sourceRoot":"","sources":["../../src/app/pages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import { type ReviewJob } from "./events.js";
2
+ import type { reviewPullRequest, ReviewResult } from "./review.js";
3
+ /** Parent → child, once, immediately after the fork. */
4
+ export interface StartMessage {
5
+ t: "start";
6
+ job: ReviewJob;
7
+ /** Minted by the parent; the child never sees the App's private key. */
8
+ token: string;
9
+ api?: string;
10
+ }
11
+ /** Parent → child: the URL a `publish` request ended up at, or null. */
12
+ export interface PublishedMessage {
13
+ t: "published";
14
+ seq: number;
15
+ url: string | null;
16
+ }
17
+ export type ToChild = StartMessage | PublishedMessage;
18
+ /** Child → parent: a line for the App's log, so a review is still traceable. */
19
+ export interface LogMessage {
20
+ t: "log";
21
+ msg: string;
22
+ }
23
+ /** Child → parent: store this page and tell me its URL. */
24
+ export interface PublishMessage {
25
+ t: "publish";
26
+ seq: number;
27
+ html: string;
28
+ }
29
+ export interface DoneMessage {
30
+ t: "done";
31
+ result: ReviewResult;
32
+ }
33
+ export interface FailedMessage {
34
+ t: "failed";
35
+ message: string;
36
+ }
37
+ export type FromChild = LogMessage | PublishMessage | DoneMessage | FailedMessage;
38
+ export interface ChildReviewerOptions {
39
+ /** The module the child runs. Only tests pass this. */
40
+ entry?: string;
41
+ timeoutMs?: number;
42
+ }
43
+ /**
44
+ * The module a review runs in.
45
+ *
46
+ * `dist/app/review-worker.js` in an installed or containerised App. Run from a
47
+ * checkout there is no `dist`, so fall back to the TypeScript source next door:
48
+ * `fork` inherits `process.execArgv`, so a parent started through tsx hands the
49
+ * child the same loader and a `.ts` entry runs. Resolved once, at import.
50
+ *
51
+ * Exported so a test can assert the path a deployed App will fork actually exists —
52
+ * getting this wrong is the one failure here that no unit test of the protocol
53
+ * would catch and every review would hit.
54
+ */
55
+ export declare function reviewWorkerEntry(): string;
56
+ /**
57
+ * A reviewer with `reviewPullRequest`'s signature that runs it in a child process.
58
+ *
59
+ * Same shape on purpose: `server.ts` swaps one for the other in a single line, and
60
+ * every existing test that injects its own reviewer through `AppSeams.review` keeps
61
+ * running in-process, which is what a test wants.
62
+ */
63
+ export declare function childReviewer(opts?: ChildReviewerOptions): typeof reviewPullRequest;
64
+ /** The App's default reviewer. */
65
+ export declare const reviewInChildProcess: typeof reviewPullRequest;
66
+ //# sourceMappingURL=review-process.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-process.d.ts","sourceRoot":"","sources":["../../src/app/review-process.ts"],"names":[],"mappings":"AAqDA,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAc,YAAY,EAAE,MAAM,aAAa,CAAC;AAa/E,wDAAwD;AACxD,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,OAAO,CAAC;IACX,GAAG,EAAE,SAAS,CAAC;IACf,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,WAAW,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,MAAM,MAAM,OAAO,GAAG,YAAY,GAAG,gBAAgB,CAAC;AAEtD,gFAAgF;AAChF,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,KAAK,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;CACb;AAED,2DAA2D;AAC3D,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,SAAS,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,CAAC,EAAE,QAAQ,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,cAAc,GAAG,WAAW,GAAG,aAAa,CAAC;AAElF,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAK1C;AAsCD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,GAAE,oBAAyB,GAAG,OAAO,iBAAiB,CAIvF;AAoFD,kCAAkC;AAClC,eAAO,MAAM,oBAAoB,0BAAkB,CAAC"}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Running a review somewhere it cannot stop the App answering.
3
+ *
4
+ * A review is synchronous from end to end, and not in one place we could fix: the
5
+ * pull request is fetched with `spawnSync` (`./checkout.ts`), the whole repository
6
+ * is parsed in one `files.forEach` loop of native tree-sitter calls
7
+ * (`graph/build.ts`), the graph is written with a blocking `JSON.stringify`, and
8
+ * the viewer page is exported synchronously too. In-process that holds the event
9
+ * loop for the entire review.
10
+ *
11
+ * That is not a theory. On the deployed App, probing `GET /p/<id>` from outside
12
+ * every ten seconds while one review ran: three consecutive timeouts (no response
13
+ * at all, not even with `--max-time 45`), then 404 in 0.9s the moment the queue
14
+ * emptied. Meanwhile the box's load average was 0.96 on eight vCPUs — one thread
15
+ * pegged, seven cores idle — and the App's own logs put a single review at 92s to
16
+ * 274s ("3 changed → 0 affected in 180134ms"). So a *single* in-flight review took
17
+ * the whole HTTP surface down for minutes: the container's 5s `HEALTHCHECK` failed,
18
+ * Docker marked the container unhealthy, and pages the App had already produced
19
+ * were unreachable. On a busy repository that is most of the working day.
20
+ *
21
+ * Neither bounding the queue nor lowering `GRAFT_CONCURRENCY` touches this —
22
+ * concurrency was already 2 and one job is enough. Cooperative yielding inside the
23
+ * parse loop does not either: a `git fetch` of a large repository is one
24
+ * uninterruptible `spawnSync` that can legitimately take a minute, and it is not
25
+ * even using a core while it blocks. The work has to leave the process.
26
+ *
27
+ * A child process rather than a `worker_threads` worker, deliberately:
28
+ *
29
+ * - **The native grammars keep loading the way they already do.** `graph/extract.ts`
30
+ * imports nine tree-sitter addons at module scope and constructs a `Parser` there
31
+ * too, and `graph/generic.ts` loads more through `createRequire`. In a worker all
32
+ * of that is re-instantiated per thread, which puts the N-API-in-worker behaviour
33
+ * of nine third-party native modules between the App and every review. A fork runs
34
+ * byte-identical code to what production runs today.
35
+ * - **A review can be killed.** A wedged parse or a 10-minute clone is a process
36
+ * to SIGKILL, not a thread with no safe way to stop it.
37
+ * - **A crash stops being an outage.** An OOM kill or a grammar segfault on a
38
+ * stranger's source now costs one review and is reported as a failure, instead
39
+ * of taking the HTTP server with it. This process clones code written by
40
+ * people who opened a pull request; that isolation is worth having on its own.
41
+ * - **The heap goes away.** A repository's graph is freed by the child exiting
42
+ * rather than left for the server process to fragment around.
43
+ *
44
+ * The parent keeps exactly the two things a review cannot mint for itself, and
45
+ * hands each across the IPC channel rather than argv or the environment (`ps` and
46
+ * `/proc/<pid>/environ` are readable; an installation token belongs in neither):
47
+ * the token, so the App's private key never crosses the boundary, and page
48
+ * publishing, because the store and the secret it signs URLs with live with the
49
+ * server.
50
+ */
51
+ import { fork } from "node:child_process";
52
+ import { existsSync } from "node:fs";
53
+ import { fileURLToPath } from "node:url";
54
+ import { jobKey } from "./events.js";
55
+ /**
56
+ * How long one review may take before the process running it is killed.
57
+ *
58
+ * Generously above the worst honest review observed in production (274s) and above
59
+ * what `checkout.ts`'s own 120s-per-git-call budget can add up to, because the cost
60
+ * of being wrong here is a review that never happens. It is a ceiling on a *lost*
61
+ * queue slot, not on responsiveness — the server no longer cares how long a review
62
+ * runs, only that the slot comes back.
63
+ */
64
+ const DEFAULT_TIMEOUT_MS = 15 * 60 * 1000;
65
+ /**
66
+ * The module a review runs in.
67
+ *
68
+ * `dist/app/review-worker.js` in an installed or containerised App. Run from a
69
+ * checkout there is no `dist`, so fall back to the TypeScript source next door:
70
+ * `fork` inherits `process.execArgv`, so a parent started through tsx hands the
71
+ * child the same loader and a `.ts` entry runs. Resolved once, at import.
72
+ *
73
+ * Exported so a test can assert the path a deployed App will fork actually exists —
74
+ * getting this wrong is the one failure here that no unit test of the protocol
75
+ * would catch and every review would hit.
76
+ */
77
+ export function reviewWorkerEntry() {
78
+ const js = fileURLToPath(new URL("./review-worker.js", import.meta.url));
79
+ if (existsSync(js))
80
+ return js;
81
+ const ts = js.replace(/\.js$/, ".ts");
82
+ return existsSync(ts) ? ts : js;
83
+ }
84
+ /**
85
+ * Live review processes.
86
+ *
87
+ * `main.ts` force-exits 25 seconds after SIGTERM if a review has not finished
88
+ * draining. That used to kill the review along with the process; now the review is
89
+ * a separate process that would outlive us, still holding a clone made with an
90
+ * installation token. So the last thing this process does is take them with it.
91
+ *
92
+ * A container stop is unaffected either way: Docker signals PID 1, not the process
93
+ * group, so a review keeps going through the grace period and the drain in `main.ts`
94
+ * means what it says. Ctrl-C in a terminal signals the whole group, so the children
95
+ * die with the default disposition and their reviews are reported as killed — which
96
+ * is what an interrupted review was before this too.
97
+ */
98
+ const live = new Set();
99
+ let hooked = false;
100
+ function track(child) {
101
+ live.add(child);
102
+ if (hooked)
103
+ return;
104
+ hooked = true;
105
+ process.on("exit", () => {
106
+ for (const c of live)
107
+ c.kill("SIGKILL");
108
+ });
109
+ }
110
+ /** A dead channel is not worth an exception: the exit handler already covers it. */
111
+ function post(child, msg) {
112
+ if (!child.connected)
113
+ return;
114
+ try {
115
+ child.send(msg);
116
+ }
117
+ catch {
118
+ /* channel closed under us — `exit` will settle the review */
119
+ }
120
+ }
121
+ /**
122
+ * A reviewer with `reviewPullRequest`'s signature that runs it in a child process.
123
+ *
124
+ * Same shape on purpose: `server.ts` swaps one for the other in a single line, and
125
+ * every existing test that injects its own reviewer through `AppSeams.review` keeps
126
+ * running in-process, which is what a test wants.
127
+ */
128
+ export function childReviewer(opts = {}) {
129
+ const entry = opts.entry ?? reviewWorkerEntry();
130
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
131
+ return (job, deps) => runInChild(job, deps, entry, timeoutMs);
132
+ }
133
+ async function runInChild(job, deps, entry, timeoutMs) {
134
+ const log = deps.log ?? (() => { });
135
+ // Minted here rather than in the child: this is a network round trip, never a
136
+ // blocking one, and the cache that owns installation tokens — including the
137
+ // forget-a-rejected-token behaviour — belongs to the one process that sees every
138
+ // delivery.
139
+ const token = await deps.token(job.installationId);
140
+ // The job key as argv makes `ps` in a container say which pull request each
141
+ // review is; the token stays out of it.
142
+ const child = fork(entry, [jobKey(job)], { stdio: ["ignore", "inherit", "inherit", "ipc"] });
143
+ track(child);
144
+ try {
145
+ return await new Promise((resolve, reject) => {
146
+ let settled = false;
147
+ const settle = (fn) => {
148
+ if (settled)
149
+ return;
150
+ settled = true;
151
+ clearTimeout(timer);
152
+ fn();
153
+ };
154
+ const timer = setTimeout(() => {
155
+ settle(() => reject(new Error(`review exceeded ${timeoutMs}ms and was killed`)));
156
+ }, timeoutMs);
157
+ timer.unref();
158
+ child.on("message", (raw) => {
159
+ const msg = raw;
160
+ if (msg.t === "log") {
161
+ log(msg.msg);
162
+ return;
163
+ }
164
+ if (msg.t === "publish") {
165
+ // Publishing happens here because the store and its signing secret do.
166
+ // A page that cannot be stored costs the link, never the review — the
167
+ // same contract `review.ts` already has with a null `publish`.
168
+ const stored = deps.publish ? deps.publish(job, msg.html) : Promise.resolve(null);
169
+ void stored
170
+ .catch(() => null)
171
+ .then((url) => post(child, { t: "published", seq: msg.seq, url }));
172
+ return;
173
+ }
174
+ if (msg.t === "done") {
175
+ settle(() => resolve(msg.result));
176
+ return;
177
+ }
178
+ settle(() => reject(new Error(msg.message)));
179
+ });
180
+ child.on("error", (err) => settle(() => reject(err)));
181
+ // The case that used to be an App-wide outage: a child that dies without
182
+ // reporting. An OOM kill, a segfault in a grammar on hostile source. Now it
183
+ // is one failed review, named, with the signal that did it.
184
+ child.on("exit", (code, signal) => settle(() => reject(new Error(`review process exited ${signal ? `on ${signal}` : `with code ${code}`} before reporting a result`))));
185
+ post(child, { t: "start", job, token, api: deps.api });
186
+ });
187
+ }
188
+ finally {
189
+ live.delete(child);
190
+ // Whatever happened — done, failed, timed out, or a delivery superseded by the
191
+ // queue — nothing forked here is allowed to outlive the review. `review.ts`
192
+ // deletes the checkout before it reports, so this leaves nothing behind.
193
+ if (child.exitCode === null && child.signalCode === null)
194
+ child.kill("SIGKILL");
195
+ }
196
+ }
197
+ /** The App's default reviewer. */
198
+ export const reviewInChildProcess = childReviewer();
199
+ //# sourceMappingURL=review-process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-process.js","sourceRoot":"","sources":["../../src/app/review-process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=review-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-worker.d.ts","sourceRoot":"","sources":["../../src/app/review-worker.ts"],"names":[],"mappings":""}