@quenty/nevermore-cli 4.42.0 → 4.42.1-canary.783.30683606415.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 (172) hide show
  1. package/CHANGELOG.md +22 -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 +7 -2
  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 +301 -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 +17 -7
  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 +74 -20
  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/runner/combined-project-generator.d.ts +1 -1
  65. package/dist/utils/testing/runner/combined-project-generator.d.ts.map +1 -1
  66. package/dist/utils/testing/runner/test-runner.d.ts +1 -1
  67. package/dist/utils/testing/runner/test-runner.d.ts.map +1 -1
  68. package/dist/utils/watch/github-context.d.ts +50 -0
  69. package/dist/utils/watch/github-context.d.ts.map +1 -0
  70. package/dist/utils/watch/github-context.js +125 -0
  71. package/dist/utils/watch/github-context.js.map +1 -0
  72. package/dist/utils/watch/github-context.test.d.ts +2 -0
  73. package/dist/utils/watch/github-context.test.d.ts.map +1 -0
  74. package/dist/utils/watch/github-context.test.js +112 -0
  75. package/dist/utils/watch/github-context.test.js.map +1 -0
  76. package/dist/utils/watch/http-watch-registry.d.ts +40 -0
  77. package/dist/utils/watch/http-watch-registry.d.ts.map +1 -0
  78. package/dist/utils/watch/http-watch-registry.js +262 -0
  79. package/dist/utils/watch/http-watch-registry.js.map +1 -0
  80. package/dist/utils/watch/http-watch-registry.test.d.ts +2 -0
  81. package/dist/utils/watch/http-watch-registry.test.d.ts.map +1 -0
  82. package/dist/utils/watch/http-watch-registry.test.js +211 -0
  83. package/dist/utils/watch/http-watch-registry.test.js.map +1 -0
  84. package/dist/utils/watch/local-watch-loop.d.ts +50 -0
  85. package/dist/utils/watch/local-watch-loop.d.ts.map +1 -0
  86. package/dist/utils/watch/local-watch-loop.js +103 -0
  87. package/dist/utils/watch/local-watch-loop.js.map +1 -0
  88. package/dist/utils/watch/local-watch-loop.test.d.ts +2 -0
  89. package/dist/utils/watch/local-watch-loop.test.d.ts.map +1 -0
  90. package/dist/utils/watch/local-watch-loop.test.js +170 -0
  91. package/dist/utils/watch/local-watch-loop.test.js.map +1 -0
  92. package/dist/utils/watch/register-notify-watches.d.ts +41 -0
  93. package/dist/utils/watch/register-notify-watches.d.ts.map +1 -0
  94. package/dist/utils/watch/register-notify-watches.js +159 -0
  95. package/dist/utils/watch/register-notify-watches.js.map +1 -0
  96. package/dist/utils/watch/register-notify-watches.test.d.ts +2 -0
  97. package/dist/utils/watch/register-notify-watches.test.d.ts.map +1 -0
  98. package/dist/utils/watch/register-notify-watches.test.js +225 -0
  99. package/dist/utils/watch/register-notify-watches.test.js.map +1 -0
  100. package/dist/utils/watch/register-watches.d.ts +68 -0
  101. package/dist/utils/watch/register-watches.d.ts.map +1 -0
  102. package/dist/utils/watch/register-watches.js +221 -0
  103. package/dist/utils/watch/register-watches.js.map +1 -0
  104. package/dist/utils/watch/register-watches.test.d.ts +2 -0
  105. package/dist/utils/watch/register-watches.test.d.ts.map +1 -0
  106. package/dist/utils/watch/register-watches.test.js +576 -0
  107. package/dist/utils/watch/register-watches.test.js.map +1 -0
  108. package/dist/utils/watch/stream-watch-loop.d.ts +54 -0
  109. package/dist/utils/watch/stream-watch-loop.d.ts.map +1 -0
  110. package/dist/utils/watch/stream-watch-loop.js +282 -0
  111. package/dist/utils/watch/stream-watch-loop.js.map +1 -0
  112. package/dist/utils/watch/stream-watch-loop.test.d.ts +2 -0
  113. package/dist/utils/watch/stream-watch-loop.test.d.ts.map +1 -0
  114. package/dist/utils/watch/stream-watch-loop.test.js +392 -0
  115. package/dist/utils/watch/stream-watch-loop.test.js.map +1 -0
  116. package/dist/utils/watch/websocket-watch-stream.d.ts +25 -0
  117. package/dist/utils/watch/websocket-watch-stream.d.ts.map +1 -0
  118. package/dist/utils/watch/websocket-watch-stream.js +306 -0
  119. package/dist/utils/watch/websocket-watch-stream.js.map +1 -0
  120. package/dist/utils/watch/websocket-watch-stream.test.d.ts +2 -0
  121. package/dist/utils/watch/websocket-watch-stream.test.d.ts.map +1 -0
  122. package/dist/utils/watch/websocket-watch-stream.test.js +475 -0
  123. package/dist/utils/watch/websocket-watch-stream.test.js.map +1 -0
  124. package/package.json +7 -4
  125. package/src/args/global-args.ts +13 -0
  126. package/src/commands/batch-command/batch-deploy-command.ts +105 -1
  127. package/src/commands/batch-command/batch-test-command.ts +8 -2
  128. package/src/commands/deploy-command/deploy-init.ts +1 -1
  129. package/src/commands/deploy-command/index.ts +331 -32
  130. package/src/commands/deploy-command/select-target.ts +1 -1
  131. package/src/commands/deploy-command/version-command.ts +215 -21
  132. package/src/commands/test-command/test-command.ts +24 -18
  133. package/src/nevermore.ts +19 -0
  134. package/src/utils/batch/changed-packages-utils.ts +1 -1
  135. package/src/utils/build/base-place-resolver-factory.ts +49 -0
  136. package/src/utils/build/build.ts +13 -2
  137. package/src/utils/build/upload.ts +1 -1
  138. package/src/utils/deploy/deploy-metadata.ts +1 -1
  139. package/src/utils/job-context/base-job-context.ts +68 -6
  140. package/src/utils/job-context/batch-script-job-context.ts +18 -0
  141. package/src/utils/job-context/cloud-job-context.ts +7 -2
  142. package/src/utils/job-context/local-job-context.ts +7 -2
  143. package/src/utils/open-cloud/open-cloud-client.test.ts +133 -14
  144. package/src/utils/open-cloud/open-cloud-client.ts +127 -34
  145. package/src/utils/testing/runner/combined-project-generator.ts +1 -1
  146. package/src/utils/testing/runner/test-runner.ts +1 -1
  147. package/src/utils/watch/github-context.test.ts +154 -0
  148. package/src/utils/watch/github-context.ts +184 -0
  149. package/src/utils/watch/http-watch-registry.test.ts +330 -0
  150. package/src/utils/watch/http-watch-registry.ts +341 -0
  151. package/src/utils/watch/local-watch-loop.test.ts +197 -0
  152. package/src/utils/watch/local-watch-loop.ts +190 -0
  153. package/src/utils/watch/register-notify-watches.test.ts +282 -0
  154. package/src/utils/watch/register-notify-watches.ts +226 -0
  155. package/src/utils/watch/register-watches.test.ts +696 -0
  156. package/src/utils/watch/register-watches.ts +369 -0
  157. package/src/utils/watch/stream-watch-loop.test.ts +625 -0
  158. package/src/utils/watch/stream-watch-loop.ts +411 -0
  159. package/src/utils/watch/websocket-watch-stream.test.ts +581 -0
  160. package/src/utils/watch/websocket-watch-stream.ts +403 -0
  161. package/tsconfig.json +19 -6
  162. package/tsconfig.tsbuildinfo +1 -1
  163. package/dist/utils/build/deploy-config.d.ts +0 -71
  164. package/dist/utils/build/deploy-config.d.ts.map +0 -1
  165. package/dist/utils/build/deploy-config.js +0 -164
  166. package/dist/utils/build/deploy-config.js.map +0 -1
  167. package/dist/utils/build/deploy-config.test.d.ts +0 -2
  168. package/dist/utils/build/deploy-config.test.d.ts.map +0 -1
  169. package/dist/utils/build/deploy-config.test.js +0 -126
  170. package/dist/utils/build/deploy-config.test.js.map +0 -1
  171. package/src/utils/build/deploy-config.test.ts +0 -149
  172. package/src/utils/build/deploy-config.ts +0 -259
@@ -0,0 +1,262 @@
1
+ const DEFAULT_TIMEOUT_MS = 30_000;
2
+ /**
3
+ * `WatchRegistry` over the Roblox Analytics Watch API.
4
+ *
5
+ * One `POST /v1/register/{lease}` per invocation. The endpoint is whatever URL
6
+ * `--watch` resolved to, so nothing here assumes a host, and the lease is
7
+ * already the last path segment of that URL.
8
+ *
9
+ * The endpoint takes no `Authorization` header — the GitHub token travels in
10
+ * `auth.github.token`, because the service holds it to dispatch with later
11
+ * rather than using it to authenticate this call.
12
+ */
13
+ export class HttpWatchRegistry {
14
+ _registerUrl;
15
+ _timeoutMs;
16
+ _fetch;
17
+ constructor(options) {
18
+ this._registerUrl = options.registerUrl;
19
+ this._timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
20
+ this._fetch = options.fetchImpl ?? fetch;
21
+ }
22
+ async registerAsync(request) {
23
+ const body = {
24
+ name: request.monitorName,
25
+ auth: {
26
+ github: {
27
+ token: request.githubToken,
28
+ repository: request.repository,
29
+ },
30
+ ...(request.robloxApiKey
31
+ ? { robloxOpenCloud: { apiKey: request.robloxApiKey } }
32
+ : {}),
33
+ },
34
+ watches: request.watches.map((watch) => ({
35
+ name: watch.name,
36
+ source: {
37
+ type: watch.source.type,
38
+ universeId: watch.source.universeId,
39
+ placeId: watch.source.placeId,
40
+ versionType: watch.source.versionType,
41
+ },
42
+ // The kind travels with the value or not at all. Without it the service
43
+ // ignores a baseline outright on the one transition it matters for —
44
+ // the first registration that supplies an Open Cloud key — and the 422
45
+ // that should catch a vocabulary mismatch can never fire.
46
+ ...(watch.baselineVersion == null
47
+ ? {}
48
+ : {
49
+ baselineVersion: watch.baselineVersion,
50
+ ...(watch.baselineVersionKind == null
51
+ ? {}
52
+ : { baselineVersionKind: watch.baselineVersionKind }),
53
+ }),
54
+ action: watch.action,
55
+ })),
56
+ ...(request.cooldownSeconds == null
57
+ ? {}
58
+ : { cooldownSeconds: request.cooldownSeconds }),
59
+ };
60
+ const controller = new AbortController();
61
+ const timeout = setTimeout(() => controller.abort(), this._timeoutMs);
62
+ let response;
63
+ try {
64
+ response = await this._fetch(this._registerUrl, {
65
+ method: 'POST',
66
+ headers: { 'Content-Type': 'application/json' },
67
+ body: JSON.stringify(body),
68
+ signal: controller.signal,
69
+ });
70
+ }
71
+ catch (err) {
72
+ if (controller.signal.aborted) {
73
+ throw new Error(`Watch registration timed out after ${this._timeoutMs}ms: ${this._registerUrl}`);
74
+ }
75
+ throw new Error(`Watch registration could not reach ${this._registerUrl}: ` +
76
+ (err instanceof Error ? err.message : String(err)));
77
+ }
78
+ finally {
79
+ clearTimeout(timeout);
80
+ }
81
+ const text = await _readTextAsync(response);
82
+ if (!response.ok) {
83
+ throw new Error(_describeFailure(response, text, request));
84
+ }
85
+ return _parseResult(text);
86
+ }
87
+ async triggerAsync(monitorId, credential, options = {}) {
88
+ const url = `${this._apiBase()}/v1/monitors/${encodeURIComponent(monitorId)}/trigger`;
89
+ const response = await this._fetchWithTimeoutAsync(url, {
90
+ method: 'POST',
91
+ headers: {
92
+ 'Content-Type': 'application/json',
93
+ // Unlike register, this endpoint authenticates the caller — the token
94
+ // is the identity, so it travels as a bearer rather than in the body.
95
+ Authorization: `Bearer ${credential}`,
96
+ },
97
+ body: JSON.stringify(options.watchName == null ? {} : { watchName: options.watchName }),
98
+ });
99
+ const text = await _readTextAsync(response);
100
+ if (!response.ok) {
101
+ const detail = _extractError(text);
102
+ throw new Error(`Forcing a dispatch failed (${response.status} ${response.statusText})` +
103
+ (detail ? `: ${detail}` : ''));
104
+ }
105
+ try {
106
+ const parsed = JSON.parse(text);
107
+ return { results: Array.isArray(parsed.results) ? parsed.results : [] };
108
+ }
109
+ catch {
110
+ return { results: [] };
111
+ }
112
+ }
113
+ /**
114
+ * The API root, recovered from the register URL.
115
+ *
116
+ * `--watch` names the register endpoint because that is the one a user has to
117
+ * type; every other call is derived from it. The lease-bearing
118
+ * `/v1/register/<lease>/` suffix is the documented shape, so trimming it is
119
+ * how a single flag reaches the rest of the API.
120
+ */
121
+ _apiBase() {
122
+ const marker = '/v1/register/';
123
+ const index = this._registerUrl.indexOf(marker);
124
+ if (index === -1) {
125
+ throw new Error(`Cannot derive the watch API root from "${this._registerUrl}" — ` +
126
+ `expected it to contain "${marker}".`);
127
+ }
128
+ return this._registerUrl.slice(0, index);
129
+ }
130
+ async _fetchWithTimeoutAsync(url, init) {
131
+ const controller = new AbortController();
132
+ const timeout = setTimeout(() => controller.abort(), this._timeoutMs);
133
+ try {
134
+ return await this._fetch(url, { ...init, signal: controller.signal });
135
+ }
136
+ catch (err) {
137
+ if (controller.signal.aborted) {
138
+ throw new Error(`Watch request timed out after ${this._timeoutMs}ms: ${url}`);
139
+ }
140
+ throw new Error(`Watch request could not reach ${url}: ` +
141
+ (err instanceof Error ? err.message : String(err)));
142
+ }
143
+ finally {
144
+ clearTimeout(timeout);
145
+ }
146
+ }
147
+ }
148
+ /**
149
+ * Turn a status code into something actionable. The API documents exactly what
150
+ * each failure means, and every one of them has a different fix — a generic
151
+ * "registration failed (403)" would send someone to the wrong place.
152
+ *
153
+ * The service's own message wins when it has one, since it knows specifics we
154
+ * can only guess at; ours is the fallback for a bare status. Only the remedy is
155
+ * always ours, and it is what the service never supplies.
156
+ */
157
+ function _describeFailure(response, text, request) {
158
+ const detail = _extractError(text);
159
+ const compose = (fallback, remedy) => {
160
+ const head = detail || fallback;
161
+ return remedy ? `${head}\n${remedy}` : head;
162
+ };
163
+ switch (response.status) {
164
+ case 400:
165
+ return compose('The watch service rejected the registration as invalid.', 'This is a bug in how the CLI built the request — please report it.');
166
+ case 401:
167
+ return compose('GitHub rejected the dispatch token.', 'Set NEVERMORE_WATCH_TOKEN to a valid, unexpired token.');
168
+ case 403:
169
+ return compose(`The dispatch token cannot write to ${request.repository}.`, `The token needs "actions: write" on ${request.repository}.`);
170
+ case 409:
171
+ return compose(`Watch quota exceeded for ${request.repository}.`, 'Release monitors you no longer need, or let their leases expire.');
172
+ case 422: {
173
+ // 422 is the service's "your config cannot work" answer, and it covers
174
+ // several unrelated causes now — a missing workflow, an Open Cloud key
175
+ // without universe.place:read, a "saved" watch with no key, a baseline in
176
+ // the wrong vocabulary. Its own message names which, so the only thing
177
+ // worth adding is a remedy that could not already be in it.
178
+ const workflows = [
179
+ ...new Set(request.watches.flatMap((w) => w.action.type === 'github-workflow-dispatch'
180
+ ? [w.action.workflow]
181
+ : [])),
182
+ ];
183
+ // Volunteering a workflow remedy for a monitor that dispatches none once
184
+ // produced "Referenced: ." under a message about an API key scope.
185
+ if (workflows.length === 0) {
186
+ return compose('The watch service rejected the configuration.');
187
+ }
188
+ return compose(`A referenced workflow does not exist in ${request.repository}.`, `Workflows referenced: ${workflows.join(', ')}. A "watch" path in ` +
189
+ 'deploy.nevermore.json must name a workflow that exists on the ' +
190
+ 'dispatched ref.');
191
+ }
192
+ default:
193
+ return compose(`Watch registration failed (${response.status} ${response.statusText}).`);
194
+ }
195
+ }
196
+ /** Pull the API's `{error, errors}` shape out, falling back to raw text. */
197
+ function _extractError(text) {
198
+ if (text === '') {
199
+ return '';
200
+ }
201
+ try {
202
+ const parsed = JSON.parse(text);
203
+ const parts = [];
204
+ if (typeof parsed.error === 'string') {
205
+ parts.push(parsed.error);
206
+ }
207
+ if (Array.isArray(parsed.errors) && parsed.errors.length > 0) {
208
+ parts.push(JSON.stringify(parsed.errors));
209
+ }
210
+ return parts.length > 0 ? parts.join(' ') : text;
211
+ }
212
+ catch {
213
+ return text;
214
+ }
215
+ }
216
+ async function _readTextAsync(response) {
217
+ try {
218
+ return (await response.text()).trim();
219
+ }
220
+ catch {
221
+ return '';
222
+ }
223
+ }
224
+ /**
225
+ * Read back the fields worth reporting. A 2xx whose body we cannot parse still
226
+ * counts as registered — the service accepted it, and failing here would claim
227
+ * otherwise.
228
+ */
229
+ function _parseResult(text) {
230
+ if (text === '') {
231
+ return {};
232
+ }
233
+ let parsed;
234
+ try {
235
+ parsed = JSON.parse(text);
236
+ }
237
+ catch {
238
+ return {};
239
+ }
240
+ if (typeof parsed !== 'object' || parsed === null) {
241
+ return {};
242
+ }
243
+ const record = parsed;
244
+ const result = {};
245
+ if (typeof record['monitorId'] === 'string') {
246
+ result.monitorId = record['monitorId'];
247
+ }
248
+ if (typeof record['revision'] === 'string') {
249
+ result.revision = record['revision'];
250
+ }
251
+ if (typeof record['leaseExpiresAt'] === 'string') {
252
+ result.leaseExpiresAt = record['leaseExpiresAt'];
253
+ }
254
+ if (typeof record['changed'] === 'boolean') {
255
+ result.changed = record['changed'];
256
+ }
257
+ if (Array.isArray(record['watches'])) {
258
+ result.watchCount = record['watches'].length;
259
+ }
260
+ return result;
261
+ }
262
+ //# sourceMappingURL=http-watch-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-watch-registry.js","sourceRoot":"","sources":["../../../src/utils/watch/http-watch-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=http-watch-registry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-watch-registry.test.d.ts","sourceRoot":"","sources":["../../../src/utils/watch/http-watch-registry.test.ts"],"names":[],"mappings":""}
@@ -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":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,MAAM,YAAY,GAAG,2CAA2C,CAAC;AAEjE,SAAS,WAAW,CAClB,YAA+C,EAAE;IAEjD,OAAO;QACL,WAAW,EAAE,aAAa;QAC1B,UAAU,EAAE,sBAAsB;QAClC,WAAW,EAAE,aAAa;QAC1B,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,gBAAgB;gBACtB,MAAM,EAAE;oBACN,IAAI,EAAE,cAAc;oBACpB,UAAU,EAAE,EAAE;oBACd,OAAO,EAAE,EAAE;oBACX,WAAW,EAAE,WAAW;iBACzB;gBACD,eAAe,EAAE,KAAK;gBACtB,mBAAmB,EAAE,sBAAsB;gBAC3C,MAAM,EAAE;oBACN,IAAI,EAAE,0BAA0B;oBAChC,QAAQ,EAAE,6BAA6B;oBACvC,GAAG,EAAE,MAAM;oBACX,MAAM,EAAE,EAAE,MAAM,EAAE,wBAAwB,EAAE;iBAC7C;aACF;SACF;QACD,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CACnB,IAAY,EACZ,OAAiD,EAAE;IAEnD,OAAO;QACL,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC,GAAG,GAAG;QAC9B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,GAAG;QAC1B,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,IAAI;QACnC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI;KACX,CAAC;AAChB,CAAC;AAED,SAAS,iBAAiB,CACxB,QAAkB,EAClB,KAAiD;IAEjD,OAAO,IAAI,iBAAiB,CAAC;QAC3B,WAAW,EAAE,YAAY;QACzB,SAAS,EAAE,CAAC,KAAK,EAAE,GAAW,EAAE,IAAiB,EAAE,EAAE;YACnD,KAAK,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3B,OAAO,QAAQ,CAAC;QAClB,CAAC,CAA4B;KAC9B,CAAC,CAAC;AACL,CAAC;AAED,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,KAAK,GAA8C,EAAE,CAAC;QAC5D,MAAM,iBAAiB,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,aAAa,CAC9D,WAAW,EAAE,CACd,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAE3C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,IAAc,CAAC,CAAC,CAAC,OAAO,CAAC;YACxD,IAAI,EAAE,aAAa;YACnB,IAAI,EAAE;gBACJ,MAAM,EAAE;oBACN,KAAK,EAAE,aAAa;oBACpB,UAAU,EAAE,sBAAsB;iBACnC;aACF;YACD,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,gBAAgB;oBACtB,MAAM,EAAE;wBACN,IAAI,EAAE,cAAc;wBACpB,UAAU,EAAE,EAAE;wBACd,OAAO,EAAE,EAAE;wBACX,WAAW,EAAE,WAAW;qBACzB;oBACD,eAAe,EAAE,KAAK;oBACtB,mBAAmB,EAAE,sBAAsB;oBAC3C,MAAM,EAAE;wBACN,IAAI,EAAE,0BAA0B;wBAChC,QAAQ,EAAE,6BAA6B;wBACvC,GAAG,EAAE,MAAM;wBACX,MAAM,EAAE,EAAE,MAAM,EAAE,wBAAwB,EAAE;qBAC7C;iBACF;aACF;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAChD,MAAM,KAAK,GAA8C,EAAE,CAAC;QAC5D,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;QAC3B,MAAM,iBAAiB,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,aAAa,CAAC;YAC/D,GAAG,IAAI;YACP,OAAO,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;SACxE,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,IAAc,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,KAAK,GAA8C,EAAE,CAAC;QAC5D,MAAM,iBAAiB,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,aAAa,CAC9D,WAAW,EAAE,CACd,CAAC;QAEF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,IAAc,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,cAAc,CACvE,iBAAiB,CAClB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACvD,MAAM,KAAK,GAA8C,EAAE,CAAC;QAC5D,MAAM,iBAAiB,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,aAAa,CAC9D,WAAW,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,CACxC,CAAC;QAEF,MAAM,CACJ,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,IAAc,CAAC,CAAC,IAAI,CAAC,eAAe,CAC/D,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,sEAAsE;IACtE,8CAA8C;IAC9C,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,KAAK,GAA8C,EAAE,CAAC;QAC5D,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;QAC3B,MAAM,iBAAiB,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,aAAa,CAAC;YAC/D,GAAG,IAAI;YACP,OAAO,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC;SAC/D,CAAC,CAAC;QAEH,MAAM,CACJ,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,IAAc,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CACrD,CAAC,GAAG,CAAC,cAAc,CAAC,qBAAqB,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,KAAK,GAA8C,EAAE,CAAC;QAC5D,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;QAC3B,MAAM,iBAAiB,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,aAAa,CAAC;YAC/D,GAAG,IAAI;YACP,OAAO,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC;SAC/D,CAAC,CAAC;QAEH,MAAM,CACJ,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,IAAc,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CACrD,CAAC,GAAG,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,MAAM,GAAG,MAAM,iBAAiB,CACpC,YAAY,CACV,IAAI,CAAC,SAAS,CAAC;YACb,SAAS,EAAE,UAAU;YACrB,QAAQ,EAAE,QAAQ;YAClB,cAAc,EAAE,sBAAsB;YACtC,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;YACvC,UAAU,EAAE,SAAS;SACtB,CAAC,CACH,CACF,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAAC;QAE/B,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;YACrB,SAAS,EAAE,UAAU;YACrB,QAAQ,EAAE,QAAQ;YAClB,cAAc,EAAE,sBAAsB;YACtC,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,CAAC;SACd,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,KAAK,IAAI,EAAE;QAC7C,MAAM,MAAM,GAAG,MAAM,iBAAiB,CACpC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CACrE,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAAC;QAE/B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,CACJ,MAAM,iBAAiB,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAC7D,WAAW,EAAE,CACd,CACF,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;YACzE,MAAM,MAAM,CACV,iBAAiB,CACf,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC,EAAE;gBACzD,MAAM,EAAE,GAAG;gBACX,UAAU,EAAE,cAAc;aAC3B,CAAC,CACH,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAC/B,CAAC,OAAO,CAAC,YAAY,CAAC,6CAA6C,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;YAC3E,MAAM,MAAM,CACV,iBAAiB,CACf,YAAY,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC,CAC9D,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAC/B,CAAC,OAAO,CAAC,YAAY,CAAC,uCAAuC,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;YAC9D,MAAM,MAAM,CACV,iBAAiB,CACf,YAAY,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAC3D,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAC/B,CAAC,OAAO,CAAC,YAAY,CACpB,wDAAwD,CACzD,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;YAC/C,MAAM,MAAM,CACV,iBAAiB,CACf,YAAY,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAC1D,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAC/B,CAAC,OAAO,CAAC,YAAY,CAAC,0CAA0C,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;YACxD,MAAM,MAAM,CACV,iBAAiB,CACf,YAAY,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,sBAAsB,EAAE,CAAC,CACtE,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAC/B,CAAC,OAAO,CAAC,YAAY,CACpB,2DAA2D,CAC5D,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,2EAA2E;QAC3E,6EAA6E;QAC7E,6EAA6E;QAC7E,wEAAwE;QACxE,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;YACxE,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;YAC9B,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBAChD,GAAG,KAAK;gBACR,MAAM,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;aAC9D,CAAC,CAAC,CAAC;YAEJ,MAAM,MAAM,CACV,iBAAiB,CACf,YAAY,CACV,IAAI,CAAC,SAAS,CAAC;gBACb,KAAK,EAAE,0DAA0D;aAClE,CAAC,EACF,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,sBAAsB,EAAE,CACpD,CACF,CAAC,aAAa,CAAC,OAAO,CAAC,CACzB,CAAC,OAAO,CAAC,YAAY,CAAC,6BAA6B,CAAC,CAAC;YAEtD,MAAM,MAAM,CACV,iBAAiB,CACf,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE;gBAC9C,MAAM,EAAE,GAAG;gBACX,UAAU,EAAE,sBAAsB;aACnC,CAAC,CACH,CAAC,aAAa,CAAC,OAAO,CAAC,CACzB,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,qBAAqB,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC3C,MAAM,MAAM,CACV,iBAAiB,CACf,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,EAAE;gBACxD,MAAM,EAAE,GAAG;gBACX,UAAU,EAAE,aAAa;aAC1B,CAAC,CACH,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAC/B,CAAC,OAAO,CAAC,YAAY,CAAC,gBAAgB,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,MAAM,CACV,iBAAiB,CACf,YAAY,CACV,IAAI,CAAC,SAAS,CAAC;gBACb,KAAK,EAAE,iDAAiD;gBACxD,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;aACxD,CAAC,EACF,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,aAAa,EAAE,CAC3C,CACF,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAC/B,CAAC,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;YAC7D,MAAM,MAAM,CACV,iBAAiB,CACf,YAAY,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,qBAAqB,EAAE,CAAC,CACrE,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAC/B,CAAC,OAAO,CAAC,YAAY,CAAC,yBAAyB,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;YACpE,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CAAC;gBACrC,WAAW,EAAE,YAAY;gBACzB,SAAS,EAAE,CAAC,KAAK,IAAI,EAAE;oBACrB,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC;gBAC/B,CAAC,CAA4B;aAC9B,CAAC,CAAC;YAEH,MAAM,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CACtE,+CAA+C,CAChD,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -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"}