Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 3 additions & 40 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
ignorePatternsRead,
matchesIgnore as matchesIgnorePatterns,
} from "./resource-parse.ts";
import { VALID_RESOURCE_TYPES } from "./types.ts";
import { resolveResourceTypes, VALID_TYPE_ARGS } from "./resource-type-args.ts";

// ─────────────────────────────────────────────────────────────────────────────
// CLI Argument Parsing
Expand All @@ -19,23 +19,8 @@ export interface ApplyFilter {
resourceIds?: string[]; // Pull only specific remote resource IDs
}

// Group aliases: expand a shorthand into multiple resource types
const RESOURCE_GROUP_MAP: Record<string, ResourceType[]> = {
simulations: [
"personalities",
"scenarios",
"simulations",
"simulationSuites",
],
};

// Path-based aliases: folder paths to resource types
const RESOURCE_PATH_MAP: Record<string, ResourceType> = {
"simulations/personalities": "personalities",
"simulations/scenarios": "scenarios",
"simulations/tests": "simulations",
"simulations/suites": "simulationSuites",
};
// The --type aliases (groups like "simulations", paths like
// "simulations/tests") live in resource-type-args.ts.

const SLUG_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;

Expand Down Expand Up @@ -67,28 +52,6 @@ function parseEnvironment(): Environment {
return envArg;
}

// Resolve a type argument into resource types (handles groups, paths, and direct types)
function resolveResourceTypes(arg: string): ResourceType[] | null {
// Check group aliases first (e.g., "simulations" → all 4 simulation types)
if (RESOURCE_GROUP_MAP[arg]) {
return RESOURCE_GROUP_MAP[arg];
}
// Check path-based aliases (e.g., "simulations/personalities" → ["personalities"])
if (RESOURCE_PATH_MAP[arg]) {
return [RESOURCE_PATH_MAP[arg]];
}
// Check direct resource type
if (VALID_RESOURCE_TYPES.includes(arg as ResourceType)) {
return [arg as ResourceType];
}
return null;
}

const VALID_TYPE_ARGS = [
...VALID_RESOURCE_TYPES,
...Object.keys(RESOURCE_GROUP_MAP),
...Object.keys(RESOURCE_PATH_MAP),
];

function parseFlags(): {
forceDelete: boolean;
Expand Down
62 changes: 62 additions & 0 deletions src/resource-type-args.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// How a `--type` argument maps to resource types. Config-free on purpose
// (like api-key.ts): config.ts parses argv and exits at import time, so the
// mapping lives here where setup.ts and tests can import it.

import type { ResourceType } from "./types.ts";
import { VALID_RESOURCE_TYPES } from "./types.ts";

// Group aliases: expand a shorthand into multiple resource types
const RESOURCE_GROUP_MAP: Record<string, ResourceType[]> = {
simulations: [
"personalities",
"scenarios",
"simulations",
"simulationSuites",
],
};

// Path-based aliases: folder paths to resource types
const RESOURCE_PATH_MAP: Record<string, ResourceType> = {
"simulations/personalities": "personalities",
"simulations/scenarios": "scenarios",
"simulations/tests": "simulations",
"simulations/suites": "simulationSuites",
};

// Resolve a type argument into resource types (handles groups, paths, and direct types)
export function resolveResourceTypes(arg: string): ResourceType[] | null {
// Check group aliases first (e.g., "simulations" → all 4 simulation types)
if (RESOURCE_GROUP_MAP[arg]) {
return RESOURCE_GROUP_MAP[arg];
}
// Check path-based aliases (e.g., "simulations/personalities" → ["personalities"])
if (RESOURCE_PATH_MAP[arg]) {
return [RESOURCE_PATH_MAP[arg]];
}
// Check direct resource type
if (VALID_RESOURCE_TYPES.includes(arg as ResourceType)) {
return [arg as ResourceType];
}
return null;
}

export const VALID_TYPE_ARGS = [
...VALID_RESOURCE_TYPES,
...Object.keys(RESOURCE_GROUP_MAP),
...Object.keys(RESOURCE_PATH_MAP),
];

/**
* The `--type` argument that names exactly one resource type. Usually the
* type itself, but `simulations` is also the group alias for all four
* simulation types, so the single type is spelled by its folder path,
* `simulations/tests`. Use this whenever a command builds a `--type` for one
* type, e.g. a single-resource pull with `--id`.
*/
export function typeArgFor(type: string): string {
if (!(type in RESOURCE_GROUP_MAP)) return type;
const pathAlias = Object.keys(RESOURCE_PATH_MAP).find(
(path) => RESOURCE_PATH_MAP[path] === type,
);
return pathAlias ?? type;
}
5 changes: 4 additions & 1 deletion src/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
resolveOrgApiKey,
} from "./api-key.ts";
import { updateEnvConnection } from "./bindings.ts";
import { typeArgFor } from "./resource-type-args.ts";
import searchableCheckbox, { BACK_SENTINEL } from "./searchableCheckbox.js";
import {
type DirectSetupOptions,
Expand Down Expand Up @@ -348,7 +349,9 @@ function invokePull(
"--force",
"--skip-bindings",
"--type",
typeKey,
// Not typeKey itself: "simulations" is also the group alias for all
// four simulation types, which a pull by --id refuses.
typeArgFor(typeKey),
...idArgs,
].join(" ");
execSync(cmd, { cwd: BASE_DIR, stdio: "inherit", env });
Expand Down
41 changes: 41 additions & 0 deletions tests/resource-type-args.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import assert from "node:assert/strict";
import test from "node:test";
import {
resolveResourceTypes,
typeArgFor,
} from "../src/resource-type-args.ts";
import { VALID_RESOURCE_TYPES } from "../src/types.ts";

// The setup wizard downloads what you pick one type at a time, with
// `pull.ts <org> --type <type> --id …`, and a pull by --id accepts exactly one
// type. It passed the internal type name, and "simulations" is also the
// group alias for all four simulation types, so choosing simulations in the
// wizard failed with "Single-resource pull requires exactly one resource
// type" and stopped setup partway.

test("typeArgFor names exactly one type for every resource type", () => {
for (const type of VALID_RESOURCE_TYPES) {
assert.deepEqual(resolveResourceTypes(typeArgFor(type)), [type], type);
}
});

test("typeArgFor spells simulations by its folder path", () => {
assert.equal(typeArgFor("simulations"), "simulations/tests");
});

test("typeArgFor leaves other types unchanged", () => {
assert.equal(typeArgFor("assistants"), "assistants");
});

test("--type simulations still selects every simulation type", () => {
assert.deepEqual(resolveResourceTypes("simulations"), [
"personalities",
"scenarios",
"simulations",
"simulationSuites",
]);
});

test("resolveResourceTypes rejects an unknown type", () => {
assert.equal(resolveResourceTypes("widgets"), null);
});
Loading