@quenty/nevermore-cli 4.41.1 → 4.42.1-canary.783.30683193716.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 (207) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/args/global-args.d.ts +13 -0
  3. package/dist/args/global-args.d.ts.map +1 -1
  4. package/dist/commands/batch-command/batch-deploy-command.d.ts +3 -0
  5. package/dist/commands/batch-command/batch-deploy-command.d.ts.map +1 -1
  6. package/dist/commands/batch-command/batch-deploy-command.js +90 -1
  7. package/dist/commands/batch-command/batch-deploy-command.js.map +1 -1
  8. package/dist/commands/batch-command/batch-test-command.d.ts.map +1 -1
  9. package/dist/commands/batch-command/batch-test-command.js +25 -4
  10. package/dist/commands/batch-command/batch-test-command.js.map +1 -1
  11. package/dist/commands/deploy-command/deploy-init.js +1 -1
  12. package/dist/commands/deploy-command/deploy-init.js.map +1 -1
  13. package/dist/commands/deploy-command/index.d.ts +3 -0
  14. package/dist/commands/deploy-command/index.d.ts.map +1 -1
  15. package/dist/commands/deploy-command/index.js +297 -60
  16. package/dist/commands/deploy-command/index.js.map +1 -1
  17. package/dist/commands/deploy-command/select-target.js +1 -1
  18. package/dist/commands/deploy-command/select-target.js.map +1 -1
  19. package/dist/commands/deploy-command/version-command.d.ts.map +1 -1
  20. package/dist/commands/deploy-command/version-command.js +138 -12
  21. package/dist/commands/deploy-command/version-command.js.map +1 -1
  22. package/dist/commands/test-command/test-command.d.ts.map +1 -1
  23. package/dist/commands/test-command/test-command.js +23 -11
  24. package/dist/commands/test-command/test-command.js.map +1 -1
  25. package/dist/nevermore.js +17 -0
  26. package/dist/nevermore.js.map +1 -1
  27. package/dist/utils/batch/changed-packages-utils.d.ts +1 -1
  28. package/dist/utils/batch/changed-packages-utils.d.ts.map +1 -1
  29. package/dist/utils/batch/changed-packages-utils.js +1 -1
  30. package/dist/utils/batch/changed-packages-utils.js.map +1 -1
  31. package/dist/utils/build/base-place-resolver-factory.d.ts +25 -0
  32. package/dist/utils/build/base-place-resolver-factory.d.ts.map +1 -0
  33. package/dist/utils/build/base-place-resolver-factory.js +40 -0
  34. package/dist/utils/build/base-place-resolver-factory.js.map +1 -0
  35. package/dist/utils/build/build.d.ts +13 -2
  36. package/dist/utils/build/build.d.ts.map +1 -1
  37. package/dist/utils/build/build.js.map +1 -1
  38. package/dist/utils/build/upload.d.ts +1 -1
  39. package/dist/utils/build/upload.d.ts.map +1 -1
  40. package/dist/utils/deploy/deploy-metadata.d.ts +1 -1
  41. package/dist/utils/deploy/deploy-metadata.d.ts.map +1 -1
  42. package/dist/utils/job-context/base-job-context.d.ts +3 -1
  43. package/dist/utils/job-context/base-job-context.d.ts.map +1 -1
  44. package/dist/utils/job-context/base-job-context.js +45 -6
  45. package/dist/utils/job-context/base-job-context.js.map +1 -1
  46. package/dist/utils/job-context/batch-script-job-context.d.ts +6 -0
  47. package/dist/utils/job-context/batch-script-job-context.d.ts.map +1 -1
  48. package/dist/utils/job-context/batch-script-job-context.js +13 -0
  49. package/dist/utils/job-context/batch-script-job-context.js.map +1 -1
  50. package/dist/utils/job-context/cloud-job-context.d.ts +2 -1
  51. package/dist/utils/job-context/cloud-job-context.d.ts.map +1 -1
  52. package/dist/utils/job-context/cloud-job-context.js +2 -2
  53. package/dist/utils/job-context/cloud-job-context.js.map +1 -1
  54. package/dist/utils/job-context/local-job-context.d.ts +2 -1
  55. package/dist/utils/job-context/local-job-context.d.ts.map +1 -1
  56. package/dist/utils/job-context/local-job-context.js +2 -2
  57. package/dist/utils/job-context/local-job-context.js.map +1 -1
  58. package/dist/utils/open-cloud/open-cloud-client.d.ts +20 -2
  59. package/dist/utils/open-cloud/open-cloud-client.d.ts.map +1 -1
  60. package/dist/utils/open-cloud/open-cloud-client.js +78 -21
  61. package/dist/utils/open-cloud/open-cloud-client.js.map +1 -1
  62. package/dist/utils/open-cloud/open-cloud-client.test.js +89 -12
  63. package/dist/utils/open-cloud/open-cloud-client.test.js.map +1 -1
  64. package/dist/utils/testing/parsers/batch-log-parser.d.ts +13 -5
  65. package/dist/utils/testing/parsers/batch-log-parser.d.ts.map +1 -1
  66. package/dist/utils/testing/parsers/batch-log-parser.js +114 -68
  67. package/dist/utils/testing/parsers/batch-log-parser.js.map +1 -1
  68. package/dist/utils/testing/parsers/batch-log-parser.test.js +57 -5
  69. package/dist/utils/testing/parsers/batch-log-parser.test.js.map +1 -1
  70. package/dist/utils/testing/parsers/traceback-parser.d.ts +31 -0
  71. package/dist/utils/testing/parsers/traceback-parser.d.ts.map +1 -0
  72. package/dist/utils/testing/parsers/traceback-parser.js +91 -0
  73. package/dist/utils/testing/parsers/traceback-parser.js.map +1 -0
  74. package/dist/utils/testing/parsers/traceback-parser.test.d.ts +2 -0
  75. package/dist/utils/testing/parsers/traceback-parser.test.d.ts.map +1 -0
  76. package/dist/utils/testing/parsers/traceback-parser.test.js +45 -0
  77. package/dist/utils/testing/parsers/traceback-parser.test.js.map +1 -0
  78. package/dist/utils/testing/runner/combined-project-generator.d.ts +1 -1
  79. package/dist/utils/testing/runner/combined-project-generator.d.ts.map +1 -1
  80. package/dist/utils/testing/runner/test-runner.d.ts +9 -1
  81. package/dist/utils/testing/runner/test-runner.d.ts.map +1 -1
  82. package/dist/utils/testing/runner/test-runner.js +28 -3
  83. package/dist/utils/testing/runner/test-runner.js.map +1 -1
  84. package/dist/utils/testing/runner/test-runner.test.d.ts +2 -0
  85. package/dist/utils/testing/runner/test-runner.test.d.ts.map +1 -0
  86. package/dist/utils/testing/runner/test-runner.test.js +18 -0
  87. package/dist/utils/testing/runner/test-runner.test.js.map +1 -0
  88. package/dist/utils/testing/test-log-parser.d.ts +32 -1
  89. package/dist/utils/testing/test-log-parser.d.ts.map +1 -1
  90. package/dist/utils/testing/test-log-parser.js +53 -9
  91. package/dist/utils/testing/test-log-parser.js.map +1 -1
  92. package/dist/utils/testing/test-log-parser.test.d.ts +2 -0
  93. package/dist/utils/testing/test-log-parser.test.d.ts.map +1 -0
  94. package/dist/utils/testing/test-log-parser.test.js +56 -0
  95. package/dist/utils/testing/test-log-parser.test.js.map +1 -0
  96. package/dist/utils/watch/github-context.d.ts +50 -0
  97. package/dist/utils/watch/github-context.d.ts.map +1 -0
  98. package/dist/utils/watch/github-context.js +125 -0
  99. package/dist/utils/watch/github-context.js.map +1 -0
  100. package/dist/utils/watch/github-context.test.d.ts +2 -0
  101. package/dist/utils/watch/github-context.test.d.ts.map +1 -0
  102. package/dist/utils/watch/github-context.test.js +112 -0
  103. package/dist/utils/watch/github-context.test.js.map +1 -0
  104. package/dist/utils/watch/http-watch-registry.d.ts +40 -0
  105. package/dist/utils/watch/http-watch-registry.d.ts.map +1 -0
  106. package/dist/utils/watch/http-watch-registry.js +262 -0
  107. package/dist/utils/watch/http-watch-registry.js.map +1 -0
  108. package/dist/utils/watch/http-watch-registry.test.d.ts +2 -0
  109. package/dist/utils/watch/http-watch-registry.test.d.ts.map +1 -0
  110. package/dist/utils/watch/http-watch-registry.test.js +211 -0
  111. package/dist/utils/watch/http-watch-registry.test.js.map +1 -0
  112. package/dist/utils/watch/local-watch-loop.d.ts +50 -0
  113. package/dist/utils/watch/local-watch-loop.d.ts.map +1 -0
  114. package/dist/utils/watch/local-watch-loop.js +103 -0
  115. package/dist/utils/watch/local-watch-loop.js.map +1 -0
  116. package/dist/utils/watch/local-watch-loop.test.d.ts +2 -0
  117. package/dist/utils/watch/local-watch-loop.test.d.ts.map +1 -0
  118. package/dist/utils/watch/local-watch-loop.test.js +170 -0
  119. package/dist/utils/watch/local-watch-loop.test.js.map +1 -0
  120. package/dist/utils/watch/register-notify-watches.d.ts +41 -0
  121. package/dist/utils/watch/register-notify-watches.d.ts.map +1 -0
  122. package/dist/utils/watch/register-notify-watches.js +159 -0
  123. package/dist/utils/watch/register-notify-watches.js.map +1 -0
  124. package/dist/utils/watch/register-notify-watches.test.d.ts +2 -0
  125. package/dist/utils/watch/register-notify-watches.test.d.ts.map +1 -0
  126. package/dist/utils/watch/register-notify-watches.test.js +225 -0
  127. package/dist/utils/watch/register-notify-watches.test.js.map +1 -0
  128. package/dist/utils/watch/register-watches.d.ts +68 -0
  129. package/dist/utils/watch/register-watches.d.ts.map +1 -0
  130. package/dist/utils/watch/register-watches.js +221 -0
  131. package/dist/utils/watch/register-watches.js.map +1 -0
  132. package/dist/utils/watch/register-watches.test.d.ts +2 -0
  133. package/dist/utils/watch/register-watches.test.d.ts.map +1 -0
  134. package/dist/utils/watch/register-watches.test.js +576 -0
  135. package/dist/utils/watch/register-watches.test.js.map +1 -0
  136. package/dist/utils/watch/stream-watch-loop.d.ts +54 -0
  137. package/dist/utils/watch/stream-watch-loop.d.ts.map +1 -0
  138. package/dist/utils/watch/stream-watch-loop.js +282 -0
  139. package/dist/utils/watch/stream-watch-loop.js.map +1 -0
  140. package/dist/utils/watch/stream-watch-loop.test.d.ts +2 -0
  141. package/dist/utils/watch/stream-watch-loop.test.d.ts.map +1 -0
  142. package/dist/utils/watch/stream-watch-loop.test.js +392 -0
  143. package/dist/utils/watch/stream-watch-loop.test.js.map +1 -0
  144. package/dist/utils/watch/websocket-watch-stream.d.ts +25 -0
  145. package/dist/utils/watch/websocket-watch-stream.d.ts.map +1 -0
  146. package/dist/utils/watch/websocket-watch-stream.js +306 -0
  147. package/dist/utils/watch/websocket-watch-stream.js.map +1 -0
  148. package/dist/utils/watch/websocket-watch-stream.test.d.ts +2 -0
  149. package/dist/utils/watch/websocket-watch-stream.test.d.ts.map +1 -0
  150. package/dist/utils/watch/websocket-watch-stream.test.js +475 -0
  151. package/dist/utils/watch/websocket-watch-stream.test.js.map +1 -0
  152. package/package.json +7 -4
  153. package/src/args/global-args.ts +13 -0
  154. package/src/commands/batch-command/batch-deploy-command.ts +105 -1
  155. package/src/commands/batch-command/batch-test-command.ts +86 -60
  156. package/src/commands/deploy-command/deploy-init.ts +1 -1
  157. package/src/commands/deploy-command/index.ts +327 -32
  158. package/src/commands/deploy-command/select-target.ts +1 -1
  159. package/src/commands/deploy-command/version-command.ts +215 -21
  160. package/src/commands/test-command/test-command.ts +31 -22
  161. package/src/nevermore.ts +19 -0
  162. package/src/utils/batch/changed-packages-utils.ts +1 -1
  163. package/src/utils/build/base-place-resolver-factory.ts +49 -0
  164. package/src/utils/build/build.ts +13 -2
  165. package/src/utils/build/upload.ts +1 -1
  166. package/src/utils/deploy/deploy-metadata.ts +1 -1
  167. package/src/utils/job-context/base-job-context.ts +68 -6
  168. package/src/utils/job-context/batch-script-job-context.ts +18 -0
  169. package/src/utils/job-context/cloud-job-context.ts +7 -2
  170. package/src/utils/job-context/local-job-context.ts +7 -2
  171. package/src/utils/open-cloud/open-cloud-client.test.ts +133 -14
  172. package/src/utils/open-cloud/open-cloud-client.ts +131 -35
  173. package/src/utils/testing/parsers/batch-log-parser.test.ts +70 -5
  174. package/src/utils/testing/parsers/batch-log-parser.ts +141 -80
  175. package/src/utils/testing/parsers/traceback-parser.test.ts +64 -0
  176. package/src/utils/testing/parsers/traceback-parser.ts +122 -0
  177. package/src/utils/testing/runner/combined-project-generator.ts +1 -1
  178. package/src/utils/testing/runner/test-runner.test.ts +29 -0
  179. package/src/utils/testing/runner/test-runner.ts +36 -4
  180. package/src/utils/testing/test-log-parser.test.ts +70 -0
  181. package/src/utils/testing/test-log-parser.ts +80 -10
  182. package/src/utils/watch/github-context.test.ts +154 -0
  183. package/src/utils/watch/github-context.ts +184 -0
  184. package/src/utils/watch/http-watch-registry.test.ts +330 -0
  185. package/src/utils/watch/http-watch-registry.ts +341 -0
  186. package/src/utils/watch/local-watch-loop.test.ts +197 -0
  187. package/src/utils/watch/local-watch-loop.ts +190 -0
  188. package/src/utils/watch/register-notify-watches.test.ts +282 -0
  189. package/src/utils/watch/register-notify-watches.ts +226 -0
  190. package/src/utils/watch/register-watches.test.ts +696 -0
  191. package/src/utils/watch/register-watches.ts +369 -0
  192. package/src/utils/watch/stream-watch-loop.test.ts +625 -0
  193. package/src/utils/watch/stream-watch-loop.ts +411 -0
  194. package/src/utils/watch/websocket-watch-stream.test.ts +581 -0
  195. package/src/utils/watch/websocket-watch-stream.ts +403 -0
  196. package/tsconfig.json +19 -6
  197. package/tsconfig.tsbuildinfo +1 -1
  198. package/dist/utils/build/deploy-config.d.ts +0 -71
  199. package/dist/utils/build/deploy-config.d.ts.map +0 -1
  200. package/dist/utils/build/deploy-config.js +0 -164
  201. package/dist/utils/build/deploy-config.js.map +0 -1
  202. package/dist/utils/build/deploy-config.test.d.ts +0 -2
  203. package/dist/utils/build/deploy-config.test.d.ts.map +0 -1
  204. package/dist/utils/build/deploy-config.test.js +0 -126
  205. package/dist/utils/build/deploy-config.test.js.map +0 -1
  206. package/src/utils/build/deploy-config.test.ts +0 -149
  207. package/src/utils/build/deploy-config.ts +0 -259
@@ -0,0 +1,211 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { HttpWatchRegistry } from './http-watch-registry.js';
3
+ const REGISTER_URL = 'https://watch.example.com/v1/register/7d/';
4
+ function makeRequest(overrides = {}) {
5
+ return {
6
+ monitorName: 'integration',
7
+ repository: 'Quenty/egg-hunt-2026',
8
+ githubToken: 'token-value',
9
+ watches: [
10
+ {
11
+ name: 'quenty/hub/hub',
12
+ source: {
13
+ type: 'roblox-place',
14
+ universeId: 10,
15
+ placeId: 20,
16
+ versionType: 'published',
17
+ },
18
+ baselineVersion: '158',
19
+ baselineVersionKind: 'roblox-place-version',
20
+ action: {
21
+ type: 'github-workflow-dispatch',
22
+ workflow: '.github/workflows/build.yml',
23
+ ref: 'main',
24
+ inputs: { target: 'integration.places.hub' },
25
+ },
26
+ },
27
+ ],
28
+ ...overrides,
29
+ };
30
+ }
31
+ function makeResponse(body, init = {}) {
32
+ return {
33
+ ok: (init.status ?? 200) < 400,
34
+ status: init.status ?? 200,
35
+ statusText: init.statusText ?? 'OK',
36
+ text: async () => body,
37
+ };
38
+ }
39
+ function registryReturning(response, calls) {
40
+ return new HttpWatchRegistry({
41
+ registerUrl: REGISTER_URL,
42
+ fetchImpl: (async (url, init) => {
43
+ calls?.push({ url, init });
44
+ return response;
45
+ }),
46
+ });
47
+ }
48
+ describe('HttpWatchRegistry', () => {
49
+ it('posts a MonitorRequest to the register URL', async () => {
50
+ const calls = [];
51
+ await registryReturning(makeResponse('{}'), calls).registerAsync(makeRequest());
52
+ expect(calls).toHaveLength(1);
53
+ expect(calls[0].url).toBe(REGISTER_URL);
54
+ expect(calls[0].init.method).toBe('POST');
55
+ expect(JSON.parse(calls[0].init.body)).toEqual({
56
+ name: 'integration',
57
+ auth: {
58
+ github: {
59
+ token: 'token-value',
60
+ repository: 'Quenty/egg-hunt-2026',
61
+ },
62
+ },
63
+ watches: [
64
+ {
65
+ name: 'quenty/hub/hub',
66
+ source: {
67
+ type: 'roblox-place',
68
+ universeId: 10,
69
+ placeId: 20,
70
+ versionType: 'published',
71
+ },
72
+ baselineVersion: '158',
73
+ baselineVersionKind: 'roblox-place-version',
74
+ action: {
75
+ type: 'github-workflow-dispatch',
76
+ workflow: '.github/workflows/build.yml',
77
+ ref: 'main',
78
+ inputs: { target: 'integration.places.hub' },
79
+ },
80
+ },
81
+ ],
82
+ });
83
+ });
84
+ it('sends every watch in one request', async () => {
85
+ const calls = [];
86
+ const base = makeRequest();
87
+ await registryReturning(makeResponse('{}'), calls).registerAsync({
88
+ ...base,
89
+ watches: [base.watches[0], { ...base.watches[0], name: 'pkg/lobby' }],
90
+ });
91
+ expect(calls).toHaveLength(1);
92
+ expect(JSON.parse(calls[0].init.body).watches).toHaveLength(2);
93
+ });
94
+ it('omits the Open Cloud key unless one was shared', async () => {
95
+ const calls = [];
96
+ await registryReturning(makeResponse('{}'), calls).registerAsync(makeRequest());
97
+ expect(JSON.parse(calls[0].init.body).auth).not.toHaveProperty('robloxOpenCloud');
98
+ });
99
+ it('includes the Open Cloud key when shared', async () => {
100
+ const calls = [];
101
+ await registryReturning(makeResponse('{}'), calls).registerAsync(makeRequest({ robloxApiKey: 'oc-key' }));
102
+ expect(JSON.parse(calls[0].init.body).auth.robloxOpenCloud).toEqual({ apiKey: 'oc-key' });
103
+ });
104
+ // The kind describes the value, so sending it alone would declare the
105
+ // vocabulary of a baseline that is not there.
106
+ it('omits the declared kind along with the baseline', async () => {
107
+ const calls = [];
108
+ const base = makeRequest();
109
+ await registryReturning(makeResponse('{}'), calls).registerAsync({
110
+ ...base,
111
+ watches: [{ ...base.watches[0], baselineVersion: undefined }],
112
+ });
113
+ expect(JSON.parse(calls[0].init.body).watches[0]).not.toHaveProperty('baselineVersionKind');
114
+ });
115
+ it('omits baselineVersion when there is no baseline', async () => {
116
+ const calls = [];
117
+ const base = makeRequest();
118
+ await registryReturning(makeResponse('{}'), calls).registerAsync({
119
+ ...base,
120
+ watches: [{ ...base.watches[0], baselineVersion: undefined }],
121
+ });
122
+ expect(JSON.parse(calls[0].init.body).watches[0]).not.toHaveProperty('baselineVersion');
123
+ });
124
+ it('reads back the monitor the service reports', async () => {
125
+ const result = await registryReturning(makeResponse(JSON.stringify({
126
+ monitorId: 'mon_9f2a',
127
+ revision: 'abc123',
128
+ leaseExpiresAt: '2026-08-06T00:00:00Z',
129
+ changed: true,
130
+ watches: [{ name: 'a' }, { name: 'b' }],
131
+ unexpected: 'ignored',
132
+ }))).registerAsync(makeRequest());
133
+ expect(result).toEqual({
134
+ monitorId: 'mon_9f2a',
135
+ revision: 'abc123',
136
+ leaseExpiresAt: '2026-08-06T00:00:00Z',
137
+ changed: true,
138
+ watchCount: 2,
139
+ });
140
+ });
141
+ it('reports an unchanged re-apply', async () => {
142
+ const result = await registryReturning(makeResponse(JSON.stringify({ monitorId: 'mon_1', changed: false }))).registerAsync(makeRequest());
143
+ expect(result.changed).toBe(false);
144
+ });
145
+ it('treats a 2xx with an unusable body as registered without details', async () => {
146
+ expect(await registryReturning(makeResponse('not json')).registerAsync(makeRequest())).toEqual({});
147
+ });
148
+ describe('failure messages', () => {
149
+ it('leads with the service message and adds our remedy on 401', async () => {
150
+ await expect(registryReturning(makeResponse(JSON.stringify({ error: 'bad credentials' }), {
151
+ status: 401,
152
+ statusText: 'Unauthorized',
153
+ })).registerAsync(makeRequest())).rejects.toThrowError(/^bad credentials\nSet NEVERMORE_WATCH_TOKEN/);
154
+ });
155
+ it('falls back to our own wording when the service says nothing', async () => {
156
+ await expect(registryReturning(makeResponse('', { status: 401, statusText: 'Unauthorized' })).registerAsync(makeRequest())).rejects.toThrowError(/^GitHub rejected the dispatch token\./);
157
+ });
158
+ it('names the repository and required scope on 403', async () => {
159
+ await expect(registryReturning(makeResponse('', { status: 403, statusText: 'Forbidden' })).registerAsync(makeRequest())).rejects.toThrowError(/cannot write to Quenty\/egg-hunt-2026.*actions: write/s);
160
+ });
161
+ it('explains a quota failure on 409', async () => {
162
+ await expect(registryReturning(makeResponse('', { status: 409, statusText: 'Conflict' })).registerAsync(makeRequest())).rejects.toThrowError(/quota exceeded for Quenty\/egg-hunt-2026/);
163
+ });
164
+ it('lists the workflows it referenced on 422', async () => {
165
+ await expect(registryReturning(makeResponse('', { status: 422, statusText: 'Unprocessable Entity' })).registerAsync(makeRequest())).rejects.toThrowError(/workflow does not exist.*\.github\/workflows\/build\.yml/s);
166
+ });
167
+ // Observed against prod: a notify-only monitor was refused 422 because the
168
+ // Open Cloud key lacked a scope, and the reply appended "Referenced: ." plus
169
+ // advice about a workflow path — under a message that had nothing to do with
170
+ // workflows. 422 now covers several causes and the service names which.
171
+ it('adds no workflow advice when the monitor dispatches none', async () => {
172
+ const request = makeRequest();
173
+ request.watches = request.watches.map((watch) => ({
174
+ ...watch,
175
+ action: { type: 'notify', payload: { target: 'x' } },
176
+ }));
177
+ await expect(registryReturning(makeResponse(JSON.stringify({
178
+ error: 'The Open Cloud key is not allowed to read place 1 (403).',
179
+ }), { status: 422, statusText: 'Unprocessable Entity' })).registerAsync(request)).rejects.toThrowError(/not allowed to read place 1/);
180
+ await expect(registryReturning(makeResponse(JSON.stringify({ error: 'nope' }), {
181
+ status: 422,
182
+ statusText: 'Unprocessable Entity',
183
+ })).registerAsync(request)).rejects.not.toThrowError(/Referenced|workflow/);
184
+ });
185
+ it('surfaces the API error body', async () => {
186
+ await expect(registryReturning(makeResponse(JSON.stringify({ error: 'lease too long' }), {
187
+ status: 400,
188
+ statusText: 'Bad Request',
189
+ })).registerAsync(makeRequest())).rejects.toThrowError(/lease too long/);
190
+ });
191
+ it('includes the API validation detail list', async () => {
192
+ await expect(registryReturning(makeResponse(JSON.stringify({
193
+ error: "request/body must have required property 'name'",
194
+ errors: [{ path: '/body/name', errorCode: 'required' }],
195
+ }), { status: 400, statusText: 'Bad Request' })).registerAsync(makeRequest())).rejects.toThrowError(/\/body\/name/);
196
+ });
197
+ it('falls back to status text on an unmapped code', async () => {
198
+ await expect(registryReturning(makeResponse('', { status: 503, statusText: 'Service Unavailable' })).registerAsync(makeRequest())).rejects.toThrowError(/503 Service Unavailable/);
199
+ });
200
+ it('reports the endpoint when the request cannot be made', async () => {
201
+ const registry = new HttpWatchRegistry({
202
+ registerUrl: REGISTER_URL,
203
+ fetchImpl: (async () => {
204
+ throw new Error('ENOTFOUND');
205
+ }),
206
+ });
207
+ await expect(registry.registerAsync(makeRequest())).rejects.toThrowError(/could not reach https:\/\/watch\.example\.com/);
208
+ });
209
+ });
210
+ });
211
+ //# sourceMappingURL=http-watch-registry.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-watch-registry.test.js","sourceRoot":"","sources":["../../../src/utils/watch/http-watch-registry.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { type BasePlaceVersionKeyword, type PlaceVersionSource } from '@quenty/nevermore-deploy';
2
+ /** One base place being watched, and what to rebuild when it moves. */
3
+ export interface LocalWatchEntry {
4
+ /** Human label for output, e.g. `integration.places.hub`. */
5
+ label: string;
6
+ universeId: number;
7
+ placeId: number;
8
+ versionType: BasePlaceVersionKeyword;
9
+ /**
10
+ * Version this entry was last built from. Undefined means "adopt whatever
11
+ * the first poll sees", which avoids a rebuild on startup for a place that
12
+ * has never been locked.
13
+ */
14
+ baseline?: number;
15
+ }
16
+ export interface LocalWatchLoopOptions {
17
+ entries: LocalWatchEntry[];
18
+ source: PlaceVersionSource;
19
+ /** Stop watching after this long. */
20
+ durationMs: number;
21
+ pollIntervalMs?: number;
22
+ /** Rebuild and redeploy. Errors are reported and the loop continues. */
23
+ redeployAsync: (entry: LocalWatchEntry, version: number) => Promise<void>;
24
+ /** Lets a caller stop the loop; SIGINT is wired to one by default. */
25
+ signal?: AbortSignal;
26
+ /** Injected in tests. */
27
+ now?: () => number;
28
+ sleepAsync?: (ms: number, signal: AbortSignal) => Promise<void>;
29
+ }
30
+ export interface LocalWatchLoopResult {
31
+ /** Rebuilds that completed. */
32
+ redeploys: number;
33
+ /** Rebuilds that were attempted and threw. */
34
+ failures: number;
35
+ /** True when the duration ran out rather than the user interrupting. */
36
+ expired: boolean;
37
+ }
38
+ /**
39
+ * Poll base places and redeploy in-process when one moves.
40
+ *
41
+ * This is what `--watch` does outside CI. Registering with the watch service
42
+ * would be pointless on a laptop: the service's only action is dispatching a
43
+ * GitHub workflow, which would rebuild on a runner rather than here — so a
44
+ * local run keeps the loop in the terminal, where the developer can see it.
45
+ *
46
+ * Poll failures never end the loop. A watch is meant to be left running for
47
+ * hours, and a dropped connection is not a reason to stop watching.
48
+ */
49
+ export declare function runLocalWatchLoopAsync(options: LocalWatchLoopOptions): Promise<LocalWatchLoopResult>;
50
+ //# sourceMappingURL=local-watch-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-watch-loop.d.ts","sourceRoot":"","sources":["../../../src/utils/watch/local-watch-loop.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACxB,MAAM,0BAA0B,CAAC;AAKlC,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,uBAAuB,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,aAAa,EAAE,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,sEAAsE;IACtE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,yBAAyB;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACjE;AAED,MAAM,WAAW,oBAAoB;IACnC,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAgH/B"}
@@ -0,0 +1,103 @@
1
+ import { OutputHelper } from '@quenty/cli-output-helpers';
2
+ /** How often to ask Open Cloud whether a base place moved. */
3
+ const DEFAULT_POLL_INTERVAL_MS = 15_000;
4
+ /**
5
+ * Poll base places and redeploy in-process when one moves.
6
+ *
7
+ * This is what `--watch` does outside CI. Registering with the watch service
8
+ * would be pointless on a laptop: the service's only action is dispatching a
9
+ * GitHub workflow, which would rebuild on a runner rather than here — so a
10
+ * local run keeps the loop in the terminal, where the developer can see it.
11
+ *
12
+ * Poll failures never end the loop. A watch is meant to be left running for
13
+ * hours, and a dropped connection is not a reason to stop watching.
14
+ */
15
+ export async function runLocalWatchLoopAsync(options) {
16
+ const { entries, source, durationMs, redeployAsync, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, now = () => Date.now(), sleepAsync = _sleepAsync, } = options;
17
+ const controller = new AbortController();
18
+ const signal = options.signal ?? controller.signal;
19
+ const onSigint = () => controller.abort();
20
+ if (!options.signal) {
21
+ process.on('SIGINT', onSigint);
22
+ }
23
+ const seen = new Map(entries.map((e) => [_key(e), e.baseline]));
24
+ const deadline = now() + durationMs;
25
+ let redeploys = 0;
26
+ let failures = 0;
27
+ OutputHelper.info(`Watching ${entries.length} base place${entries.length === 1 ? '' : 's'} locally. Ctrl-C to stop.`);
28
+ for (const entry of entries) {
29
+ OutputHelper.info(` ${entry.label} — base place ${entry.placeId} (${entry.versionType}${entry.baseline == null ? '' : ` v${entry.baseline}`})`);
30
+ }
31
+ try {
32
+ while (!signal.aborted && now() < deadline) {
33
+ for (const entry of entries) {
34
+ if (signal.aborted) {
35
+ break;
36
+ }
37
+ let latest;
38
+ try {
39
+ latest = await source.resolveLatestPlaceVersionAsync(entry.universeId, entry.placeId, entry.versionType);
40
+ }
41
+ catch (err) {
42
+ OutputHelper.warn(`Could not check ${entry.label}: ` +
43
+ (err instanceof Error ? err.message : String(err)));
44
+ continue;
45
+ }
46
+ const key = _key(entry);
47
+ const previous = seen.get(key);
48
+ if (previous === undefined) {
49
+ // First sighting of a place with no recorded baseline: adopt it
50
+ // rather than treating "unknown -> something" as a change.
51
+ seen.set(key, latest);
52
+ continue;
53
+ }
54
+ if (latest === previous) {
55
+ continue;
56
+ }
57
+ OutputHelper.info(`${entry.label}: base place moved v${previous} → v${latest}, rebuilding...`);
58
+ try {
59
+ await redeployAsync(entry, latest);
60
+ redeploys++;
61
+ // Recorded only on success, so a failed rebuild is retried on the
62
+ // next poll instead of being silently skipped forever.
63
+ seen.set(key, latest);
64
+ OutputHelper.info(`${entry.label}: rebuilt from v${latest}.`);
65
+ }
66
+ catch (err) {
67
+ failures++;
68
+ OutputHelper.error(`${entry.label}: rebuild failed — ` +
69
+ (err instanceof Error ? err.message : String(err)));
70
+ }
71
+ }
72
+ if (signal.aborted || now() >= deadline) {
73
+ break;
74
+ }
75
+ await sleepAsync(pollIntervalMs, signal);
76
+ }
77
+ }
78
+ finally {
79
+ if (!options.signal) {
80
+ process.off('SIGINT', onSigint);
81
+ }
82
+ }
83
+ const expired = !signal.aborted;
84
+ OutputHelper.info(expired
85
+ ? `Watch lease elapsed. ${redeploys} rebuild${redeploys === 1 ? '' : 's'}.`
86
+ : `Stopped watching. ${redeploys} rebuild${redeploys === 1 ? '' : 's'}.`);
87
+ return { redeploys, failures, expired };
88
+ }
89
+ function _key(entry) {
90
+ return `${entry.placeId}:${entry.versionType}`;
91
+ }
92
+ function _sleepAsync(ms, signal) {
93
+ return new Promise((resolve) => {
94
+ const timer = setTimeout(finish, ms);
95
+ function finish() {
96
+ clearTimeout(timer);
97
+ signal.removeEventListener('abort', finish);
98
+ resolve();
99
+ }
100
+ signal.addEventListener('abort', finish, { once: true });
101
+ });
102
+ }
103
+ //# sourceMappingURL=local-watch-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-watch-loop.js","sourceRoot":"","sources":["../../../src/utils/watch/local-watch-loop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAM1D,8DAA8D;AAC9D,MAAM,wBAAwB,GAAG,MAAM,CAAC;AAyCxC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAA8B;IAE9B,MAAM,EACJ,OAAO,EACP,MAAM,EACN,UAAU,EACV,aAAa,EACb,cAAc,GAAG,wBAAwB,EACzC,GAAG,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,EACtB,UAAU,GAAG,WAAW,GACzB,GAAG,OAAO,CAAC;IAEZ,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,UAAU,CAAC,MAAM,CAAC;IACnD,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC1C,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,GAAG,CAClB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAC1C,CAAC;IACF,MAAM,QAAQ,GAAG,GAAG,EAAE,GAAG,UAAU,CAAC;IACpC,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,YAAY,CAAC,IAAI,CACf,YAAY,OAAO,CAAC,MAAM,cACxB,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAC9B,2BAA2B,CAC5B,CAAC;IACF,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,YAAY,CAAC,IAAI,CACf,KAAK,KAAK,CAAC,KAAK,iBAAiB,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,WAAW,GAClE,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,QAAQ,EACnD,GAAG,CACJ,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;YAC3C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnB,MAAM;gBACR,CAAC;gBAED,IAAI,MAAc,CAAC;gBACnB,IAAI,CAAC;oBACH,MAAM,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAClD,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,WAAW,CAClB,CAAC;gBACJ,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,YAAY,CAAC,IAAI,CACf,mBAAmB,KAAK,CAAC,KAAK,IAAI;wBAChC,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CACrD,CAAC;oBACF,SAAS;gBACX,CAAC;gBAED,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;gBACxB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAC/B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;oBAC3B,gEAAgE;oBAChE,2DAA2D;oBAC3D,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;oBACtB,SAAS;gBACX,CAAC;gBACD,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;oBACxB,SAAS;gBACX,CAAC;gBAED,YAAY,CAAC,IAAI,CACf,GAAG,KAAK,CAAC,KAAK,uBAAuB,QAAQ,OAAO,MAAM,iBAAiB,CAC5E,CAAC;gBACF,IAAI,CAAC;oBACH,MAAM,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;oBACnC,SAAS,EAAE,CAAC;oBACZ,kEAAkE;oBAClE,uDAAuD;oBACvD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;oBACtB,YAAY,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,mBAAmB,MAAM,GAAG,CAAC,CAAC;gBAChE,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,QAAQ,EAAE,CAAC;oBACX,YAAY,CAAC,KAAK,CAChB,GAAG,KAAK,CAAC,KAAK,qBAAqB;wBACjC,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CACrD,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,IAAI,MAAM,CAAC,OAAO,IAAI,GAAG,EAAE,IAAI,QAAQ,EAAE,CAAC;gBACxC,MAAM;YACR,CAAC;YACD,MAAM,UAAU,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;YAAS,CAAC;QACT,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC;IAChC,YAAY,CAAC,IAAI,CACf,OAAO;QACL,CAAC,CAAC,wBAAwB,SAAS,WAC/B,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GACzB,GAAG;QACL,CAAC,CAAC,qBAAqB,SAAS,WAAW,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAC3E,CAAC;IAEF,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC1C,CAAC;AAED,SAAS,IAAI,CAAC,KAAsB;IAClC,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;AACjD,CAAC;AAED,SAAS,WAAW,CAAC,EAAU,EAAE,MAAmB;IAClD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACrC,SAAS,MAAM;YACb,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC5C,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=local-watch-loop.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-watch-loop.test.d.ts","sourceRoot":"","sources":["../../../src/utils/watch/local-watch-loop.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,170 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { runLocalWatchLoopAsync, } from './local-watch-loop.js';
3
+ function makeEntry(overrides = {}) {
4
+ return {
5
+ label: 'integration.places.hub',
6
+ universeId: 10,
7
+ placeId: 20,
8
+ versionType: 'published',
9
+ baseline: 158,
10
+ ...overrides,
11
+ };
12
+ }
13
+ /**
14
+ * Drives the loop deterministically: time only advances when the loop sleeps,
15
+ * so a test controls exactly how many poll rounds happen.
16
+ */
17
+ function makeClock(pollIntervalMs) {
18
+ let current = 0;
19
+ return {
20
+ now: () => current,
21
+ sleepAsync: async () => {
22
+ current += pollIntervalMs;
23
+ },
24
+ };
25
+ }
26
+ describe('runLocalWatchLoopAsync', () => {
27
+ it('redeploys when the base place moves', async () => {
28
+ const versions = [158, 158, 159];
29
+ let i = 0;
30
+ const redeployed = [];
31
+ const clock = makeClock(1000);
32
+ const result = await runLocalWatchLoopAsync({
33
+ entries: [makeEntry()],
34
+ source: {
35
+ resolveLatestPlaceVersionAsync: async () => versions[Math.min(i++, versions.length - 1)],
36
+ },
37
+ durationMs: 3000,
38
+ pollIntervalMs: 1000,
39
+ redeployAsync: async (_entry, version) => {
40
+ redeployed.push(version);
41
+ },
42
+ ...clock,
43
+ });
44
+ expect(redeployed).toEqual([159]);
45
+ expect(result.redeploys).toBe(1);
46
+ expect(result.failures).toBe(0);
47
+ expect(result.expired).toBe(true);
48
+ });
49
+ it('does not redeploy while the version holds still', async () => {
50
+ const redeployed = [];
51
+ const clock = makeClock(1000);
52
+ const result = await runLocalWatchLoopAsync({
53
+ entries: [makeEntry()],
54
+ source: { resolveLatestPlaceVersionAsync: async () => 158 },
55
+ durationMs: 5000,
56
+ pollIntervalMs: 1000,
57
+ redeployAsync: async (_e, v) => {
58
+ redeployed.push(v);
59
+ },
60
+ ...clock,
61
+ });
62
+ expect(redeployed).toEqual([]);
63
+ expect(result.redeploys).toBe(0);
64
+ });
65
+ // An entry with no lock entry has no baseline. Treating "unknown -> 158" as a
66
+ // change would rebuild immediately on startup for no reason.
67
+ it('adopts the first observed version when there is no baseline', async () => {
68
+ const redeployed = [];
69
+ const clock = makeClock(1000);
70
+ await runLocalWatchLoopAsync({
71
+ entries: [makeEntry({ baseline: undefined })],
72
+ source: { resolveLatestPlaceVersionAsync: async () => 158 },
73
+ durationMs: 3000,
74
+ pollIntervalMs: 1000,
75
+ redeployAsync: async (_e, v) => {
76
+ redeployed.push(v);
77
+ },
78
+ ...clock,
79
+ });
80
+ expect(redeployed).toEqual([]);
81
+ });
82
+ it('keeps watching after a poll failure', async () => {
83
+ let call = 0;
84
+ const redeployed = [];
85
+ const clock = makeClock(1000);
86
+ const result = await runLocalWatchLoopAsync({
87
+ entries: [makeEntry()],
88
+ source: {
89
+ resolveLatestPlaceVersionAsync: async () => {
90
+ call++;
91
+ if (call === 1) {
92
+ throw new Error('ECONNRESET');
93
+ }
94
+ return 159;
95
+ },
96
+ },
97
+ durationMs: 3000,
98
+ pollIntervalMs: 1000,
99
+ redeployAsync: async (_e, v) => {
100
+ redeployed.push(v);
101
+ },
102
+ ...clock,
103
+ });
104
+ expect(redeployed).toEqual([159]);
105
+ expect(result.redeploys).toBe(1);
106
+ });
107
+ // The new version is recorded only on a successful rebuild, so a transient
108
+ // build failure is retried rather than silently skipped forever.
109
+ it('retries a version whose rebuild failed', async () => {
110
+ let attempts = 0;
111
+ const clock = makeClock(1000);
112
+ const result = await runLocalWatchLoopAsync({
113
+ entries: [makeEntry()],
114
+ source: { resolveLatestPlaceVersionAsync: async () => 159 },
115
+ durationMs: 3000,
116
+ pollIntervalMs: 1000,
117
+ redeployAsync: async () => {
118
+ attempts++;
119
+ if (attempts === 1) {
120
+ throw new Error('rojo exploded');
121
+ }
122
+ },
123
+ ...clock,
124
+ });
125
+ expect(attempts).toBeGreaterThan(1);
126
+ expect(result.failures).toBe(1);
127
+ expect(result.redeploys).toBe(1);
128
+ });
129
+ it('stops when the caller aborts', async () => {
130
+ const controller = new AbortController();
131
+ const clock = makeClock(1000);
132
+ const poll = vi.fn(async () => {
133
+ controller.abort();
134
+ return 158;
135
+ });
136
+ const result = await runLocalWatchLoopAsync({
137
+ entries: [makeEntry()],
138
+ source: { resolveLatestPlaceVersionAsync: poll },
139
+ durationMs: 60_000,
140
+ pollIntervalMs: 1000,
141
+ redeployAsync: async () => { },
142
+ signal: controller.signal,
143
+ ...clock,
144
+ });
145
+ expect(poll).toHaveBeenCalledTimes(1);
146
+ expect(result.expired).toBe(false);
147
+ });
148
+ it('watches every entry independently', async () => {
149
+ const seen = [];
150
+ const clock = makeClock(1000);
151
+ await runLocalWatchLoopAsync({
152
+ entries: [
153
+ makeEntry({ label: 'hub', placeId: 20, baseline: 1 }),
154
+ makeEntry({ label: 'lobby', placeId: 21, baseline: 1 }),
155
+ ],
156
+ source: {
157
+ resolveLatestPlaceVersionAsync: async (_u, placeId) => placeId === 21 ? 2 : 1,
158
+ },
159
+ durationMs: 2000,
160
+ pollIntervalMs: 1000,
161
+ redeployAsync: async (entry) => {
162
+ seen.push(entry.label);
163
+ },
164
+ ...clock,
165
+ });
166
+ expect(seen).toContain('lobby');
167
+ expect(seen).not.toContain('hub');
168
+ });
169
+ });
170
+ //# sourceMappingURL=local-watch-loop.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-watch-loop.test.js","sourceRoot":"","sources":["../../../src/utils/watch/local-watch-loop.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { type WatchOption, type WatchRegistry } from '@quenty/nevermore-deploy';
2
+ import { type LocalWatchEntry } from './local-watch-loop.js';
3
+ import { type StreamWatchEntry } from './stream-watch-loop.js';
4
+ export interface RegisterNotifyWatchesOptions {
5
+ option: WatchOption;
6
+ /** Lease key within the repository, as for a dispatching monitor. */
7
+ monitorName: string;
8
+ entries: LocalWatchEntry[];
9
+ /** Open Cloud key to share, so the service can poll a private base place. */
10
+ robloxApiKey?: string;
11
+ /** Let the GitHub CLI supply the token when no env var does. */
12
+ useGhAuth?: boolean;
13
+ /** Injected in tests; defaults to the HTTP registry. */
14
+ createRegistry?: (registerUrl: string) => WatchRegistry;
15
+ }
16
+ export type RegisterNotifyWatchesResult = {
17
+ success: true;
18
+ monitorId: string;
19
+ /** Bearer token for the stream; the same one registration used. */
20
+ credential: string;
21
+ entries: StreamWatchEntry[];
22
+ } | {
23
+ success: false;
24
+ reason: 'no_github_context' | 'no_token' | 'unwatchable_version_type' | 'no_entries' | 'register_failed' | 'no_monitor_id';
25
+ detail?: string;
26
+ };
27
+ /**
28
+ * Register a monitor that notifies this process instead of dispatching CI.
29
+ *
30
+ * Best-effort on purpose. Every way this can fail — no GitHub identity to
31
+ * register under, no token, a `"saved"` base place the service cannot poll, a
32
+ * service that is unreachable — has a working answer already: poll Open Cloud
33
+ * from here. So a failure downgrades the watch rather than the run, and the
34
+ * caller falls back.
35
+ */
36
+ export declare function tryRegisterNotifyWatchesAsync(options: RegisterNotifyWatchesOptions): Promise<RegisterNotifyWatchesResult>;
37
+ /** Why the run is polling instead, in words a user can act on. */
38
+ export declare function describeNotifyWatchFallback(result: RegisterNotifyWatchesResult & {
39
+ success: false;
40
+ }): string;
41
+ //# sourceMappingURL=register-notify-watches.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-notify-watches.d.ts","sourceRoot":"","sources":["../../../src/utils/watch/register-notify-watches.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,aAAa,EACnB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,WAAW,CAAC;IACpB,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wDAAwD;IACxD,cAAc,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,aAAa,CAAC;CACzD;AAED,MAAM,MAAM,2BAA2B,GACnC;IACE,OAAO,EAAE,IAAI,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B,GACD;IACE,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EACF,mBAAmB,GACnB,UAAU,GACV,0BAA0B,GAC1B,YAAY,GACZ,iBAAiB,GACjB,eAAe,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEN;;;;;;;;GAQG;AACH,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,2BAA2B,CAAC,CA2GtC;AAED,kEAAkE;AAClE,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,2BAA2B,GAAG;IAAE,OAAO,EAAE,KAAK,CAAA;CAAE,GACvD,MAAM,CAqBR"}