@celilo/cli 0.3.30-alpha.0 → 0.4.0-alpha.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 (155) hide show
  1. package/drizzle/0005_module_operations.sql +12 -0
  2. package/drizzle/0006_base_module_aspects.sql +15 -0
  3. package/drizzle/0007_module_systems.sql +17 -0
  4. package/drizzle/meta/_journal.json +21 -0
  5. package/package.json +3 -3
  6. package/schemas/system_config.json +14 -28
  7. package/src/ansible/inventory.test.ts +46 -62
  8. package/src/ansible/inventory.ts +48 -25
  9. package/src/capabilities/registration.ts +25 -7
  10. package/src/capabilities/validation.test.ts +30 -0
  11. package/src/capabilities/validation.ts +8 -0
  12. package/src/cli/backup-rename.test.ts +95 -0
  13. package/src/cli/cli.test.ts +17 -23
  14. package/src/cli/command-registry.ts +199 -0
  15. package/src/cli/commands/backup-list.ts +1 -1
  16. package/src/cli/commands/events.ts +96 -0
  17. package/src/cli/commands/machine-add.ts +103 -59
  18. package/src/cli/commands/module-import.ts +153 -4
  19. package/src/cli/commands/module-remove.ts +86 -17
  20. package/src/cli/commands/module-status.ts +6 -2
  21. package/src/cli/commands/publish/alpha.test.ts +185 -0
  22. package/src/cli/commands/publish/alpha.ts +226 -0
  23. package/src/cli/commands/publish/changesets.test.ts +89 -0
  24. package/src/cli/commands/publish/changesets.ts +144 -0
  25. package/src/cli/commands/publish/consumer-pins.test.ts +155 -0
  26. package/src/cli/commands/publish/consumer-pins.ts +149 -0
  27. package/src/cli/commands/publish/execute.ts +131 -0
  28. package/src/cli/commands/publish/global-install.test.ts +154 -0
  29. package/src/cli/commands/publish/global-install.ts +171 -0
  30. package/src/cli/commands/publish/helpers.ts +227 -0
  31. package/src/cli/commands/publish/index.ts +365 -0
  32. package/src/cli/commands/publish/module-registry.test.ts +40 -0
  33. package/src/cli/commands/publish/module-registry.ts +64 -0
  34. package/src/cli/commands/publish/plan.ts +107 -0
  35. package/src/cli/commands/publish/preflight.ts +238 -0
  36. package/src/cli/commands/publish/types.ts +264 -0
  37. package/src/cli/commands/publish/workspace.test.ts +323 -0
  38. package/src/cli/commands/publish/workspace.ts +596 -0
  39. package/src/cli/commands/restore.ts +126 -0
  40. package/src/cli/commands/storage-add-local.ts +1 -1
  41. package/src/cli/commands/storage-add-s3.ts +1 -1
  42. package/src/cli/commands/subscribers-add.ts +68 -0
  43. package/src/cli/commands/subscribers-list.ts +48 -0
  44. package/src/cli/commands/subscribers-remove.ts +38 -0
  45. package/src/cli/commands/subscribers-serve.ts +77 -0
  46. package/src/cli/commands/subscribers-status.ts +33 -0
  47. package/src/cli/commands/subscribers-test.ts +71 -0
  48. package/src/cli/commands/system-apply-config-equivalence.test.ts +108 -0
  49. package/src/cli/commands/system-apply-config.test.ts +70 -0
  50. package/src/cli/commands/system-apply-config.ts +130 -0
  51. package/src/cli/commands/system-audit.ts +2 -1
  52. package/src/cli/commands/system-init-deprecation.test.ts +90 -0
  53. package/src/cli/commands/system-init.ts +36 -70
  54. package/src/cli/commands/system-update.ts +3 -2
  55. package/src/cli/completion.ts +22 -1
  56. package/src/cli/index.ts +214 -6
  57. package/src/cli/interactive-config.test.ts +19 -0
  58. package/src/cli/restore-command.test.ts +131 -0
  59. package/src/db/client.ts +42 -0
  60. package/src/db/schema.test.ts +13 -16
  61. package/src/db/schema.ts +161 -9
  62. package/src/hooks/capability-loader-firewall.test.ts +6 -15
  63. package/src/hooks/capability-loader.test.ts +2 -3
  64. package/src/hooks/capability-loader.ts +36 -2
  65. package/src/hooks/define-hook.test.ts +4 -0
  66. package/src/hooks/executor.test.ts +18 -0
  67. package/src/hooks/executor.ts +21 -2
  68. package/src/hooks/load-hook-config.test.ts +26 -24
  69. package/src/hooks/load-hook-config.ts +11 -2
  70. package/src/hooks/run-named-hook.ts +16 -0
  71. package/src/hooks/types.ts +9 -1
  72. package/src/manifest/contracts/v1.ts +70 -0
  73. package/src/manifest/schema.ts +262 -16
  74. package/src/manifest/validate-privileged.test.ts +84 -0
  75. package/src/manifest/validate.test.ts +156 -0
  76. package/src/manifest/validate.ts +69 -0
  77. package/src/module/import.ts +12 -0
  78. package/src/services/aspect-approvals.test.ts +231 -0
  79. package/src/services/aspect-approvals.ts +120 -0
  80. package/src/services/aspect-runner.test.ts +493 -0
  81. package/src/services/aspect-runner.ts +438 -0
  82. package/src/services/aspect-template-resolver.test.ts +101 -0
  83. package/src/services/aspect-template-resolver.ts +122 -0
  84. package/src/services/backup-create.ts +104 -25
  85. package/src/services/backup-envelope-roundtrip.test.ts +199 -0
  86. package/src/services/backup-in-flight-refusal.test.ts +163 -0
  87. package/src/services/backup-manifest.test.ts +115 -0
  88. package/src/services/backup-manifest.ts +163 -0
  89. package/src/services/backup-restore.ts +154 -19
  90. package/src/services/build-bus/delivery-events.ts +92 -0
  91. package/src/services/build-bus/event-factory.ts +54 -0
  92. package/src/services/build-bus/fan-out.test.ts +279 -0
  93. package/src/services/build-bus/fan-out.ts +161 -0
  94. package/src/services/build-bus/hook-dispatch-mgmt.test.ts +157 -0
  95. package/src/services/build-bus/hook-dispatch.test.ts +207 -0
  96. package/src/services/build-bus/hook-dispatch.ts +198 -0
  97. package/src/services/build-bus/hook-dispatcher.ts +115 -0
  98. package/src/services/build-bus/index.ts +41 -0
  99. package/src/services/build-bus/receiver-server.test.ts +179 -0
  100. package/src/services/build-bus/receiver-server.ts +159 -0
  101. package/src/services/build-bus/status.test.ts +212 -0
  102. package/src/services/build-bus/status.ts +213 -0
  103. package/src/services/build-bus/subscriber-store.ts +113 -0
  104. package/src/services/celilo-events.test.ts +70 -0
  105. package/src/services/celilo-events.ts +92 -0
  106. package/src/services/celilo-mgmt-hooks.test.ts +296 -0
  107. package/src/services/config-interview.ts +13 -95
  108. package/src/services/cross-module-data-manager.ts +2 -31
  109. package/src/services/cross-module-read.test.ts +250 -0
  110. package/src/services/cross-module-read.ts +232 -0
  111. package/src/services/deploy-validation.ts +7 -0
  112. package/src/services/deployed-systems.test.ts +235 -0
  113. package/src/services/deployed-systems.ts +308 -0
  114. package/src/services/dns-provider-backfill.ts +75 -0
  115. package/src/services/health-runner.ts +19 -3
  116. package/src/services/infrastructure-variable-resolver.test.ts +6 -32
  117. package/src/services/infrastructure-variable-resolver.ts +3 -13
  118. package/src/services/machine-detector.ts +104 -48
  119. package/src/services/machine-pool.ts +145 -2
  120. package/src/services/module-config.ts +78 -120
  121. package/src/services/module-deploy.ts +113 -40
  122. package/src/services/module-operations.test.ts +154 -0
  123. package/src/services/module-operations.ts +154 -0
  124. package/src/services/module-subscriptions.test.ts +58 -0
  125. package/src/services/module-subscriptions.ts +24 -1
  126. package/src/services/module-types-generator.test.ts +3 -3
  127. package/src/services/module-types-generator.ts +7 -2
  128. package/src/services/proxmox-reconcile.test.ts +333 -0
  129. package/src/services/proxmox-reconcile.ts +156 -0
  130. package/src/services/proxmox-state-recovery.ts +3 -24
  131. package/src/services/restore-from-file.test.ts +177 -0
  132. package/src/services/restore-from-file.ts +355 -0
  133. package/src/services/restore-preflight.test.ts +127 -0
  134. package/src/services/restore-preflight.ts +118 -0
  135. package/src/services/storage-providers/s3.ts +10 -2
  136. package/src/services/system-identity.ts +30 -0
  137. package/src/services/system-init.test.ts +64 -21
  138. package/src/services/system-init.ts +28 -26
  139. package/src/templates/generator.test.ts +7 -16
  140. package/src/templates/generator.ts +28 -115
  141. package/src/test-utils/integration.ts +5 -2
  142. package/src/types/infrastructure.ts +8 -0
  143. package/src/variables/computed/computed-integration.test.ts +191 -0
  144. package/src/variables/computed/computed.test.ts +177 -0
  145. package/src/variables/computed/evaluate.ts +271 -0
  146. package/src/variables/computed/marker.ts +53 -0
  147. package/src/variables/computed/parse.ts +262 -0
  148. package/src/variables/computed/provider-lookup.ts +130 -0
  149. package/src/variables/context.test.ts +89 -28
  150. package/src/variables/context.ts +196 -191
  151. package/src/variables/parser.ts +3 -3
  152. package/src/variables/resolver.test.ts +61 -0
  153. package/src/variables/resolver.ts +81 -0
  154. package/src/variables/types.ts +23 -1
  155. package/src/services/dns-auto-register.ts +0 -211
@@ -0,0 +1,323 @@
1
+ /**
2
+ * Workspace planner tests — v2/PUBLILO_CLI.md Phase 2 payoff.
3
+ *
4
+ * The planner moved out of the monolithic runPublishPhase precisely so
5
+ * these tests would be possible: no npm subprocesses, no bun publish
6
+ * shelling, no fs writes. We mock the I/O helpers and assert that the
7
+ * planner produces the right WorkspaceItem[] for each PublishMode.
8
+ *
9
+ * Scoped to this file's import-chain only — `./helpers` resolves to
10
+ * `apps/celilo/src/cli/commands/publish/helpers.ts`, and that path is
11
+ * not imported anywhere outside `commands/publish/`, so the
12
+ * `mock.module` call here doesn't leak into unrelated test files.
13
+ * (Contrast with the `prompts.ts` leak that bit us in
14
+ * `interactive-config.test.ts` — `prompts.ts` is imported across the
15
+ * codebase, so a partial mock taints everything.)
16
+ */
17
+
18
+ import { describe, expect, mock, test } from 'bun:test';
19
+
20
+ const PACKAGES = [
21
+ 'packages/cli-display',
22
+ 'packages/capabilities',
23
+ 'packages/event-bus',
24
+ 'apps/celilo',
25
+ 'packages/e2e',
26
+ ] as const;
27
+
28
+ // Synthetic package state. Tests can swap entries here before importing
29
+ // the SUT to control what `readPkg` reports.
30
+ type FakePkg = { name: string; version: string };
31
+ const fakePkgs: Record<string, FakePkg> = {
32
+ 'packages/cli-display': { name: '@celilo/cli-display', version: '0.2.0' },
33
+ 'packages/capabilities': { name: '@celilo/capabilities', version: '0.3.0' },
34
+ 'packages/event-bus': { name: '@celilo/event-bus', version: '0.4.0' },
35
+ 'apps/celilo': { name: '@celilo/cli', version: '0.5.0' },
36
+ 'packages/e2e': { name: '@celilo/e2e', version: '0.6.0' },
37
+ };
38
+
39
+ // Tunable per-test responses for the npm/git helpers.
40
+ let nextAlphaResponses: Record<string, number> = {};
41
+ let skipResponses: Record<string, { skip: boolean; reason?: string }> = {};
42
+ let publishedSet: Set<string> = new Set();
43
+
44
+ mock.module('./helpers', () => ({
45
+ PACKAGES,
46
+ REPO_ROOT: '/fake/repo',
47
+ ENV_FILE: '/fake/repo/.env',
48
+ readPkg: (pkg: string) => fakePkgs[pkg] ?? { name: undefined, version: undefined },
49
+ isPublished: (name: string, version: string) => publishedSet.has(`${name}@${version}`),
50
+ currentGitHead: () => 'fakehead00000000',
51
+ lastCommitTouching: () => null,
52
+ isAncestor: () => false,
53
+ buildWorkspaceVersionMap: () => {
54
+ const m = new Map<string, string>();
55
+ for (const pkg of PACKAGES) m.set(fakePkgs[pkg].name, fakePkgs[pkg].version);
56
+ return m;
57
+ },
58
+ listModuleDirs: () => [],
59
+ readExternalProjectPaths: () => [],
60
+ findPackageJsons: () => [],
61
+ bareVersion: (s: string) => s.replace(/^[\s^~=><]+/, '').trim(),
62
+ withOperator: (oldSpec: string, newVersion: string) => {
63
+ const m = oldSpec.match(/^(\^|~|>=|<=|=|>|<)?/);
64
+ return `${m?.[1] ?? ''}${newVersion}`;
65
+ },
66
+ readGlobalInstalledVersion: () => null,
67
+ fetchLatestVersions: () => new Map(),
68
+ }));
69
+
70
+ mock.module('./alpha', () => ({
71
+ ALPHA_TAG: 'alpha',
72
+ parsePackageSpec: (spec: string) => {
73
+ const i = spec.lastIndexOf('@');
74
+ return { name: spec.slice(0, i), version: spec.slice(i + 1) };
75
+ },
76
+ stripAlphaSuffix: (v: string) => v.replace(/-alpha\.\d+$/, ''),
77
+ isAlphaVersion: (v: string) => /-alpha\.\d+$/.test(v),
78
+ nextAlphaNumber: (name: string, semverCore: string) =>
79
+ nextAlphaResponses[`${name}@${semverCore}`] ?? 0,
80
+ alphaSkipDecision: (_pkg: string, name: string, semverCore: string, n: number) =>
81
+ skipResponses[`${name}@${semverCore}-alpha.${n}`] ?? { skip: false },
82
+ // Pure helpers — re-export real impls so any inner usage works.
83
+ pickNextAlphaN: () => 0,
84
+ decideAlphaSkip: () => ({ skip: false }),
85
+ }));
86
+
87
+ const { planWorkspace } = await import('./workspace');
88
+
89
+ function buildBaseMap(): Map<string, string> {
90
+ const m = new Map<string, string>();
91
+ for (const pkg of PACKAGES) m.set(fakePkgs[pkg].name, fakePkgs[pkg].version);
92
+ return m;
93
+ }
94
+
95
+ describe('planWorkspace', () => {
96
+ describe('normal mode', () => {
97
+ test('plans every package at its current base version', () => {
98
+ publishedSet = new Set();
99
+ const result = planWorkspace({
100
+ mode: { kind: 'normal' },
101
+ packages: PACKAGES,
102
+ baseWorkspaceVersions: buildBaseMap(),
103
+ gitHead: 'fakehead00000000',
104
+ });
105
+
106
+ expect(result.items).toHaveLength(5);
107
+ expect(result.items.map((i) => `${i.name}@${i.versionToPublish}`)).toEqual([
108
+ '@celilo/cli-display@0.2.0',
109
+ '@celilo/capabilities@0.3.0',
110
+ '@celilo/event-bus@0.4.0',
111
+ '@celilo/cli@0.5.0',
112
+ '@celilo/e2e@0.6.0',
113
+ ]);
114
+ expect(result.items.every((i) => i.tag === undefined)).toBe(true);
115
+ expect(result.items.every((i) => i.skipReason === undefined)).toBe(true);
116
+ });
117
+
118
+ test('marks already-published packages as skipped', () => {
119
+ publishedSet = new Set(['@celilo/e2e@0.6.0']);
120
+ const result = planWorkspace({
121
+ mode: { kind: 'normal' },
122
+ packages: PACKAGES,
123
+ baseWorkspaceVersions: buildBaseMap(),
124
+ gitHead: 'fakehead00000000',
125
+ });
126
+
127
+ const e2e = result.items.find((i) => i.name === '@celilo/e2e');
128
+ expect(e2e?.skipReason).toBe('already published');
129
+ // Other packages keep going.
130
+ const cli = result.items.find((i) => i.name === '@celilo/cli');
131
+ expect(cli?.skipReason).toBeUndefined();
132
+ });
133
+
134
+ test('e2e gets the registry-bundle + netapps + cache hooks', () => {
135
+ publishedSet = new Set();
136
+ const result = planWorkspace({
137
+ mode: { kind: 'normal' },
138
+ packages: PACKAGES,
139
+ baseWorkspaceVersions: buildBaseMap(),
140
+ gitHead: 'fakehead00000000',
141
+ });
142
+
143
+ const e2e = result.items.find((i) => i.pkg === 'packages/e2e');
144
+ expect(e2e?.hooks).toEqual([
145
+ 'registryServerBundle',
146
+ 'rebuildE2eNetapps',
147
+ 'stageE2ePublishCaches',
148
+ ]);
149
+ const cli = result.items.find((i) => i.pkg === 'apps/celilo');
150
+ expect(cli?.hooks).toEqual([]);
151
+ });
152
+ });
153
+
154
+ describe('alpha mode', () => {
155
+ test('plans X.Y.Z-alpha.0 when no prior alphas on npm', () => {
156
+ publishedSet = new Set();
157
+ nextAlphaResponses = {};
158
+ skipResponses = {};
159
+
160
+ const result = planWorkspace({
161
+ mode: { kind: 'alpha', trackAlpha: false, alphaModules: false },
162
+ packages: PACKAGES,
163
+ baseWorkspaceVersions: buildBaseMap(),
164
+ gitHead: 'fakehead00000000',
165
+ });
166
+
167
+ expect(result.items.map((i) => i.versionToPublish)).toEqual([
168
+ '0.2.0-alpha.0',
169
+ '0.3.0-alpha.0',
170
+ '0.4.0-alpha.0',
171
+ '0.5.0-alpha.0',
172
+ '0.6.0-alpha.0',
173
+ ]);
174
+ expect(result.items.every((i) => i.tag === 'alpha')).toBe(true);
175
+ });
176
+
177
+ test('uses next-N + skipDecision per package', () => {
178
+ publishedSet = new Set();
179
+ nextAlphaResponses = {
180
+ '@celilo/cli-display@0.2.0': 3,
181
+ '@celilo/capabilities@0.3.0': 0,
182
+ '@celilo/event-bus@0.4.0': 1,
183
+ '@celilo/cli@0.5.0': 2,
184
+ '@celilo/e2e@0.6.0': 5,
185
+ };
186
+ // event-bus has nothing new since alpha.0 — skip.
187
+ skipResponses = {
188
+ '@celilo/event-bus@0.4.0-alpha.1': {
189
+ skip: true,
190
+ reason: 'no source changes since 0.4.0-alpha.0 (gitHead abc123)',
191
+ },
192
+ };
193
+
194
+ const result = planWorkspace({
195
+ mode: { kind: 'alpha', trackAlpha: false, alphaModules: false },
196
+ packages: PACKAGES,
197
+ baseWorkspaceVersions: buildBaseMap(),
198
+ gitHead: 'fakehead00000000',
199
+ });
200
+
201
+ // Skipped → pins to the EXISTING alpha (alpha.{n-1} = alpha.0), NOT the
202
+ // computed-next alpha.1 which is never published. (ISS-0017: pinning to
203
+ // the next made dependents unpublishable — "@celilo/...@alpha.1 not on
204
+ // npm".) Both the item and the consumer-pin map must use alpha.0.
205
+ const eb = result.items.find((i) => i.name === '@celilo/event-bus');
206
+ expect(eb?.versionToPublish).toBe('0.4.0-alpha.0');
207
+ expect(eb?.skipReason).toContain('no source changes since 0.4.0-alpha.0');
208
+ expect(result.workspaceVersions.get('@celilo/event-bus')).toBe('0.4.0-alpha.0');
209
+
210
+ const cli = result.items.find((i) => i.name === '@celilo/cli');
211
+ expect(cli?.versionToPublish).toBe('0.5.0-alpha.2');
212
+ expect(cli?.skipReason).toBeUndefined();
213
+ });
214
+
215
+ test('alpha mode sets exactPins and stamps gitHead in rewriteOptions', () => {
216
+ publishedSet = new Set();
217
+ nextAlphaResponses = {};
218
+ skipResponses = {};
219
+
220
+ const result = planWorkspace({
221
+ mode: { kind: 'alpha', trackAlpha: false, alphaModules: false },
222
+ packages: PACKAGES,
223
+ baseWorkspaceVersions: buildBaseMap(),
224
+ gitHead: 'abcdef1234567890',
225
+ });
226
+
227
+ for (const item of result.items) {
228
+ expect(item.rewriteOptions.exactPins).toBe(true);
229
+ expect(item.rewriteOptions.gitHead).toBe('abcdef1234567890');
230
+ expect(item.rewriteOptions.targetVersion).toBe(item.versionToPublish);
231
+ }
232
+ });
233
+
234
+ test('workspace dep map gets tightened to alpha versions for sibling rewrites', () => {
235
+ publishedSet = new Set();
236
+ nextAlphaResponses = {};
237
+ skipResponses = {};
238
+
239
+ const result = planWorkspace({
240
+ mode: { kind: 'alpha', trackAlpha: false, alphaModules: false },
241
+ packages: PACKAGES,
242
+ baseWorkspaceVersions: buildBaseMap(),
243
+ gitHead: 'fakehead00000000',
244
+ });
245
+
246
+ // After planning, the workspaceVersions map should hold alpha
247
+ // versions (so the executor's workspace:^ rewrites pin to alphas).
248
+ expect(result.workspaceVersions.get('@celilo/cli-display')).toBe('0.2.0-alpha.0');
249
+ expect(result.workspaceVersions.get('@celilo/event-bus')).toBe('0.4.0-alpha.0');
250
+ expect(result.workspaceVersions.get('@celilo/e2e')).toBe('0.6.0-alpha.0');
251
+ });
252
+ });
253
+
254
+ describe('promote mode', () => {
255
+ test('plans only the named package, stripping the alpha suffix', () => {
256
+ publishedSet = new Set();
257
+
258
+ const result = planWorkspace({
259
+ mode: {
260
+ kind: 'promote',
261
+ target: { name: '@celilo/e2e', version: '0.6.0-alpha.3' },
262
+ },
263
+ packages: PACKAGES,
264
+ baseWorkspaceVersions: buildBaseMap(),
265
+ gitHead: 'fakehead00000000',
266
+ });
267
+
268
+ expect(result.items).toHaveLength(1);
269
+ expect(result.items[0].name).toBe('@celilo/e2e');
270
+ expect(result.items[0].versionToPublish).toBe('0.6.0');
271
+ expect(result.items[0].tag).toBeUndefined();
272
+ });
273
+
274
+ test('throws when the target name is not a known workspace', () => {
275
+ publishedSet = new Set();
276
+ expect(() =>
277
+ planWorkspace({
278
+ mode: {
279
+ kind: 'promote',
280
+ target: { name: '@some/other-pkg', version: '1.0.0-alpha.0' },
281
+ },
282
+ packages: PACKAGES,
283
+ baseWorkspaceVersions: buildBaseMap(),
284
+ gitHead: 'fakehead00000000',
285
+ }),
286
+ ).toThrow('not a known workspace package');
287
+ });
288
+
289
+ test('throws when the target version has no alpha suffix', () => {
290
+ publishedSet = new Set();
291
+ expect(() =>
292
+ planWorkspace({
293
+ mode: {
294
+ kind: 'promote',
295
+ target: { name: '@celilo/e2e', version: '0.6.0' },
296
+ },
297
+ packages: PACKAGES,
298
+ baseWorkspaceVersions: buildBaseMap(),
299
+ gitHead: 'fakehead00000000',
300
+ }),
301
+ ).toThrow('not an alpha');
302
+ });
303
+ });
304
+
305
+ describe('dependency order', () => {
306
+ test('preserves PACKAGES order in planned items', () => {
307
+ publishedSet = new Set();
308
+
309
+ const result = planWorkspace({
310
+ mode: { kind: 'normal' },
311
+ packages: PACKAGES,
312
+ baseWorkspaceVersions: buildBaseMap(),
313
+ gitHead: 'fakehead00000000',
314
+ });
315
+
316
+ // Leaves (cli-display) first, consumers (e2e) last — same as the
317
+ // PACKAGES tuple. The executor relies on this ordering for the
318
+ // workspace:^ rewrite to find sibling versions before they're
319
+ // needed.
320
+ expect(result.items.map((i) => i.pkg)).toEqual([...PACKAGES]);
321
+ });
322
+ });
323
+ });