@intentius/chant 0.124.16 → 0.124.18
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.
- package/dist/__golden__/wave-loop/fan-out-applied.json +511 -0
- package/dist/__golden__/wave-loop/fan-out-apply-fails.json +406 -0
- package/dist/__golden__/wave-loop/fan-out-gated.json +707 -0
- package/dist/__golden__/wave-loop/fan-out-no-members.json +898 -0
- package/dist/__golden__/wave-loop/fan-out-only.json +514 -0
- package/dist/__golden__/wave-loop/fan-out-plan-fails.json +359 -0
- package/dist/__golden__/wave-loop/fan-out-same-root.json +311 -0
- package/dist/__golden__/wave-loop/op-by-apply-after-fails.json +401 -0
- package/dist/__golden__/wave-loop/op-by-apply-hold.json +756 -0
- package/dist/__golden__/wave-loop/op-by-apply-ignored.json +340 -0
- package/dist/__golden__/wave-loop/op-by-apply-pass.json +400 -0
- package/dist/__golden__/wave-loop/op-by-apply-skip.json +404 -0
- package/dist/__golden__/wave-loop/op-cost-member-clash.json +62 -0
- package/dist/__golden__/wave-loop/op-cost-member.json +191 -0
- package/dist/__golden__/wave-loop/op-denied-source.json +407 -0
- package/dist/__golden__/wave-loop/op-denied.json +399 -0
- package/dist/__golden__/wave-loop/op-discover-rest-short.json +92 -0
- package/dist/__golden__/wave-loop/op-discover-rest-stops.json +340 -0
- package/dist/__golden__/wave-loop/op-discover-rest.json +659 -0
- package/dist/__golden__/wave-loop/op-discover-wide.json +290 -0
- package/dist/__golden__/wave-loop/op-held-on-again.json +176 -0
- package/dist/__golden__/wave-loop/op-held-on-before-apply.json +689 -0
- package/dist/__golden__/wave-loop/op-held-on.json +1455 -0
- package/dist/__golden__/wave-loop/op-hooks-apply-fails.json +673 -0
- package/dist/__golden__/wave-loop/op-hooks-fail-after-apply.json +737 -0
- package/dist/__golden__/wave-loop/op-hooks-fail-before-apply.json +450 -0
- package/dist/__golden__/wave-loop/op-hooks-fail-before-plan.json +97 -0
- package/dist/__golden__/wave-loop/op-hooks-held.json +1059 -0
- package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +693 -0
- package/dist/__golden__/wave-loop/op-hooks-pass.json +735 -0
- package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +148 -0
- package/dist/__golden__/wave-loop/op-hooks-share.json +1225 -0
- package/dist/__golden__/wave-loop/op-outcome-held.json +202 -0
- package/dist/__golden__/wave-loop/op-outcome-replan-approved.json +286 -0
- package/dist/__golden__/wave-loop/op-outcome-replan-forever.json +823 -0
- package/dist/__golden__/wave-loop/op-outcome-replan.json +259 -0
- package/dist/__golden__/wave-loop/op-outcome-stood-down.json +196 -0
- package/dist/__golden__/wave-loop/op-outcome-unreadable.json +193 -0
- package/dist/__golden__/wave-loop/op-overridden.json +508 -0
- package/dist/__golden__/wave-loop/op-per-run.json +518 -0
- package/dist/__golden__/wave-loop/op-point.json +495 -0
- package/dist/__golden__/wave-loop/op-pr-review.json +397 -0
- package/dist/__golden__/wave-loop/op-sealed.json +125 -0
- package/dist/__golden__/wave-loop/op-spend-change-only.json +168 -0
- package/dist/__golden__/wave-loop/op-spend.json +328 -0
- package/dist/__golden__/wave-loop/op-wave-commands.json +1808 -0
- package/dist/__golden__/wave-loop/op-wide-refused.json +291 -0
- package/dist/__golden__/wave-loop/op-wide-waiting.json +231 -0
- package/dist/__golden__/wave-loop/op-wide.json +440 -0
- package/dist/cli/commands/check-lexicon-plugin.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-plugin.js +2 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/lint.js +3 -2
- package/dist/components/discover.d.ts.map +1 -1
- package/dist/components/discover.js +2 -1
- package/dist/components/fan-out-run.d.ts +4 -3
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +138 -123
- package/dist/config-sandbox.d.ts.map +1 -1
- package/dist/config-sandbox.js +5 -1
- package/dist/discovery/import.d.ts.map +1 -1
- package/dist/discovery/import.js +2 -1
- package/dist/discovery/sandbox/driver.d.ts.map +1 -1
- package/dist/discovery/sandbox/driver.js +8 -3
- package/dist/lexicon-module.d.ts.map +1 -1
- package/dist/lexicon-module.js +4 -3
- package/dist/lint/policy-import.d.ts.map +1 -1
- package/dist/lint/policy-import.js +2 -1
- package/dist/lint/rule-loader.d.ts.map +1 -1
- package/dist/lint/rule-loader.js +2 -1
- package/dist/module-interop.d.ts +20 -0
- package/dist/module-interop.d.ts.map +1 -0
- package/dist/module-interop.js +39 -0
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/discover.js +3 -2
- package/dist/op/op-waves-run.d.ts +7 -1
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +129 -72
- package/dist/wave-engine.d.ts +74 -5
- package/dist/wave-engine.d.ts.map +1 -1
- package/dist/wave-engine.js +47 -5
- package/dist/wave-runtime.mjs +218 -218
- package/dist/workspace/lineage-migrations.d.ts.map +1 -1
- package/dist/workspace/lineage-migrations.js +2 -1
- package/package.json +1 -1
- package/src/__golden__/wave-loop/fan-out-applied.json +511 -0
- package/src/__golden__/wave-loop/fan-out-apply-fails.json +406 -0
- package/src/__golden__/wave-loop/fan-out-gated.json +707 -0
- package/src/__golden__/wave-loop/fan-out-no-members.json +898 -0
- package/src/__golden__/wave-loop/fan-out-only.json +514 -0
- package/src/__golden__/wave-loop/fan-out-plan-fails.json +359 -0
- package/src/__golden__/wave-loop/fan-out-same-root.json +311 -0
- package/src/__golden__/wave-loop/op-by-apply-after-fails.json +401 -0
- package/src/__golden__/wave-loop/op-by-apply-hold.json +756 -0
- package/src/__golden__/wave-loop/op-by-apply-ignored.json +340 -0
- package/src/__golden__/wave-loop/op-by-apply-pass.json +400 -0
- package/src/__golden__/wave-loop/op-by-apply-skip.json +404 -0
- package/src/__golden__/wave-loop/op-cost-member-clash.json +62 -0
- package/src/__golden__/wave-loop/op-cost-member.json +191 -0
- package/src/__golden__/wave-loop/op-denied-source.json +407 -0
- package/src/__golden__/wave-loop/op-denied.json +399 -0
- package/src/__golden__/wave-loop/op-discover-rest-short.json +92 -0
- package/src/__golden__/wave-loop/op-discover-rest-stops.json +340 -0
- package/src/__golden__/wave-loop/op-discover-rest.json +659 -0
- package/src/__golden__/wave-loop/op-discover-wide.json +290 -0
- package/src/__golden__/wave-loop/op-held-on-again.json +176 -0
- package/src/__golden__/wave-loop/op-held-on-before-apply.json +689 -0
- package/src/__golden__/wave-loop/op-held-on.json +1455 -0
- package/src/__golden__/wave-loop/op-hooks-apply-fails.json +673 -0
- package/src/__golden__/wave-loop/op-hooks-fail-after-apply.json +737 -0
- package/src/__golden__/wave-loop/op-hooks-fail-before-apply.json +450 -0
- package/src/__golden__/wave-loop/op-hooks-fail-before-plan.json +97 -0
- package/src/__golden__/wave-loop/op-hooks-held.json +1059 -0
- package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +693 -0
- package/src/__golden__/wave-loop/op-hooks-pass.json +735 -0
- package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +148 -0
- package/src/__golden__/wave-loop/op-hooks-share.json +1225 -0
- package/src/__golden__/wave-loop/op-outcome-held.json +202 -0
- package/src/__golden__/wave-loop/op-outcome-replan-approved.json +286 -0
- package/src/__golden__/wave-loop/op-outcome-replan-forever.json +823 -0
- package/src/__golden__/wave-loop/op-outcome-replan.json +259 -0
- package/src/__golden__/wave-loop/op-outcome-stood-down.json +196 -0
- package/src/__golden__/wave-loop/op-outcome-unreadable.json +193 -0
- package/src/__golden__/wave-loop/op-overridden.json +508 -0
- package/src/__golden__/wave-loop/op-per-run.json +518 -0
- package/src/__golden__/wave-loop/op-point.json +495 -0
- package/src/__golden__/wave-loop/op-pr-review.json +397 -0
- package/src/__golden__/wave-loop/op-sealed.json +125 -0
- package/src/__golden__/wave-loop/op-spend-change-only.json +168 -0
- package/src/__golden__/wave-loop/op-spend.json +328 -0
- package/src/__golden__/wave-loop/op-wave-commands.json +1808 -0
- package/src/__golden__/wave-loop/op-wide-refused.json +291 -0
- package/src/__golden__/wave-loop/op-wide-waiting.json +231 -0
- package/src/__golden__/wave-loop/op-wide.json +440 -0
- package/src/cli/commands/check-lexicon-plugin.ts +2 -1
- package/src/cli/commands/lint.ts +3 -2
- package/src/cli/compiled-cli.e2e.test.ts +42 -1
- package/src/components/discover.ts +2 -1
- package/src/components/fan-out-run.ts +156 -128
- package/src/config-sandbox.ts +5 -1
- package/src/discovery/import.ts +2 -1
- package/src/discovery/sandbox/driver.ts +8 -3
- package/src/lexicon-module.ts +4 -3
- package/src/lint/policy-import.ts +2 -1
- package/src/lint/rule-loader.ts +2 -1
- package/src/meta/peer-deps.test.ts +39 -0
- package/src/module-interop.test.ts +65 -0
- package/src/module-interop.ts +39 -0
- package/src/op/discover.ts +3 -2
- package/src/op/op-waves-run.ts +184 -68
- package/src/wave-engine.test.ts +38 -1
- package/src/wave-engine.ts +101 -5
- package/src/wave-loop-golden.test.ts +627 -0
- package/src/workspace/lineage-migrations.ts +2 -1
|
@@ -31,16 +31,17 @@
|
|
|
31
31
|
* ## Or one approval per wave
|
|
32
32
|
*
|
|
33
33
|
* `waveGate` (#3049) is the other mode. Each wave is planned once the waves
|
|
34
|
-
* before it applied, and its gate binds that wave's set digest.
|
|
35
|
-
* the digest and the gate decision come from the wave engine
|
|
36
|
-
* (`../wave-engine.ts`, #3766), which `chant run wave` uses too
|
|
34
|
+
* before it applied, and its gate binds that wave's set digest. The loop,
|
|
35
|
+
* gate names, the digest and the gate decision come from the wave engine
|
|
36
|
+
* (`../wave-engine.ts`, #3766), which `chant run wave` uses too; this module
|
|
37
|
+
* hands it the steps a fan-out plans, decides and applies a wave with. See
|
|
37
38
|
* {@link runGatedWaves}.
|
|
38
39
|
*/
|
|
39
40
|
import { deployContext, runComponentDeploy, } from "./driver.js";
|
|
40
41
|
import { downstreamWithin, remainingFanOut } from "./fan-out.js";
|
|
41
42
|
import { evaluateGate, gitGateLedgerPort } from "../op/gate.js";
|
|
42
43
|
import { planWaveComponent } from "./wave-plan.js";
|
|
43
|
-
import { decideWaveGate, waveGateName, waveSetDigest } from "../wave-engine.js";
|
|
44
|
+
import { decideWaveGate, runWaveLoop, waveGateName, waveSetDigest } from "../wave-engine.js";
|
|
44
45
|
/** Thrown when `FanOutWaveGate.only` names a wave whose predecessors have not all applied. */
|
|
45
46
|
export class EarlierWaveNotAppliedError extends Error {
|
|
46
47
|
constructor(wave, earlier, components) {
|
|
@@ -230,131 +231,145 @@ async function runGatedWaves(plan, active, components, registry, options, waveGa
|
|
|
230
231
|
}
|
|
231
232
|
};
|
|
232
233
|
options.onProgress?.({ type: "run-start", waves: plan.waves });
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const gateName = waveGateName(waveGate.gate, waveNum);
|
|
246
|
-
const contexts = new Map(runnable.map((name) => [name, fanOutContext(options, name)]));
|
|
247
|
-
const planned = await Promise.all(runnable.map((name) => planComponent(byName.get(name), contexts.get(name), componentOutputs)));
|
|
248
|
-
const members = [];
|
|
249
|
-
const plansFor = new Map();
|
|
250
|
-
const waveFailures = [];
|
|
251
|
-
for (const p of planned) {
|
|
252
|
-
if ("error" in p) {
|
|
253
|
-
waveFailures.push({ component: p.component, error: p.error });
|
|
254
|
-
continue;
|
|
234
|
+
await runWaveLoop({
|
|
235
|
+
first: 1,
|
|
236
|
+
onward: (_k, outcome) => outcome === "next",
|
|
237
|
+
async plan({ k: waveNum }) {
|
|
238
|
+
const wave = plan.waves[waveNum - 1];
|
|
239
|
+
if (!wave)
|
|
240
|
+
return { end: "stop" };
|
|
241
|
+
const runnable = wave.filter((name) => left.has(name) && !blockedBy.has(name));
|
|
242
|
+
if (waveGate.only !== undefined && waveNum < waveGate.only) {
|
|
243
|
+
if (runnable.length > 0)
|
|
244
|
+
throw new EarlierWaveNotAppliedError(waveGate.only, waveNum, runnable);
|
|
245
|
+
return { end: "next" };
|
|
255
246
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
247
|
+
if (waveGate.only !== undefined && waveNum > waveGate.only)
|
|
248
|
+
return { end: "stop" };
|
|
249
|
+
if (runnable.length === 0)
|
|
250
|
+
return { end: "next" };
|
|
251
|
+
const gateName = waveGateName(waveGate.gate, waveNum);
|
|
252
|
+
const contexts = new Map(runnable.map((name) => [name, fanOutContext(options, name)]));
|
|
253
|
+
const planned = await Promise.all(runnable.map((name) => planComponent(byName.get(name), contexts.get(name), componentOutputs)));
|
|
254
|
+
const members = [];
|
|
255
|
+
const plansFor = new Map();
|
|
256
|
+
const waveFailures = [];
|
|
257
|
+
for (const p of planned) {
|
|
258
|
+
if ("error" in p) {
|
|
259
|
+
waveFailures.push({ component: p.component, error: p.error });
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
members.push(...p.members);
|
|
263
|
+
plansFor.set(p.component, p.plans);
|
|
264
|
+
}
|
|
265
|
+
return { go: { runnable, gateName, contexts, members, plansFor, waveFailures } };
|
|
266
|
+
},
|
|
267
|
+
digest(_at, { members, plansFor, waveFailures }) {
|
|
268
|
+
try {
|
|
269
|
+
// A wave where nothing planned has no set, and so no digest.
|
|
270
|
+
return members.length > 0 ? waveSetDigest(members) : "";
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
// Two components planning the same root: there is no one plan for it.
|
|
274
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
275
|
+
for (const name of plansFor.keys())
|
|
276
|
+
waveFailures.push({ component: name, error });
|
|
277
|
+
plansFor.clear();
|
|
278
|
+
return "";
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
async decide({ k: waveNum }, { runnable, gateName, members, plansFor, waveFailures }, digest) {
|
|
282
|
+
for (const f of waveFailures) {
|
|
283
|
+
failed.push(f.component);
|
|
284
|
+
results.push(planFailure(f.component, f.error ?? "plan failed"));
|
|
285
|
+
markBlocked(f.component);
|
|
286
|
+
}
|
|
287
|
+
const toRun = runnable.filter((name) => plansFor.has(name));
|
|
288
|
+
const record = {
|
|
289
|
+
wave: waveNum,
|
|
290
|
+
op: waveGate.op,
|
|
291
|
+
gate: gateName,
|
|
292
|
+
components: [...runnable].sort(),
|
|
293
|
+
digest,
|
|
294
|
+
members: [...members].sort((a, b) => a.member.localeCompare(b.member)),
|
|
295
|
+
status: "failed",
|
|
296
|
+
...(waveFailures.length > 0 ? { failed: waveFailures } : {}),
|
|
297
|
+
};
|
|
298
|
+
if (toRun.length === 0) {
|
|
299
|
+
waves.push(record);
|
|
300
|
+
options.onWaveSettled?.(record);
|
|
301
|
+
return { end: "next" };
|
|
302
|
+
}
|
|
303
|
+
const check = await decideWaveGate(gates.port, {
|
|
304
|
+
op: waveGate.op,
|
|
305
|
+
gate: gateName,
|
|
306
|
+
digest,
|
|
307
|
+
description: waveGate.description ?? `wave ${waveNum} of ${plan.waves.length}: ${toRun.join(", ")}`,
|
|
308
|
+
...(waveGate.timeout ? { timeout: waveGate.timeout } : {}),
|
|
309
|
+
...(options.now ? { now: options.now } : {}),
|
|
310
|
+
});
|
|
311
|
+
if (!check.satisfied) {
|
|
312
|
+
// Nothing in this wave ran, and no later wave is planned: its plan
|
|
313
|
+
// would read outputs this wave has not written.
|
|
314
|
+
const stopped = {
|
|
315
|
+
...record,
|
|
316
|
+
status: "gated",
|
|
317
|
+
...(check.mismatch?.approved ? { approved: check.mismatch.approved } : {}),
|
|
318
|
+
};
|
|
319
|
+
waves.push(stopped);
|
|
320
|
+
options.onWaveSettled?.(stopped);
|
|
321
|
+
gatedFact = check.pending;
|
|
322
|
+
return { end: "stop" };
|
|
323
|
+
}
|
|
324
|
+
return { go: { check, record, toRun } };
|
|
325
|
+
},
|
|
326
|
+
async apply({ k: waveNum }, { contexts, plansFor, waveFailures }, { check, record, toRun }) {
|
|
327
|
+
options.onProgress?.({ type: "wave-start", wave: waveNum, components: toRun });
|
|
328
|
+
const waveResults = await Promise.all(toRun.map(async (name) => {
|
|
329
|
+
options.onProgress?.({ type: "component-start", wave: waveNum, component: name });
|
|
330
|
+
const plans = plansFor.get(name);
|
|
331
|
+
const ctx = { ...contexts.get(name), ...(Object.keys(plans).length > 0 ? { plans } : {}) };
|
|
332
|
+
const result = await runComponentDeploy(byName.get(name), ctx, registry, componentOutputs, options.onProgress, gates);
|
|
333
|
+
options.onProgress?.({
|
|
334
|
+
type: "component-done",
|
|
335
|
+
wave: waveNum,
|
|
336
|
+
component: name,
|
|
337
|
+
status: result.status === "fail" ? "failed" : result.status,
|
|
338
|
+
});
|
|
339
|
+
options.onComponentSettled?.(result, componentOutputs[name]);
|
|
340
|
+
return result;
|
|
341
|
+
}));
|
|
342
|
+
results.push(...waveResults);
|
|
343
|
+
for (const result of waveResults) {
|
|
344
|
+
if (result.status === "ok") {
|
|
345
|
+
completed.push(result.component);
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
if (result.status === "fail") {
|
|
349
|
+
failed.push(result.component);
|
|
350
|
+
waveFailures.push({ component: result.component });
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
gatedFact ?? (gatedFact = result.gate);
|
|
354
|
+
}
|
|
355
|
+
markBlocked(result.component);
|
|
356
|
+
}
|
|
357
|
+
const settled = {
|
|
305
358
|
...record,
|
|
306
|
-
status: "
|
|
307
|
-
|
|
359
|
+
status: waveFailures.length > 0 ? "failed" : "applied",
|
|
360
|
+
approvedBy: check.resolution.resolvedBy,
|
|
361
|
+
...(waveFailures.length > 0 ? { failed: waveFailures } : {}),
|
|
308
362
|
};
|
|
309
|
-
waves.push(
|
|
310
|
-
options.onWaveSettled?.(
|
|
311
|
-
gatedFact = check.pending;
|
|
312
|
-
break;
|
|
313
|
-
}
|
|
314
|
-
options.onProgress?.({ type: "wave-start", wave: waveNum, components: toRun });
|
|
315
|
-
const waveResults = await Promise.all(toRun.map(async (name) => {
|
|
316
|
-
options.onProgress?.({ type: "component-start", wave: waveNum, component: name });
|
|
317
|
-
const plans = plansFor.get(name);
|
|
318
|
-
const ctx = { ...contexts.get(name), ...(Object.keys(plans).length > 0 ? { plans } : {}) };
|
|
319
|
-
const result = await runComponentDeploy(byName.get(name), ctx, registry, componentOutputs, options.onProgress, gates);
|
|
363
|
+
waves.push(settled);
|
|
364
|
+
options.onWaveSettled?.(settled);
|
|
320
365
|
options.onProgress?.({
|
|
321
|
-
type: "
|
|
366
|
+
type: "wave-done",
|
|
322
367
|
wave: waveNum,
|
|
323
|
-
|
|
324
|
-
status: result.status === "fail" ? "failed" : result.status,
|
|
368
|
+
status: waveFailures.length > 0 ? "failed" : waveResults.some((r) => r.status === "gated") ? "gated" : "ok",
|
|
325
369
|
});
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
results.push(...waveResults);
|
|
330
|
-
for (const result of waveResults) {
|
|
331
|
-
if (result.status === "ok") {
|
|
332
|
-
completed.push(result.component);
|
|
333
|
-
continue;
|
|
334
|
-
}
|
|
335
|
-
if (result.status === "fail") {
|
|
336
|
-
failed.push(result.component);
|
|
337
|
-
waveFailures.push({ component: result.component });
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
340
|
-
gatedFact ?? (gatedFact = result.gate);
|
|
341
|
-
}
|
|
342
|
-
markBlocked(result.component);
|
|
343
|
-
}
|
|
344
|
-
const settled = {
|
|
345
|
-
...record,
|
|
346
|
-
status: waveFailures.length > 0 ? "failed" : "applied",
|
|
347
|
-
approvedBy: check.resolution.resolvedBy,
|
|
348
|
-
...(waveFailures.length > 0 ? { failed: waveFailures } : {}),
|
|
349
|
-
};
|
|
350
|
-
waves.push(settled);
|
|
351
|
-
options.onWaveSettled?.(settled);
|
|
352
|
-
options.onProgress?.({
|
|
353
|
-
type: "wave-done",
|
|
354
|
-
wave: waveNum,
|
|
355
|
-
status: waveFailures.length > 0 ? "failed" : waveResults.some((r) => r.status === "gated") ? "gated" : "ok",
|
|
356
|
-
});
|
|
357
|
-
}
|
|
370
|
+
return "next";
|
|
371
|
+
},
|
|
372
|
+
});
|
|
358
373
|
const blocked = [...blockedBy.entries()]
|
|
359
374
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
360
375
|
.map(([component, by]) => ({ component, reason: "blocked", blockedBy: by }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-sandbox.d.ts","sourceRoot":"","sources":["../src/config-sandbox.ts"],"names":[],"mappings":"AACA,OAAO,EAA2C,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"config-sandbox.d.ts","sourceRoot":"","sources":["../src/config-sandbox.ts"],"names":[],"mappings":"AACA,OAAO,EAA2C,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAoDtG;;;;;;GAMG;AACH,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD;AAED,kEAAkE;AAClE,wBAAgB,8BAA8B,IAAI,OAAO,CAExD;AAED;;;GAGG;AACH,wBAAgB,oCAAoC,IAAI,IAAI,CAG3D;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,qBAAqB,GAAG,OAAO,CAK5E;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,WAAW,GAAE,MAA4B,GACxC,OAAO,CAAC,OAAO,CAAC,CAclB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAUlF"}
|
package/dist/config-sandbox.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { dirname } from "node:path";
|
|
2
2
|
import { importConfigModule, requireConfigModule } from "./config-import.js";
|
|
3
|
+
import { esModuleNamespace } from "./module-interop.js";
|
|
3
4
|
/**
|
|
4
5
|
* chant #1113 — decides WHERE a project's `chant.config.ts` is evaluated.
|
|
5
6
|
*
|
|
@@ -77,7 +78,10 @@ export function resetSandboxConfigEvaluationForTests() {
|
|
|
77
78
|
* evaluated, never how its result is read.
|
|
78
79
|
*/
|
|
79
80
|
export function selectConfigExport(namespace) {
|
|
80
|
-
|
|
81
|
+
// A config tsx compiled as CommonJS arrives as `{ default: module.exports }`
|
|
82
|
+
// under a native import (#3897); read the module's own namespace instead.
|
|
83
|
+
const ns = esModuleNamespace(namespace);
|
|
84
|
+
return ns.default ?? ns.config ?? ns;
|
|
81
85
|
}
|
|
82
86
|
/**
|
|
83
87
|
* Evaluate a project's `chant.config.ts` and return its configuration object,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../src/discovery/import.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../src/discovery/import.ts"],"names":[],"mappings":"AA8BA;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAclC"}
|
package/dist/discovery/import.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DiscoveryError } from "../errors.js";
|
|
2
2
|
import { ensureTypeScriptLoader } from "../ts-loader.js";
|
|
3
|
+
import { esModuleNamespace } from "../module-interop.js";
|
|
3
4
|
/**
|
|
4
5
|
* Modules whose evaluation threw, and the error each threw with (#2368).
|
|
5
6
|
*
|
|
@@ -54,7 +55,7 @@ export async function importModule(path) {
|
|
|
54
55
|
throw failed;
|
|
55
56
|
try {
|
|
56
57
|
ensureTypeScriptLoader();
|
|
57
|
-
return await import(path);
|
|
58
|
+
return esModuleNamespace(await import(path));
|
|
58
59
|
}
|
|
59
60
|
catch (error) {
|
|
60
61
|
const message = error instanceof Error ? error.message : "Unknown import error";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../../src/discovery/sandbox/driver.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../../src/discovery/sandbox/driver.ts"],"names":[],"mappings":"AAgEA,MAAM,WAAW,qBAAqB;IACpC,qIAAqI;IACrI,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,oNAAoN;IACpN,SAAS,EAAE,MAAM,CAAC;CACnB;AAOD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,EAAE,CAsB3E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAqH3E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA8CrE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAiGjF"}
|
|
@@ -56,6 +56,8 @@ const POST_SYNTH_MODULE = join(dirname(DISCOVERY_DIR), "lint", "post-synth.ts");
|
|
|
56
56
|
// file, so the driver's `setBuildParams` call below mutates the one object
|
|
57
57
|
// every bundled import observes.
|
|
58
58
|
const PARAMS_MODULE = join(dirname(DISCOVERY_DIR), "params.ts");
|
|
59
|
+
// #3897: a project file tsx compiled as CommonJS reads as its own namespace.
|
|
60
|
+
const INTEROP_MODULE = join(dirname(DISCOVERY_DIR), "module-interop.ts");
|
|
59
61
|
/** Escape a value for embedding as a JS/TS source-level literal. */
|
|
60
62
|
function lit(value) {
|
|
61
63
|
return JSON.stringify(value);
|
|
@@ -116,6 +118,7 @@ export function generateDriverSource(options) {
|
|
|
116
118
|
`import { classifyChildError } from ${lit(CHILD_ERRORS_MODULE)};`,
|
|
117
119
|
`import { getProvenance } from ${lit(PROVENANCE_MODULE)};`,
|
|
118
120
|
`import { setBuildParams } from ${lit(PARAMS_MODULE)};`,
|
|
121
|
+
`import { esModuleNamespace } from ${lit(INTEROP_MODULE)};`,
|
|
119
122
|
`import { writeSync } from "node:fs";`,
|
|
120
123
|
``,
|
|
121
124
|
`const BUILD_ROOT = ${lit(buildRoot)};`,
|
|
@@ -163,7 +166,7 @@ export function generateDriverSource(options) {
|
|
|
163
166
|
` const errors = [];`,
|
|
164
167
|
];
|
|
165
168
|
for (const file of files) {
|
|
166
|
-
lines.push(` importing = ${lit(file)};`, ` try {`, ` const mod = await import(${lit(file)});`, ` modules.push({ file: ${lit(file)}, exports: mod });`, ` } catch (err) {`, ` errors.push(classifyChildError(${lit(file)}, err).toJSON());`, ` }`, ` importing = undefined;`);
|
|
169
|
+
lines.push(` importing = ${lit(file)};`, ` try {`, ` const mod = esModuleNamespace(await import(${lit(file)}));`, ` modules.push({ file: ${lit(file)}, exports: mod });`, ` } catch (err) {`, ` errors.push(classifyChildError(${lit(file)}, err).toJSON());`, ` }`, ` importing = undefined;`);
|
|
167
170
|
}
|
|
168
171
|
lines.push(``, ` let entities = new Map();`, ` try {`, ` entities = collectEntities(modules, BUILD_ROOT);`, ` } catch (err) {`, ` errors.push(classifyChildError(errFile(err), err, "resolution").toJSON());`, ` await send({ entitySet: { entities: [] }, errors, provenanceByName: {} });`, ` return;`, ` }`, ``,
|
|
169
172
|
// Recorded BEFORE resolveAttrRefs/encode — not for the parent's own
|
|
@@ -198,6 +201,7 @@ export function generateConfigDriverSource(configPath) {
|
|
|
198
201
|
return [
|
|
199
202
|
`import { classifyChildError } from ${lit(CHILD_ERRORS_MODULE)};`,
|
|
200
203
|
`import { scanConfigWireSafety } from ${lit(CONFIG_WIRE_MODULE)};`,
|
|
204
|
+
`import { esModuleNamespace } from ${lit(INTEROP_MODULE)};`,
|
|
201
205
|
`import { writeSync } from "node:fs";`,
|
|
202
206
|
``,
|
|
203
207
|
...sendFunctionSource(),
|
|
@@ -205,7 +209,7 @@ export function generateConfigDriverSource(configPath) {
|
|
|
205
209
|
`async function main() {`,
|
|
206
210
|
` let namespace;`,
|
|
207
211
|
` try {`,
|
|
208
|
-
` namespace = await import(${lit(configPath)});`,
|
|
212
|
+
` namespace = esModuleNamespace(await import(${lit(configPath)}));`,
|
|
209
213
|
` } catch (err) {`,
|
|
210
214
|
` await send({ kind: "chant-config", ok: false, error: classifyChildError(${lit(configPath)}, err).toJSON() });`,
|
|
211
215
|
` return;`,
|
|
@@ -273,6 +277,7 @@ export function generatePolicyDriverSource(policyPaths) {
|
|
|
273
277
|
`import { classifyChildError } from ${lit(CHILD_ERRORS_MODULE)};`,
|
|
274
278
|
`import { decodePolicyBuildResult, scanPolicyDiagnostics, PolicyWireError } from ${lit(POLICY_WIRE_MODULE)};`,
|
|
275
279
|
`import { runPostSynthChecks, isPostSynthCheck } from ${lit(POST_SYNTH_MODULE)};`,
|
|
280
|
+
`import { esModuleNamespace } from ${lit(INTEROP_MODULE)};`,
|
|
276
281
|
`import { writeSync } from "node:fs";`,
|
|
277
282
|
``,
|
|
278
283
|
...sendFunctionSource(),
|
|
@@ -315,7 +320,7 @@ export function generatePolicyDriverSource(policyPaths) {
|
|
|
315
320
|
// the same order `loadPolicyChecks` collects in, so the diagnostics sequence
|
|
316
321
|
// matches the in-process one exactly.
|
|
317
322
|
for (const policyPath of policyPaths) {
|
|
318
|
-
lines.push(` try {`, ` const mod = await import(${lit(policyPath)});`, ` for (const value of Object.values(mod)) {`, ` if (isPostSynthCheck(value)) checks.push(guard(value, ${lit(policyPath)}));`, ` }`, ` } catch (err) {`, ` await fail(${lit(policyPath)}, err, "import");`, ` return;`, ` }`);
|
|
323
|
+
lines.push(` try {`, ` const mod = esModuleNamespace(await import(${lit(policyPath)}));`, ` for (const value of Object.values(mod)) {`, ` if (isPostSynthCheck(value)) checks.push(guard(value, ${lit(policyPath)}));`, ` }`, ` } catch (err) {`, ` await fail(${lit(policyPath)}, err, "import");`, ` return;`, ` }`);
|
|
319
324
|
}
|
|
320
325
|
lines.push(``, ` let diagnostics;`, ` try {`, ` diagnostics = runPostSynthChecks(checks, buildResult, request.env ?? undefined);`, ` } catch (err) {`, ` await fail("", err, "resolution");`, ` return;`, ` }`, ``, ` await send({ kind: "chant-policy", ok: true, diagnostics });`, `}`, ``,
|
|
321
326
|
// Registered synchronously at module top level — see the doc above on why
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lexicon-module.d.ts","sourceRoot":"","sources":["../src/lexicon-module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;
|
|
1
|
+
{"version":3,"file":"lexicon-module.d.ts","sourceRoot":"","sources":["../src/lexicon-module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAQH;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1F,mEAAmE;AACnE,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAExE;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,GAAG,MAAM,EAAE,CAAC;AAC/E,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,GAAG,SAAS,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;AAiBvG;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EACxC,OAAO,EAAE,MAAM,EACf,SAAS,SAAM,GACd;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAgBrC;AAED;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,SAAS,kBAAkB,EAAE,GAAG,SAAS,EAClD,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBxB;AAMD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAM3G;AAED,2DAA2D;AAC3D,wBAAgB,sBAAsB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAE5D;AAED,sGAAsG;AACtG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAElE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAElE;AAED,6CAA6C;AAC7C,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAYpG;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,MAAsB,EAC/B,KAAK,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAe,GAC/C,MAAM,CAKR;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAe,GAC/C,MAAM,EAAE,CAEV;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI9D;AAUD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAsB,GAAG,MAAM,GAAG,SAAS,CAMpG;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAW1H"}
|
package/dist/lexicon-module.js
CHANGED
|
@@ -27,6 +27,7 @@ import { createRequire } from "node:module";
|
|
|
27
27
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
28
28
|
import { pathToFileURL } from "node:url";
|
|
29
29
|
import { ensureTypeScriptLoader } from "./ts-loader.js";
|
|
30
|
+
import { esModuleNamespace } from "./module-interop.js";
|
|
30
31
|
/** The lexicon name an entry declares, whichever form it takes. */
|
|
31
32
|
export function lexiconDeclarationName(entry) {
|
|
32
33
|
return typeof entry === "string" ? entry : entry.name;
|
|
@@ -156,7 +157,7 @@ export async function importLexiconModule(name) {
|
|
|
156
157
|
return undefined;
|
|
157
158
|
try {
|
|
158
159
|
ensureTypeScriptLoader();
|
|
159
|
-
return (await import(path));
|
|
160
|
+
return esModuleNamespace((await import(path)));
|
|
160
161
|
}
|
|
161
162
|
catch (err) {
|
|
162
163
|
throw new Error(`lexicon "${name}" is declared with module ${path}, which could not be loaded: ` +
|
|
@@ -227,7 +228,7 @@ export function resolveFromProject(spec, fromDir = process.cwd()) {
|
|
|
227
228
|
export async function importLexiconPackage(spec, fromDir = process.cwd()) {
|
|
228
229
|
try {
|
|
229
230
|
ensureTypeScriptLoader();
|
|
230
|
-
return (await import(spec));
|
|
231
|
+
return esModuleNamespace((await import(spec)));
|
|
231
232
|
}
|
|
232
233
|
catch (err) {
|
|
233
234
|
const pkg = packageNameOf(spec);
|
|
@@ -236,6 +237,6 @@ export async function importLexiconPackage(spec, fromDir = process.cwd()) {
|
|
|
236
237
|
const resolved = resolveFromProject(spec, fromDir);
|
|
237
238
|
if (resolved === undefined)
|
|
238
239
|
throw err;
|
|
239
|
-
return (await import(pathToFileURL(resolved).href));
|
|
240
|
+
return esModuleNamespace((await import(pathToFileURL(resolved).href)));
|
|
240
241
|
}
|
|
241
242
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy-import.d.ts","sourceRoot":"","sources":["../../src/lint/policy-import.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"policy-import.d.ts","sourceRoot":"","sources":["../../src/lint/policy-import.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAQH;;;;GAIG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD;AAED,iEAAiE;AACjE,wBAAgB,6BAA6B,IAAI,OAAO,CAEvD;AAED,4IAA4I;AAC5I,wBAAgB,mCAAmC,IAAI,IAAI,CAE1D;AAED,2HAA2H;AAC3H,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE5D;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAQ3F"}
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* that might forget to ask.
|
|
24
24
|
*/
|
|
25
25
|
import { ensureTypeScriptLoader } from "../ts-loader.js";
|
|
26
|
+
import { esModuleNamespace } from "../module-interop.js";
|
|
26
27
|
/** Whether this process must run project policy modules inside the sandbox boundary instead of here. */
|
|
27
28
|
let armed = false;
|
|
28
29
|
/**
|
|
@@ -58,5 +59,5 @@ export async function importPolicyModule(policyPath) {
|
|
|
58
59
|
throw new Error(`Refusing to import the policy module ${policyPath} into the chant process under --sandbox: it is project-authored code and must be imported inside the sandbox boundary (packages/core/src/lint/policy-sandbox.ts's runProjectPolicies).`);
|
|
59
60
|
}
|
|
60
61
|
ensureTypeScriptLoader();
|
|
61
|
-
return (await import(policyPath));
|
|
62
|
+
return esModuleNamespace((await import(policyPath)));
|
|
62
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-loader.d.ts","sourceRoot":"","sources":["../../src/lint/rule-loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"rule-loader.d.ts","sourceRoot":"","sources":["../../src/lint/rule-loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAiDvC;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAsC5E"}
|
package/dist/lint/rule-loader.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readdirSync, existsSync } from "fs";
|
|
2
2
|
import { dirname, join, resolve } from "path";
|
|
3
3
|
import { ensureTypeScriptLoader } from "../ts-loader.js";
|
|
4
|
+
import { esModuleNamespace } from "../module-interop.js";
|
|
4
5
|
/**
|
|
5
6
|
* Type guard to check if a value conforms to the LintRule interface.
|
|
6
7
|
*/
|
|
@@ -74,7 +75,7 @@ export async function loadLocalRules(projectDir) {
|
|
|
74
75
|
let mod;
|
|
75
76
|
try {
|
|
76
77
|
ensureTypeScriptLoader();
|
|
77
|
-
mod = await import(filePath);
|
|
78
|
+
mod = esModuleNamespace(await import(filePath));
|
|
78
79
|
}
|
|
79
80
|
catch (err) {
|
|
80
81
|
throw new Error(`Failed to load rule file "${file}": ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The namespace a project module was authored with, whichever module format
|
|
3
|
+
* tsx compiled it to (#3897).
|
|
4
|
+
*
|
|
5
|
+
* A `.ts` file with no `"type": "module"` `package.json` above it is compiled
|
|
6
|
+
* by tsx as CommonJS: `exports.default = ...` plus a non-enumerable
|
|
7
|
+
* `__esModule: true` marker. The compiled CLI loads project files with a
|
|
8
|
+
* native `import()`, and Node hands a CommonJS module back as a namespace
|
|
9
|
+
* whose `default` is the whole `module.exports`. So `export default x` arrives
|
|
10
|
+
* as `namespace.default.default`. Under the source CLI tsx's ESM loader sits
|
|
11
|
+
* in front of the `import()` and does this interop itself, which is why the
|
|
12
|
+
* nesting only showed up once the CLI ran compiled (#3879).
|
|
13
|
+
*
|
|
14
|
+
* When `namespace.default` carries the `__esModule` marker, the module's real
|
|
15
|
+
* namespace is that exports object: this returns a copy of it with the marker
|
|
16
|
+
* left out. Anything else, including an ES module whose default export is an
|
|
17
|
+
* object that happens to have a `default` key, is returned unchanged.
|
|
18
|
+
*/
|
|
19
|
+
export declare function esModuleNamespace<T extends Record<string, unknown>>(namespace: T): T;
|
|
20
|
+
//# sourceMappingURL=module-interop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-interop.d.ts","sourceRoot":"","sources":["../src/module-interop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,CAAC,CAWpF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The namespace a project module was authored with, whichever module format
|
|
3
|
+
* tsx compiled it to (#3897).
|
|
4
|
+
*
|
|
5
|
+
* A `.ts` file with no `"type": "module"` `package.json` above it is compiled
|
|
6
|
+
* by tsx as CommonJS: `exports.default = ...` plus a non-enumerable
|
|
7
|
+
* `__esModule: true` marker. The compiled CLI loads project files with a
|
|
8
|
+
* native `import()`, and Node hands a CommonJS module back as a namespace
|
|
9
|
+
* whose `default` is the whole `module.exports`. So `export default x` arrives
|
|
10
|
+
* as `namespace.default.default`. Under the source CLI tsx's ESM loader sits
|
|
11
|
+
* in front of the `import()` and does this interop itself, which is why the
|
|
12
|
+
* nesting only showed up once the CLI ran compiled (#3879).
|
|
13
|
+
*
|
|
14
|
+
* When `namespace.default` carries the `__esModule` marker, the module's real
|
|
15
|
+
* namespace is that exports object: this returns a copy of it with the marker
|
|
16
|
+
* left out. Anything else, including an ES module whose default export is an
|
|
17
|
+
* object that happens to have a `default` key, is returned unchanged.
|
|
18
|
+
*/
|
|
19
|
+
export function esModuleNamespace(namespace) {
|
|
20
|
+
// `in` before the read: a vitest factory mock throws on reading an export
|
|
21
|
+
// it does not define, and most modules have no `default` at all.
|
|
22
|
+
if (namespace === null || typeof namespace !== "object" || !("default" in namespace))
|
|
23
|
+
return namespace;
|
|
24
|
+
const exported = namespace.default;
|
|
25
|
+
if (!isCommonJsEsModule(exported))
|
|
26
|
+
return namespace;
|
|
27
|
+
const unwrapped = {};
|
|
28
|
+
for (const key of Object.keys(exported)) {
|
|
29
|
+
if (key !== "__esModule")
|
|
30
|
+
unwrapped[key] = exported[key];
|
|
31
|
+
}
|
|
32
|
+
return unwrapped;
|
|
33
|
+
}
|
|
34
|
+
/** A CommonJS `module.exports` that a compiler marked as an ES module. */
|
|
35
|
+
function isCommonJsEsModule(value) {
|
|
36
|
+
return (value !== null &&
|
|
37
|
+
(typeof value === "object" || typeof value === "function") &&
|
|
38
|
+
value.__esModule === true);
|
|
39
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../src/op/discover.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAIxC,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../src/op/discover.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAIxC,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAI1E,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AA6GD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA6CrF;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,kBAAkB,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IACzC,wCAAwC;IACxC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,2GAA2G;IAC3G,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA2C/F"}
|
package/dist/op/discover.js
CHANGED
|
@@ -6,6 +6,7 @@ import { hasDiscoveryMarkerSync } from "../discovery/files.js";
|
|
|
6
6
|
import { resolveDiscoveryGlobs } from "../config.js";
|
|
7
7
|
import { isStewardDeclaration } from "./steward.js";
|
|
8
8
|
import { ensureTypeScriptLoader } from "../ts-loader.js";
|
|
9
|
+
import { esModuleNamespace } from "../module-interop.js";
|
|
9
10
|
async function findGitRoot(cwd) {
|
|
10
11
|
const rt = getRuntime();
|
|
11
12
|
const result = await rt.spawn(["git", "rev-parse", "--show-toplevel"], { cwd });
|
|
@@ -132,7 +133,7 @@ export async function discoverOps(opts) {
|
|
|
132
133
|
for (const filePath of files) {
|
|
133
134
|
try {
|
|
134
135
|
ensureTypeScriptLoader();
|
|
135
|
-
const mod = (await import(filePath));
|
|
136
|
+
const mod = esModuleNamespace((await import(filePath)));
|
|
136
137
|
const exported = opsExportedBy(mod);
|
|
137
138
|
// A file may hold a steward and no Op (#2731): the steward sits beside
|
|
138
139
|
// the Ops it runs, and `discoverStewards` reads it from here.
|
|
@@ -180,7 +181,7 @@ export async function discoverStewards(opts) {
|
|
|
180
181
|
let mod;
|
|
181
182
|
try {
|
|
182
183
|
ensureTypeScriptLoader();
|
|
183
|
-
mod = (await import(filePath));
|
|
184
|
+
mod = esModuleNamespace((await import(filePath)));
|
|
184
185
|
}
|
|
185
186
|
catch (err) {
|
|
186
187
|
errors.push(`${filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -561,7 +561,13 @@ export declare function opWaveHooksAtBase(options: {
|
|
|
561
561
|
}): OpWaveHooksFile;
|
|
562
562
|
/** Whether `policy` lets a wave with these members through without an approval. */
|
|
563
563
|
export declare function opWaveNeedsApproval(policy: OpWaveGatePolicy, members: readonly OpWaveMember[]): boolean;
|
|
564
|
-
/**
|
|
564
|
+
/**
|
|
565
|
+
* Plan, decide and apply one wave, or one part of a wide wave: the wave
|
|
566
|
+
* engine's loop (`runWaveLoop`, #3766) over this module's steps. An apply
|
|
567
|
+
* that comes back `replan` plans the wave again (#3821), at most
|
|
568
|
+
* {@link OP_WAVE_MAX_REPLANS} times; a `rest` wave goes on through the waves
|
|
569
|
+
* its plan command found.
|
|
570
|
+
*/
|
|
565
571
|
export declare function runOpWave(options: RunOpWaveOptions): Promise<RunOpWaveResult>;
|
|
566
572
|
/** How many times one wave job plans again when its apply asks to (#3821). */
|
|
567
573
|
export declare const OP_WAVE_MAX_REPLANS = 10;
|