Skip to content
77 changes: 46 additions & 31 deletions packages-private/scripting-utils/source/yaml/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,19 @@ export async function createOrUpdateExtConfig(
) {
const extConfigDoc = doc ?? new Document({});

// Keep this up so that it appears before other sections, and users see it right away.
if (config.web !== undefined) {
buildWeb(extConfigDoc, config.web);
}

config.hooks ??= {};
config.operations ??= { workerProcess: [] };
config.runtimeManifest ??= { packages: {} };

await buildHooks(extConfigDoc, config.hooks);
buildOperations(extConfigDoc, config.operations);
buildRuntimeManifest(extConfigDoc, config.runtimeManifest);

if (config.web !== undefined) {
buildWeb(extConfigDoc, config.web);
if (config.operations !== undefined) {
buildOperations(extConfigDoc, config.operations);
}
buildRuntimeManifest(extConfigDoc, config.runtimeManifest);

await writeExtConfig(path, extConfigDoc);
return extConfigDoc;
Expand Down Expand Up @@ -113,38 +115,46 @@ function buildActionDefinition(action: ActionDefinition) {
* @param operations - The operations to build
*/
function buildOperations(extConfig: Document, operations: Operations) {
getOrCreateMap(extConfig, ["operations"], {
onBeforeCreate: (pair) => {
pair.key.spaceBefore = true;
},
});

const workerProcess = getOrCreateSeq(
extConfig,
["operations", "workerProcess"],
{
const ourOps = operations.workerProcess ?? [];
if (ourOps.length > 0) {
getOrCreateMap(extConfig, ["operations"], {
onBeforeCreate: (pair) => {
pair.key.commentBefore =
" These worker processes definitions are auto-generated. Do not remove or manually edit.";
pair.key.spaceBefore = true;
},
},
);
});

// Clear existing items to rebuild from scratch
workerProcess.items = [];
const workerProcess = getOrCreateSeq(
extConfig,
["operations", "workerProcess"],
{
onBeforeCreate: (pair) => {
pair.key.commentBefore =
" These worker processes definitions are auto-generated. Do not remove or manually edit.";
},
},
);

const ourOps = operations.workerProcess ?? [];
workerProcess.items.push(
...ourOps.map((op) => {
const map = new YAMLMap();
map.set("type", op.type);
map.set("impl", op.impl);
workerProcess.items = [];
workerProcess.items.push(
...ourOps.map((op) => {
const map = new YAMLMap();
map.set("type", op.type);
map.set("impl", op.impl);

return map;
}),
);
return map;
}),
);
} else if (extConfig.hasIn(["operations", "workerProcess"])) {
extConfig.deleteIn(["operations", "workerProcess"]);
}

if (operations.view !== undefined) {
getOrCreateMap(extConfig, ["operations"], {
onBeforeCreate: (pair) => {
pair.key.spaceBefore = true;
},
});

const view = getOrCreateSeq(extConfig, ["operations", "view"]);

// Seed defaults only when the user has not already populated the view list.
Expand All @@ -160,6 +170,11 @@ function buildOperations(extConfig: Document, operations: Operations) {
);
}
}

const operationsMap = extConfig.getIn(["operations"]);
if (operationsMap instanceof YAMLMap && operationsMap.items.length === 0) {
extConfig.deleteIn(["operations"]);
}
}

/**
Expand Down
9 changes: 7 additions & 2 deletions packages-private/scripting-utils/test/yaml/codegen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ runtimeManifest:
const fileContent = await readFile(configPath, "utf-8");
expect(fileContent).toContain("existing-hook");
expect(fileContent).toContain("new-hook");
expect(fileContent).not.toContain("workerProcess: []");
},
);
});
Expand All @@ -127,7 +128,7 @@ runtimeManifest:

// Should have default empty structures
expect(doc.has("hooks")).toBe(true);
expect(doc.has("operations")).toBe(true);
expect(doc.has("operations")).toBe(false);
expect(doc.has("runtimeManifest")).toBe(true);
});
});
Expand Down Expand Up @@ -165,7 +166,10 @@ runtimeManifest:
new Document({}),
);

expect(doc.has("operations")).toBe(true);
expect(doc.has("operations")).toBe(false);

const fileContent = await readFile(configPath, "utf-8");
expect(fileContent).not.toContain("workerProcess: []");
});
});

Expand Down Expand Up @@ -533,6 +537,7 @@ hooks:
expect(fileContent).toContain("view:");
expect(fileContent).toContain("type: web");
expect(fileContent).toContain("impl: index.html");
expect(fileContent).not.toContain("workerProcess: []");
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { createOrUpdateExtConfig } from "@aio-commerce-sdk/scripting-utils/yaml"
import { readYamlFile } from "@aio-commerce-sdk/scripting-utils/yaml/index";
import { consola } from "consola";
import { formatTree } from "consola/utils";
import stringify from "safe-stable-stringify";

import {
BACKEND_UI_EXTENSION_POINT_ID,
Expand Down Expand Up @@ -294,7 +295,7 @@ export async function generateRegistrationActionFile(
);
const content = template.replace(
REGISTRATION_JSON_PLACEHOLDER,
`const registration = ${JSON.stringify(registration)};`,
`const registration = ${stringify(registration)};`,
);

const formattedContent = await prettierFormat(content, actionPath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,18 @@ const SANDBOX_VALUES = [
"allow-downloads",
"allow-modals",
"allow-popups",
] as const;
] as const satisfies string[];

type SandboxValue = (typeof SANDBOX_VALUES)[number];

function isSandboxValue(value: string): value is SandboxValue {
return SANDBOX_VALUES.includes(value as SandboxValue);
}

const SandboxSchema = v.pipe(
v.string('Expected a string value for "sandbox"'),
v.check(
(val) =>
val
.split(" ")
.every((value) =>
(SANDBOX_VALUES as readonly string[]).includes(value),
),
(val) => val.split(" ").every(isSandboxValue),
`sandbox must contain only single-space-separated values from: ${SANDBOX_VALUES.map((t) => `"${t}"`).join(", ")}`,
),
);
Expand Down Expand Up @@ -114,6 +115,7 @@ const sandboxDisplayIframeCheck = v.forward(
["sandbox"],
);

// TODO: Cleanup after https://github.com/open-circle/valibot/issues/1459
function withSandboxDisplayIframeCheck<
TSchema extends v.BaseSchema<
unknown,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,25 @@ describe("AdminUiSdkSchema", () => {
expect(result.success).toBe(true);
});

test("iframe-enabled mass action allows sandbox", () => {
const result = v.safeParse(AdminUiSdkSchema, {
registration: {
order: {
massActions: [
{
actionId: "app::action",
label: "Action",
path: "#/action",
displayIframe: true,
sandbox: "allow-modals",
},
],
},
},
});
expect(result.success).toBe(true);
});

test("registration with grid columns — all 5 type values", () => {
for (const type of [
"boolean",
Expand Down Expand Up @@ -163,6 +182,25 @@ describe("AdminUiSdkSchema", () => {
}
});

test("iframe-enabled view button allows sandbox", () => {
const result = v.safeParse(AdminUiSdkSchema, {
registration: {
order: {
viewButtons: [
{
buttonId: "app::btn",
label: "Btn",
path: "#/btn",
displayIframe: true,
sandbox: "allow-modals",
},
],
},
},
});
expect(result.success).toBe(true);
});

test("registration with custom fees including applyFeeOnLastCreditMemo", () => {
const result = v.safeParse(AdminUiSdkSchema, {
registration: {
Expand Down