feat: scaffold CLI and minimal issue list (task 0001)
Tracer bullet for gitea-axi: runnable npm package on axi-sdk-js with gitea-js as the sole HTTP layer, ESM on Node 20+. - issue list with --state/--limit, default fields, count line from X-Total-Count, type=issues guard, explicit empty state, and next-step suggestions - repo context detection from the git origin remote (SSH/scp/HTTPS), tea credential discovery with the three-way login-matching split, and -R/--repo and --login overrides (flag > env > auto) - token retrieval via tea login helper get: tea's login list JSON carries no token (ADR 0001 amended) - full AxiError classification table with path-based 404 split, TOON errors on stdout, exit codes 0/1/2 - test mode (GITEA_AXI_API_URL/TOKEN/REPO) suppressing subprocesses, fixture server, and vitest suites driving the CLI seam (50 tests)
This commit is contained in:
54
src/fields.ts
Normal file
54
src/fields.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { relativeTime } from "./time.js";
|
||||
|
||||
export interface ExtractContext {
|
||||
now: Date;
|
||||
}
|
||||
|
||||
export interface FieldDef<T> {
|
||||
name: string;
|
||||
extract: (raw: T, context: ExtractContext) => unknown;
|
||||
}
|
||||
|
||||
function pluckPath(raw: unknown, path: string): unknown {
|
||||
let value: unknown = raw;
|
||||
for (const key of path.split(".")) {
|
||||
if (typeof value !== "object" || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
value = (value as Record<string, unknown>)[key];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function pluck<T>(name: string, path: string = name): FieldDef<T> {
|
||||
return { name, extract: (raw) => pluckPath(raw, path) ?? "" };
|
||||
}
|
||||
|
||||
export function lowercased<T>(name: string, path: string = name): FieldDef<T> {
|
||||
return {
|
||||
name,
|
||||
extract: (raw) => String(pluckPath(raw, path) ?? "").toLowerCase(),
|
||||
};
|
||||
}
|
||||
|
||||
export function relativeTimeField<T>(name: string, path: string): FieldDef<T> {
|
||||
return {
|
||||
name,
|
||||
extract: (raw, context) => {
|
||||
const value = pluckPath(raw, path);
|
||||
return relativeTime(typeof value === "string" ? value : undefined, context.now);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function extractRow<T>(
|
||||
raw: T,
|
||||
fields: FieldDef<T>[],
|
||||
context: ExtractContext,
|
||||
): Record<string, unknown> {
|
||||
const row: Record<string, unknown> = {};
|
||||
for (const field of fields) {
|
||||
row[field.name] = field.extract(raw, context);
|
||||
}
|
||||
return row;
|
||||
}
|
||||
Reference in New Issue
Block a user