Skip to content

Commit cabdd97

Browse files
committed
fix(auto-pin-latest): handle auto-pin-latest
1 parent f46e6a6 commit cabdd97

9 files changed

Lines changed: 52 additions & 18 deletions

File tree

packages/cli/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,7 @@ async function collectRuntimePlanBareSpecifiers(
688688
}
689689
}
690690

691-
for (const specifier of plan.capabilities.allowedModules ?? []) {
691+
for (const specifier of plan.capabilities?.allowedModules ?? []) {
692692
if (isBareModuleSpecifier(specifier)) {
693693
specifiers.add(specifier);
694694
}

packages/core/src/codegen.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,10 @@ export class DefaultCodeGenerator implements CodeGenerator {
147147
return false;
148148
}
149149

150-
return typeof plan.capabilities === "object" && plan.capabilities !== null;
150+
return (
151+
plan.capabilities === undefined ||
152+
(typeof plan.capabilities === "object" && plan.capabilities !== null)
153+
);
151154
}
152155

153156
async transformPlan(

packages/ir/src/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export interface RuntimePlan {
168168
id: string;
169169
version: number;
170170
root: RuntimeNode;
171-
capabilities: RuntimeCapabilities;
171+
capabilities?: RuntimeCapabilities;
172172
state?: RuntimeStateModel;
173173
imports?: string[];
174174
moduleManifest?: RuntimeModuleManifest;
@@ -539,7 +539,10 @@ export function isRuntimePlan(value: unknown): value is RuntimePlan {
539539
return false;
540540
}
541541

542-
if (!isRuntimeCapabilities(value.capabilities)) {
542+
if (
543+
value.capabilities !== undefined &&
544+
!isRuntimeCapabilities(value.capabilities)
545+
) {
543546
return false;
544547
}
545548

packages/runtime/src/manager.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -226,16 +226,16 @@ export class DefaultRuntimeManager implements RuntimeManager {
226226
};
227227
}
228228

229+
const capabilities = plan.capabilities ?? {};
229230
const frame: ExecutionFrame = {
230231
startedAt: nowMs(),
231-
maxExecutionMs:
232-
plan.capabilities.maxExecutionMs ?? this.defaultMaxExecutionMs,
232+
maxExecutionMs: capabilities.maxExecutionMs ?? this.defaultMaxExecutionMs,
233233
maxComponentInvocations:
234-
plan.capabilities.maxComponentInvocations ??
234+
capabilities.maxComponentInvocations ??
235235
this.defaultMaxComponentInvocations,
236236
componentInvocations: 0,
237237
executionProfile:
238-
plan.capabilities.executionProfile ?? this.defaultExecutionProfile,
238+
capabilities.executionProfile ?? this.defaultExecutionProfile,
239239
};
240240

241241
const dependencies = await this.preflightPlanDependencies(
@@ -284,21 +284,21 @@ export class DefaultRuntimeManager implements RuntimeManager {
284284

285285
const state = this.resolveState(plan, stateOverride);
286286
const appliedActions: RuntimeAction[] = [];
287+
const capabilities = plan.capabilities ?? {};
287288

288289
const frame: ExecutionFrame = {
289290
startedAt: nowMs(),
290-
maxExecutionMs:
291-
plan.capabilities.maxExecutionMs ?? this.defaultMaxExecutionMs,
291+
maxExecutionMs: capabilities.maxExecutionMs ?? this.defaultMaxExecutionMs,
292292
maxComponentInvocations:
293-
plan.capabilities.maxComponentInvocations ??
293+
capabilities.maxComponentInvocations ??
294294
this.defaultMaxComponentInvocations,
295295
componentInvocations: 0,
296296
executionProfile:
297-
plan.capabilities.executionProfile ?? this.defaultExecutionProfile,
297+
capabilities.executionProfile ?? this.defaultExecutionProfile,
298298
signal,
299299
};
300300

301-
const maxImports = plan.capabilities.maxImports ?? this.defaultMaxImports;
301+
const maxImports = capabilities.maxImports ?? this.defaultMaxImports;
302302
const imports = plan.imports ?? [];
303303

304304
if (this.enableDependencyPreflight) {

packages/security/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ export class DefaultSecurityChecker implements SecurityChecker {
283283
}
284284

285285
const capabilityResult = this.checkCapabilities(
286-
plan.capabilities,
286+
plan.capabilities ?? {},
287287
moduleManifest,
288288
);
289289
issues.push(...capabilityResult.issues);
@@ -658,7 +658,7 @@ export class DefaultSecurityChecker implements SecurityChecker {
658658
}
659659
}
660660

661-
for (const specifier of plan.capabilities.allowedModules ?? []) {
661+
for (const specifier of plan.capabilities?.allowedModules ?? []) {
662662
if (this.isBareSpecifier(specifier)) {
663663
requiredSpecifiers.add(specifier);
664664
}

tests/codegen.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ test("codegen parses RuntimePlan JSON output directly", async () => {
3636
assert.equal(plan.version, 3);
3737
assert.equal(plan.specVersion, "runtime-plan/v1");
3838
assert.equal(plan.state?.initial.count, 0);
39-
assert.equal(plan.capabilities.maxExecutionMs, 1200);
39+
assert.equal(plan.capabilities?.maxExecutionMs, 1200);
4040
assert.equal(plan.metadata?.sourcePrompt, "Counter plan");
4141
});
4242

@@ -53,7 +53,7 @@ test("codegen falls back to section root when no JSON payload exists", async ()
5353
throw new Error("expected fallback element node");
5454
}
5555
assert.equal(plan.root.tag, "section");
56-
assert.equal(plan.capabilities.domWrite, true);
56+
assert.equal(plan.capabilities?.domWrite, true);
5757
assert.equal(plan.specVersion, "runtime-plan/v1");
5858
});
5959

tests/ir.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,13 @@ test("runtime plan/type guards validate structures", () => {
102102
assert.equal(isRuntimeCapabilities(plan.capabilities), true);
103103
assert.equal(isRuntimeStateModel(plan.state), true);
104104
assert.equal(isRuntimePlan(plan), true);
105+
assert.equal(
106+
isRuntimePlan({
107+
...plan,
108+
capabilities: undefined,
109+
}),
110+
true,
111+
);
105112
assert.equal(
106113
isRuntimePlan({
107114
...plan,

tests/runtime.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,25 @@ test("renderPlanInBrowser renders runtime node HTML without mount target", async
596596
assert.equal(result.security.safe, true);
597597
});
598598

599+
test("renderPlanInBrowser accepts plans without capabilities", async () => {
600+
const plan: RuntimePlan = {
601+
specVersion: DEFAULT_RUNTIME_PLAN_SPEC_VERSION,
602+
id: "embed_runtime_plan_without_capabilities",
603+
version: 1,
604+
root: createElementNode("section", undefined, [createTextNode("hello")]),
605+
};
606+
607+
const result = await renderPlanInBrowser(plan, {
608+
runtimeOptions: {
609+
browserSourceSandboxMode: "none",
610+
},
611+
});
612+
613+
assert.match(result.html, /<section>/);
614+
assert.match(result.html, /hello/);
615+
assert.equal(result.security.safe, true);
616+
});
617+
599618
test("renderPlanInBrowser rejects plan when security policy fails", async () => {
600619
const plan: RuntimePlan = {
601620
specVersion: DEFAULT_RUNTIME_PLAN_SPEC_VERSION,

tests/security.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import {
1313
listSecurityProfiles,
1414
} from "../packages/security/src/index";
1515

16-
function createPlan(rootTag = "section"): RuntimePlan {
16+
function createPlan(rootTag = "section"): RuntimePlan & {
17+
capabilities: NonNullable<RuntimePlan["capabilities"]>;
18+
} {
1719
return {
1820
specVersion: DEFAULT_RUNTIME_PLAN_SPEC_VERSION,
1921
id: "plan_security_test",

0 commit comments

Comments
 (0)