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
3 changes: 2 additions & 1 deletion .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
"allow": ["URL", "URLSearchParams"]
}
],
"jsx-a11y/interactive-supports-focus": "off"
"jsx-a11y/interactive-supports-focus": "off",
"unicorn/prefer-spread": "error"
},
"overrides": [
{
Expand Down
6 changes: 3 additions & 3 deletions apps/docs-mcp/src/agent/query-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ function canonicalizeDocsUrl(value: string): string {

url.search = "";
url.hash = "";
url.pathname = url.pathname
const pathname = url.pathname
.replace(TRAILING_MD_RE, "")
.replace(TRAILING_SLASHES_RE, "")
.concat("/");
.replace(TRAILING_SLASHES_RE, "");
url.pathname = `${pathname}/`;
return url.toString();
}

Expand Down
8 changes: 8 additions & 0 deletions docs/contributing/coding-guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ product-specific implementation patterns.
write `return await` in async functions so callers stay in async stack
traces. Type-aware correctness rules that still have a backlog are turned
off at the root; #1573 tracks enabling them.
- Copy and combine iterables with spread: `[...set]`, `[...a, ...b]`,
`[...path, key]`. `unicorn/prefer-spread` enforces this repo-wide and
autofixes `Array.from(x)` and `.slice()`. Spread rejects non-iterables at
compile time and at runtime, where `Array.from` accepts any `{ length }`
object. Keep `Array.from` for array-likes and `Array.from({ length: n }, fn)`.
To walk a string's UTF-16 code units, use `split("")`. Spread a string only
when you mean code points, and suppress `typescript/no-misused-spread` with
the reason.
- Don't pass an async callback to `.catch()`. Use `try`/`await`/`catch`, or a
wrapper such as `withDashboardErrors(ctx, fn)`. The `no-async-catch-callback`
ast-grep rule enforces this.
Expand Down
4 changes: 1 addition & 3 deletions packages/cli/script/generate-docs-sections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,7 @@ function getSubcommandLabel(route: RouteInfo): string {
const parts = cmd.path.split(" ");
return parts.at(-1) ?? route.name;
});
return Array.from(new Set(raw))
.sort((a, b) => a.localeCompare(b))
.join(", ");
return [...new Set(raw)].sort((a, b) => a.localeCompare(b)).join(", ");
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/explore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const PAGINATION_KEY = "explore";

const DATASETS = buildDatasetParser(
["errors", "spans", "tracemetrics", "logs", "replays"],
Array.from(LEGACY_TRANSACTION_DATASETS),
[...LEGACY_TRANSACTION_DATASETS],
);

// ---------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/issue/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ async function resolveAllIssues(
const orgs = new Set(resolved.map((r) => r.org as string));
if (orgs.size > 1) {
throw new ValidationError(
`Cannot merge issues across organizations (${Array.from(orgs).join(", ")}).\n\n` +
`Cannot merge issues across organizations (${[...orgs].join(", ")}).\n\n` +
"All issues must belong to the same organization.",
);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/span/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ function extractExtraApiFields(
expanded.delete(known);
}

return expanded.size > 0 ? Array.from(expanded) : undefined;
return expanded.size > 0 ? [...expanded] : undefined;
}

/** Accepted values for the --sort flag (matches trace list) */
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/trace/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ const PROGRESS_THRESHOLD = 20;
export function flattenSpanTree(spans: TraceSpan[]): TraceSpan[] {
const result: TraceSpan[] = [];
// Reverse so the first child is popped first (depth-first order)
const stack = Array.from(spans).reverse();
const stack = [...spans].reverse();
let span = stack.pop();
while (span) {
result.push(span);
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/lib/api/attribute-pagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function encodeBookmark(
return (
CURSOR_PREFIX +
Buffer.from(
JSON.stringify({ cursor: cursor ?? null, seen: Array.from(seen) }),
JSON.stringify({ cursor: cursor ?? null, seen: [...seen] }),
).toString("base64url")
);
}
Expand Down Expand Up @@ -126,7 +126,7 @@ export async function fetchAttributeTypePage(
pending || next
? encodeBookmark(
next,
Array.from(seen).filter((key) => keysOnPage.has(key)),
[...seen].filter((key) => keysOnPage.has(key)),
)
: undefined,
};
Expand Down Expand Up @@ -170,6 +170,6 @@ export function attributeCursorAfter(
const keysOnPage = page.pageKeys.get(pageId(replay)) ?? new Set<string>();
return encodeBookmark(
replay,
Array.from(seen).filter((key) => keysOnPage.has(key)),
[...seen].filter((key) => keysOnPage.has(key)),
);
}
4 changes: 1 addition & 3 deletions packages/cli/src/lib/api/issues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,7 @@ export async function listIssuesPaginated(
): Promise<PaginatedResponse<SentryIssue[]>> {
// Prefer the API's repeated `project` parameter. A lone slug retains the
// search-query fallback for callers that have not resolved project identity.
const projects = options.projects?.length
? Array.from(options.projects)
: undefined;
const projects = options.projects?.length ? [...options.projects] : undefined;
const projectFilter =
projects === undefined && projectSlug ? `project:${projectSlug}` : "";
const fullQuery = [projectFilter, options.query].filter(Boolean).join(" ");
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/api/traces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ async function fetchSpansPage(
const fullQuery = [projectFilter, options.query].filter(Boolean).join(" ");

const fields = options.extraFields?.length
? SPAN_FIELDS.concat(options.extraFields)
? [...SPAN_FIELDS, ...options.extraFields]
: SPAN_FIELDS;

let projectParam: string | undefined;
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/lib/attribute-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export function parseAttributeFilter(
);
}
return {
query: hasTerms.size > 0 ? Array.from(hasTerms).join(" ") : undefined,
query: hasTerms.size > 0 ? [...hasTerms].join(" ") : undefined,
name,
};
}
Expand Down Expand Up @@ -109,5 +109,5 @@ export function parseAttributeTypes(
}
types.add(value);
}
return types.size > 0 ? Array.from(types) : undefined;
return types.size > 0 ? [...types] : undefined;
}
3 changes: 2 additions & 1 deletion packages/cli/src/lib/banner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ export const FULL_BANNER_LINES: BannerLine[] = colorize(BANNER_ROWS_FULL);
export function bannerLinesWidth(lines: readonly BannerLine[]): number {
let max = 0;
for (const { content } of lines) {
const width = Array.from(content).length;
// oxlint-disable-next-line typescript/no-misused-spread -- width is measured in code points
const width = [...content].length;
if (width > max) {
max = width;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/lib/complete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ export function completeOrgSlugs(partial: string, suffix = ""): Completion[] {
}

const nameMap = new Map(orgs.map((o) => [o.slug, o.name]));
const matched = fuzzyMatch(partial, Array.from(nameMap.keys()), {
const matched = fuzzyMatch(partial, [...nameMap.keys()], {
mode: "search",
});

Expand Down Expand Up @@ -402,7 +402,7 @@ export function completeProjectSlugs(
}

const nameMap = new Map(projects.map((p) => [p.projectSlug, p.projectName]));
const matched = fuzzyMatch(projectPartial, Array.from(nameMap.keys()), {
const matched = fuzzyMatch(projectPartial, [...nameMap.keys()], {
mode: "search",
});

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/formatters/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export function parseFieldsList(input: string): string[] {
const { parse } = _require(
"../../generated/field-list-grammar.js",
) as typeof import("../../generated/field-list-grammar.js");
return Array.from(new Set(parse(input)));
return [...new Set(parse(input))];
}

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/lib/formatters/pixel-canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@ function* normalizeBitmapText(text: string): Iterable<string> {
.replace(COMBINING_MARK_RE, "");
if (
decomposed.length > 0 &&
Array.from(decomposed).every((candidate) => hasSpleenGlyph(candidate))
// oxlint-disable-next-line typescript/no-misused-spread -- glyphs are looked up per code point
[...decomposed].every((candidate) => hasSpleenGlyph(candidate))
) {
yield* decomposed;
continue;
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/hex-id-recovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ export type FuzzyLookupAdapter = (ctx: LookupContext) => Promise<string[]>;

/** Deduplicate while preserving order, then filter by prefix + suffix. */
function filterByCandidate(ids: string[], candidate: HexCandidate): string[] {
const unique = Array.from(new Set(ids));
const unique = [...new Set(ids)];
return unique.filter(
(id) =>
id.startsWith(candidate.prefix) &&
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/init/clack-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ const FEATURE_DISPLAY_ORDER = [

/** Sort features into the canonical order used by summaries and final output. */
export function sortFeatures(features: string[]): string[] {
return features.slice().sort((a, b) => {
return [...features].sort((a, b) => {
const ai = FEATURE_DISPLAY_ORDER.indexOf(a);
const bi = FEATURE_DISPLAY_ORDER.indexOf(b);
return (
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/init/interactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const FEATURE_SELECTION_CONTEXT =
"Based on your project, these features are available to set up.";

function sortFeatureOptions(features: string[]): string[] {
return features.slice().sort((a, b) => {
return [...features].sort((a, b) => {
const rankDifference =
(DEFAULT_FEATURE_RANK.get(a) ?? Number.MAX_SAFE_INTEGER) -
(DEFAULT_FEATURE_RANK.get(b) ?? Number.MAX_SAFE_INTEGER);
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/init/wizard-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ async function handleSuspendedStep(
"done",
]);
const history = stepHistory.get(stepId) ?? [];
const previousPhases = history.slice();
const previousPhases = [...history];
history.push(summarizeToolPhaseForHistory(payload, phase, toolResult));
stepHistory.set(stepId, history);

Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/lib/metrics-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ export function resolveMetricField(

const match = metrics.find((m) => m.name === metricName);
if (!match) {
const candidateNames = Array.from(
new Set(metrics.map((m) => m.name).filter((n) => n.length > 0)),
);
const candidateNames = [
...new Set(metrics.map((m) => m.name).filter((n) => n.length > 0)),
];
const suggestions = fuzzyMatch(metricName, candidateNames, {
maxResults: 5,
});
Expand Down
8 changes: 3 additions & 5 deletions packages/cli/src/lib/resolve-target.ts
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,7 @@ async function findSimilarProjectsAcrossOrgs(

// Deduplicate slugs so fuzzyMatch doesn't waste slots on the same
// project appearing in multiple orgs.
const uniqueSlugs = Array.from(new Set(allProjects.map((p) => p.slug)));
const uniqueSlugs = [...new Set(allProjects.map((p) => p.slug))];
const slugMatches = fuzzyMatch(slug, uniqueSlugs, { maxResults: 5 });

// When a display name is provided (input contained spaces), also
Expand All @@ -816,7 +816,7 @@ async function findSimilarProjectsAcrossOrgs(
nameToSlug.set(p.name, p.slug);
}
}
const uniqueNames = Array.from(nameToSlug.keys());
const uniqueNames = [...nameToSlug.keys()];
const matchedNames = fuzzyMatch(displayName, uniqueNames, {
maxResults: 5,
});
Expand All @@ -826,9 +826,7 @@ async function findSimilarProjectsAcrossOrgs(
}

// Merge slug matches and name matches, preferring slug matches.
const mergedSlugs = Array.from(
new Set(slugMatches.concat(nameMatchedSlugs)),
);
const mergedSlugs = [...new Set([...slugMatches, ...nameMatchedSlugs])];

// Expand matched slugs back to org-qualified entries (may include
// the same slug from multiple orgs — that's correct for suggestions).
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/test/lib/api/infrastructure.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -778,11 +778,11 @@ describe("rawApiRequest binary handling", () => {
expect(result.status).toBe(200);
expect(result.body).toBeInstanceOf(Uint8Array);
const body = result.body as Uint8Array;
expect(Array.from(body.slice(0, 8))).toEqual([
expect([...body.subarray(0, 8)]).toEqual([
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
]);
// Must NOT contain the UTF-8 replacement sequence EF BF BD
expect(Array.from(body.slice(0, 3))).not.toEqual([0xef, 0xbf, 0xbd]);
expect([...body.subarray(0, 3)]).not.toEqual([0xef, 0xbf, 0xbd]);
expect(body).toEqual(pngBytes);
});

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/test/lib/db/sqlite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ describe.each(DRIVERS)("sqlite adapter [%s driver]", (kind) => {
const row = db.query("SELECT data FROM t").get() as {
data: Uint8Array;
};
expect(Array.from(row.data)).toEqual([1, 2, 3, 255]);
expect([...row.data]).toEqual([1, 2, 3, 255]);
db.close();
});

Expand Down
3 changes: 2 additions & 1 deletion packages/cli/test/lib/delta-upgrade.mocked.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ function mockFetch(
const BINARY_NAME = getPlatformBinaryName();

function versionHex(version: string): string {
return Array.from(version)
return version
.split("")
.map((c) => c.charCodeAt(0).toString(16).padStart(2, "0"))
.join("");
}
Expand Down
6 changes: 4 additions & 2 deletions packages/cli/test/lib/delta-upgrade.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ describe("extractStableChain", () => {
* Converts each char to its hex code to produce valid [0-9a-f]+ output.
*/
function versionToHex(version: string): string {
return Array.from(version)
return version
.split("")
.map((c) => c.charCodeAt(0).toString(16).padStart(2, "0"))
.join("");
}
Expand Down Expand Up @@ -943,7 +944,8 @@ describe("resolveStableChain", () => {
* Reuses the same approach as the extractStableChain tests above.
*/
function versionHex(version: string): string {
return Array.from(version)
return version
.split("")
.map((c) => c.charCodeAt(0).toString(16).padStart(2, "0"))
.join("");
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/test/lib/dsn/code-scanner.property.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const contentArb = array(constantFrom(...BENIGN_CHARS), {
const DSN_RE =
/https?:\/\/[a-z0-9]+(?::[a-z0-9]+)?@[a-z0-9.-]+(?:\.[a-z]+|:[0-9]+)\/\d+/gi;
function referenceMatches(content: string): string[] {
return Array.from(new Set(Array.from(content.matchAll(DSN_RE), (m) => m[0])));
return [...new Set(Array.from(content.matchAll(DSN_RE), (m) => m[0]))];
}

describe("property: extractDsnsFromContent fast-path invariance", () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/test/lib/formatters/output.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ describe("renderCommandOutput", () => {
render(w, png, config, { json: false });
expect(w.chunks).toHaveLength(1);
expect(w.chunks[0]).toBeInstanceOf(Uint8Array);
expect(Array.from(w.bytes)).toEqual([
expect([...w.bytes]).toEqual([
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
]);
// No trailing newline added by the text formatter path
Expand All @@ -139,7 +139,7 @@ describe("renderCommandOutput", () => {
};
const bytes = new Uint8Array([0xff, 0x00, 0x80]);
render(w, bytes, config, { json: true });
expect(Array.from(w.bytes)).toEqual([0xff, 0x00, 0x80]);
expect([...w.bytes]).toEqual([0xff, 0x00, 0x80]);
});

test("jsonExclude strips fields from JSON output", () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/test/lib/help.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ function maxLineWidth(banner: string): number {
0,
...stripAnsi(banner)
.split("\n")
.map((line) => Array.from(line).length),
// oxlint-disable-next-line typescript/no-misused-spread -- width is measured in code points
.map((line) => [...line].length),
);
}

Expand Down
6 changes: 3 additions & 3 deletions packages/cli/test/lib/init/ui/wizard-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ describe("WizardStore step progress", () => {
test("pre-populates the checklist from CHECKLIST_VISIBLE_STEPS", () => {
const store = new WizardStore();
const snapshot = store.getSnapshot();
expect(snapshot.steps.map((entry) => entry.id)).toEqual(
CHECKLIST_VISIBLE_STEPS.slice(),
);
expect(snapshot.steps.map((entry) => entry.id)).toEqual([
...CHECKLIST_VISIBLE_STEPS,
]);
expect(snapshot.steps.every((entry) => entry.status === "pending")).toBe(
true,
);
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/test/lib/sdk-invoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ describe("parseOutput: binary output", () => {
]);
const result = parseOutput<Uint8Array>(undefined, [png]);
expect(result).toBeInstanceOf(Uint8Array);
expect(Array.from(result)).toEqual([
expect([...result]).toEqual([
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
]);
});
Expand All @@ -247,7 +247,7 @@ describe("parseOutput: binary output", () => {
"AB",
new Uint8Array([0x00, 0xff]),
]);
expect(Array.from(result)).toEqual([0x41, 0x42, 0x00, 0xff]);
expect([...result]).toEqual([0x41, 0x42, 0x00, 0xff]);
});

test("still JSON-parses textual chunks", () => {
Expand Down
Loading
Loading