@theholocron/cli 5.0.0-alpha.110 → 5.0.0-alpha.111
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +74 -63
- package/dist/cli.mjs.map +1 -1
- package/package.json +4 -4
package/dist/cli.mjs
CHANGED
|
@@ -2167,14 +2167,16 @@ describe("resolveToken", () => {
|
|
|
2167
2167
|
});
|
|
2168
2168
|
|
|
2169
2169
|
it("throws AuthError with a helpful message when nothing is set", () => {
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2170
|
+
const err = (() => {
|
|
2171
|
+
try {
|
|
2172
|
+
resolveToken({ env: {}, keyring: noKeyring });
|
|
2173
|
+
} catch (e) {
|
|
2174
|
+
return e;
|
|
2175
|
+
}
|
|
2176
|
+
})();
|
|
2177
|
+
expect(err).toBeInstanceOf(AuthError);
|
|
2178
|
+
expect((err as Error).message).toMatch(/${inputs.tokenEnv}/);
|
|
2179
|
+
expect((err as Error).message).toMatch(/holocron auth set ${inputs.slug}/);
|
|
2178
2180
|
});
|
|
2179
2181
|
});
|
|
2180
2182
|
`;
|
|
@@ -2218,24 +2220,24 @@ export class ${inputs.capabilityClass} {
|
|
|
2218
2220
|
//#endregion
|
|
2219
2221
|
//#region src/commands/plugin-create/templates/capability-test.ts
|
|
2220
2222
|
function render$14(inputs) {
|
|
2221
|
-
const
|
|
2222
|
-
return `import { describe, it } from "vitest";
|
|
2223
|
+
const factoryName = `create${inputs.vendorName}RestClient`;
|
|
2224
|
+
return `import { describe, expect, it } from "vitest";
|
|
2223
2225
|
|
|
2224
2226
|
import { ${inputs.capabilityClass} } from "../capabilities/${inputs.capability}.js";
|
|
2225
|
-
import { ${
|
|
2227
|
+
import { ${factoryName} } from "../rest.js";
|
|
2226
2228
|
import { stubFetch } from "./helpers.js";
|
|
2227
2229
|
|
|
2228
2230
|
// Stub client used by the constructor smoke test. Real capability
|
|
2229
2231
|
// tests replace this with per-method stubs once implementations land.
|
|
2230
2232
|
function makeCapability() {
|
|
2231
2233
|
const stub = stubFetch([]);
|
|
2232
|
-
const rest =
|
|
2234
|
+
const rest = ${factoryName}({ token: "t", fetch: stub.fetch });
|
|
2233
2235
|
return new ${inputs.capabilityClass}(() => rest);
|
|
2234
2236
|
}
|
|
2235
2237
|
|
|
2236
2238
|
describe("${inputs.capabilityClass}", () => {
|
|
2237
2239
|
it("constructs with a REST client", () => {
|
|
2238
|
-
makeCapability();
|
|
2240
|
+
expect(makeCapability().key).toBe("${inputs.capability}");
|
|
2239
2241
|
});
|
|
2240
2242
|
|
|
2241
2243
|
// TODO: implement one test per ${inputs.capability} capability method
|
|
@@ -2251,14 +2253,11 @@ describe("${inputs.capabilityClass}", () => {
|
|
|
2251
2253
|
//#endregion
|
|
2252
2254
|
//#region src/commands/plugin-create/templates/eslint-config.ts
|
|
2253
2255
|
function render$13(_inputs) {
|
|
2254
|
-
return `import
|
|
2256
|
+
return `import type { Linter } from "eslint";
|
|
2255
2257
|
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
ignores: ["dist/**", "coverage/**"],
|
|
2260
|
-
},
|
|
2261
|
-
];
|
|
2258
|
+
import root from "../../eslint.config.js";
|
|
2259
|
+
|
|
2260
|
+
export default [...root, { ignores: ["dist/**", "coverage/**"] }] satisfies Linter.Config[];
|
|
2262
2261
|
`;
|
|
2263
2262
|
}
|
|
2264
2263
|
//#endregion
|
|
@@ -2341,10 +2340,17 @@ describe("AUTH_HINT", () => {
|
|
|
2341
2340
|
//#endregion
|
|
2342
2341
|
//#region src/commands/plugin-create/templates/package-json.ts
|
|
2343
2342
|
function render$10(inputs) {
|
|
2343
|
+
const keywords = [
|
|
2344
|
+
inputs.capability,
|
|
2345
|
+
"holocron",
|
|
2346
|
+
"plugin",
|
|
2347
|
+
inputs.slug
|
|
2348
|
+
].sort((a, b) => a.localeCompare(b));
|
|
2344
2349
|
return JSON.stringify({
|
|
2345
2350
|
name: `@theholocron/holocron-plugin-${inputs.slug}`,
|
|
2346
2351
|
version: "2.0.0-alpha.1",
|
|
2347
2352
|
description: `Holocron plugin for ${inputs.vendorName}. Implements the ${inputs.capability} capability against ${inputs.vendorName}'s REST API, plus exports verifyToken + AUTH_HINT for \`holocron auth\`.`,
|
|
2353
|
+
keywords,
|
|
2348
2354
|
homepage: `https://github.com/theholocron/holocron/tree/main/packages/holocron-plugin-${inputs.slug}#readme`,
|
|
2349
2355
|
bugs: "https://github.com/theholocron/holocron/issues",
|
|
2350
2356
|
repository: {
|
|
@@ -2354,9 +2360,13 @@ function render$10(inputs) {
|
|
|
2354
2360
|
},
|
|
2355
2361
|
license: "MIT",
|
|
2356
2362
|
author: "Newton Koumantzelis",
|
|
2363
|
+
sideEffects: false,
|
|
2357
2364
|
type: "module",
|
|
2358
|
-
|
|
2359
|
-
|
|
2365
|
+
exports: { ".": {
|
|
2366
|
+
types: "./dist/index.d.mts",
|
|
2367
|
+
import: "./dist/index.mjs",
|
|
2368
|
+
default: "./dist/index.mjs"
|
|
2369
|
+
} },
|
|
2360
2370
|
scripts: {
|
|
2361
2371
|
"delivery.build": "tsdown",
|
|
2362
2372
|
"sourceQuality.staticAnalysis": "holocron run sourceQuality.staticAnalysis --",
|
|
@@ -2368,33 +2378,26 @@ function render$10(inputs) {
|
|
|
2368
2378
|
peerDependencies: { "@theholocron/cli": "workspace:*" },
|
|
2369
2379
|
devDependencies: {
|
|
2370
2380
|
"@theholocron/cli": "workspace:*",
|
|
2371
|
-
"@theholocron/tsconfig": "catalog:",
|
|
2372
|
-
"@
|
|
2381
|
+
"@theholocron/tsconfig": "catalog:configs",
|
|
2382
|
+
"@types/node": "catalog:",
|
|
2373
2383
|
"@vitest/coverage-v8": "catalog:",
|
|
2374
2384
|
eslint: "catalog:",
|
|
2375
2385
|
globals: "catalog:",
|
|
2376
|
-
typescript: "catalog:",
|
|
2377
|
-
vitest: "catalog:",
|
|
2378
2386
|
tsdown: "catalog:",
|
|
2379
|
-
tsx: "catalog:"
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
access: "public",
|
|
2383
|
-
main: "./dist/index.mjs",
|
|
2384
|
-
types: "./dist/index.d.mts",
|
|
2385
|
-
exports: { ".": {
|
|
2386
|
-
types: "./dist/index.d.mts",
|
|
2387
|
-
import: "./dist/index.mjs",
|
|
2388
|
-
default: "./dist/index.mjs"
|
|
2389
|
-
} }
|
|
2387
|
+
tsx: "catalog:",
|
|
2388
|
+
typescript: "catalog:",
|
|
2389
|
+
vitest: "catalog:"
|
|
2390
2390
|
},
|
|
2391
|
-
|
|
2392
|
-
|
|
2391
|
+
engines: { node: ">=22" },
|
|
2392
|
+
publishConfig: { access: "public" },
|
|
2393
|
+
files: ["dist"]
|
|
2394
|
+
}, null, 2) + "\n";
|
|
2393
2395
|
}
|
|
2394
2396
|
//#endregion
|
|
2395
2397
|
//#region src/commands/plugin-create/templates/plugin-index.ts
|
|
2396
2398
|
function render$9(inputs) {
|
|
2397
2399
|
const clientClass = `${inputs.vendorName}RestClient`;
|
|
2400
|
+
const factoryName = `create${inputs.vendorName}RestClient`;
|
|
2398
2401
|
const capabilityInterface = inputs.capability.charAt(0).toUpperCase() + inputs.capability.slice(1);
|
|
2399
2402
|
return `/**
|
|
2400
2403
|
* \`@theholocron/holocron-plugin-${inputs.slug}\` — entrypoint.
|
|
@@ -2411,7 +2414,7 @@ function render$9(inputs) {
|
|
|
2411
2414
|
|
|
2412
2415
|
import { resolveToken, type ResolveTokenInput } from "./auth.js";
|
|
2413
2416
|
import { ${inputs.capabilityClass} } from "./capabilities/${inputs.capability}.js";
|
|
2414
|
-
import { ${clientClass} } from "./rest.js";
|
|
2417
|
+
import { ${factoryName}, type ${clientClass} } from "./rest.js";
|
|
2415
2418
|
|
|
2416
2419
|
export interface ${inputs.vendorName}PluginOptions extends ResolveTokenInput {
|
|
2417
2420
|
/** Override base URL for tests. */
|
|
@@ -2432,10 +2435,12 @@ export function createContext(options: ${inputs.vendorName}PluginOptions): Plugi
|
|
|
2432
2435
|
options,
|
|
2433
2436
|
rest: () => {
|
|
2434
2437
|
if (rest) return rest;
|
|
2435
|
-
const restOpts:
|
|
2438
|
+
const restOpts: { token: string; baseUrl?: string; fetch?: typeof fetch } = {
|
|
2439
|
+
token: resolveToken(options),
|
|
2440
|
+
};
|
|
2436
2441
|
if (options.baseUrl !== undefined) restOpts.baseUrl = options.baseUrl;
|
|
2437
2442
|
if (options.fetch !== undefined) restOpts.fetch = options.fetch;
|
|
2438
|
-
return (rest =
|
|
2443
|
+
return (rest = ${factoryName}(restOpts));
|
|
2439
2444
|
},
|
|
2440
2445
|
};
|
|
2441
2446
|
}
|
|
@@ -2469,7 +2474,7 @@ export const AUTH_HINT =
|
|
|
2469
2474
|
// ── Public re-exports ────────────────────────────────────────────────
|
|
2470
2475
|
|
|
2471
2476
|
export * from "./auth.js";
|
|
2472
|
-
export { ${clientClass} } from "./rest.js";
|
|
2477
|
+
export { type ${clientClass}, ${factoryName} } from "./rest.js";
|
|
2473
2478
|
export { ${inputs.capabilityClass} } from "./capabilities/${inputs.capability}.js";
|
|
2474
2479
|
export { verifyToken } from "./verify-token.js";
|
|
2475
2480
|
export type { VerifyTokenResult, VerifyTokenSuccess, VerifyTokenFailure } from "./verify-token.js";
|
|
@@ -2546,15 +2551,26 @@ Not yet published; capability methods are stubs.
|
|
|
2546
2551
|
//#endregion
|
|
2547
2552
|
//#region src/commands/plugin-create/templates/rest.ts
|
|
2548
2553
|
function render$7(inputs) {
|
|
2549
|
-
|
|
2554
|
+
const factoryName = `create${inputs.vendorName}RestClient`;
|
|
2555
|
+
const clientType = `${inputs.vendorName}RestClient`;
|
|
2556
|
+
return `import { createRestClient, type RestClient } from "@theholocron/cli";
|
|
2550
2557
|
|
|
2551
|
-
export type {
|
|
2558
|
+
export type { RestClient };
|
|
2552
2559
|
|
|
2553
|
-
|
|
2560
|
+
/**
|
|
2561
|
+
* TODO: narrow this to the specific ${inputs.vendorName} endpoints this
|
|
2562
|
+
* plugin's capability needs (see \`@theholocron/holocron-plugin-axiom\`'s
|
|
2563
|
+
* \`rest.ts\` for the pattern: an interface with one method per endpoint,
|
|
2564
|
+
* built internally via \`createRestClient\`). Starts as an alias for the
|
|
2565
|
+
* generic client so the scaffold compiles end to end immediately.
|
|
2566
|
+
*/
|
|
2567
|
+
export type ${clientType} = RestClient;
|
|
2568
|
+
|
|
2569
|
+
export function ${factoryName}(opts: {
|
|
2554
2570
|
\ttoken: string;
|
|
2555
2571
|
\tbaseUrl?: string;
|
|
2556
2572
|
\tfetch?: typeof fetch;
|
|
2557
|
-
}):
|
|
2573
|
+
}): ${clientType} {
|
|
2558
2574
|
\treturn createRestClient({
|
|
2559
2575
|
\t\tbaseUrl: opts.baseUrl ?? "${inputs.baseUrl}",
|
|
2560
2576
|
\t\ttoken: opts.token,
|
|
@@ -2627,7 +2643,7 @@ describe("${factoryName}", () => {
|
|
|
2627
2643
|
function render$5(inputs) {
|
|
2628
2644
|
return JSON.stringify({
|
|
2629
2645
|
display: `Holocron Plugin: ${inputs.vendorName}`,
|
|
2630
|
-
extends: "@tsconfig/node-lts
|
|
2646
|
+
extends: "@theholocron/tsconfig/node-lts",
|
|
2631
2647
|
compilerOptions: {
|
|
2632
2648
|
baseUrl: "./",
|
|
2633
2649
|
outDir: "./dist",
|
|
@@ -2635,7 +2651,7 @@ function render$5(inputs) {
|
|
|
2635
2651
|
},
|
|
2636
2652
|
include: ["src/**/*.ts"],
|
|
2637
2653
|
exclude: ["node_modules", "dist"]
|
|
2638
|
-
}, null,
|
|
2654
|
+
}, null, 2) + "\n";
|
|
2639
2655
|
}
|
|
2640
2656
|
//#endregion
|
|
2641
2657
|
//#region src/commands/plugin-create/templates/tsdown-config.ts
|
|
@@ -2751,6 +2767,7 @@ console.log("Done. Fill in capability method calls above before shipping.");
|
|
|
2751
2767
|
// ── helpers ────────────────────────────────────────────────────────
|
|
2752
2768
|
|
|
2753
2769
|
/** Wrap a capability call, print ERROR + hint on failure. */
|
|
2770
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- called once a real capability check replaces the TODO example above
|
|
2754
2771
|
async function runStep(body) {
|
|
2755
2772
|
try {
|
|
2756
2773
|
await body();
|
|
@@ -2790,7 +2807,7 @@ function hintFor(message) {
|
|
|
2790
2807
|
//#endregion
|
|
2791
2808
|
//#region src/commands/plugin-create/templates/verify-token.ts
|
|
2792
2809
|
function render$2(inputs) {
|
|
2793
|
-
const
|
|
2810
|
+
const factoryName = `create${inputs.vendorName}RestClient`;
|
|
2794
2811
|
return `/**
|
|
2795
2812
|
* \`verifyToken\` — plugin-level export used by \`holocron auth set\` +
|
|
2796
2813
|
* \`holocron auth check\`. Hits a lightweight whoami-style endpoint
|
|
@@ -2807,7 +2824,7 @@ function render$2(inputs) {
|
|
|
2807
2824
|
* \`/me\`, \`/account\`.
|
|
2808
2825
|
*/
|
|
2809
2826
|
|
|
2810
|
-
import { ${
|
|
2827
|
+
import { ${factoryName} } from "./rest.js";
|
|
2811
2828
|
|
|
2812
2829
|
export interface VerifyTokenSuccess {
|
|
2813
2830
|
ok: true;
|
|
@@ -2834,10 +2851,10 @@ export interface VerifyTokenOptions {
|
|
|
2834
2851
|
}
|
|
2835
2852
|
|
|
2836
2853
|
export async function verifyToken(token: string, opts: VerifyTokenOptions = {}): Promise<VerifyTokenResult> {
|
|
2837
|
-
const restOpts:
|
|
2854
|
+
const restOpts: { token: string; baseUrl?: string; fetch?: typeof fetch } = { token };
|
|
2838
2855
|
if (opts.baseUrl !== undefined) restOpts.baseUrl = opts.baseUrl;
|
|
2839
2856
|
if (opts.fetch !== undefined) restOpts.fetch = opts.fetch;
|
|
2840
|
-
const rest =
|
|
2857
|
+
const rest = ${factoryName}(restOpts);
|
|
2841
2858
|
try {
|
|
2842
2859
|
const me = await rest.request<MeResponse>("/me");
|
|
2843
2860
|
// Optional chaining because \`me\` is \`undefined\` on 204 / empty body.
|
|
@@ -2863,18 +2880,14 @@ describe("verifyToken", () => {
|
|
|
2863
2880
|
const stub = stubFetch([{ status: 200, body: { email: "user@example.com" } }]);
|
|
2864
2881
|
const result = await verifyToken("token", { fetch: stub.fetch });
|
|
2865
2882
|
expect(result.ok).toBe(true);
|
|
2866
|
-
|
|
2867
|
-
expect(result.subject).toMatch(/user@example.com/);
|
|
2868
|
-
}
|
|
2883
|
+
expect((result as { ok: boolean; subject?: string }).subject).toMatch(/user@example.com/);
|
|
2869
2884
|
});
|
|
2870
2885
|
|
|
2871
2886
|
it("returns ok:false with the error message on 401", async () => {
|
|
2872
2887
|
const stub = stubFetch([{ status: 401, body: { messages: ["Invalid token"] } }]);
|
|
2873
2888
|
const result = await verifyToken("bad", { fetch: stub.fetch });
|
|
2874
2889
|
expect(result.ok).toBe(false);
|
|
2875
|
-
|
|
2876
|
-
expect(result.message).toMatch(/→ 401/);
|
|
2877
|
-
}
|
|
2890
|
+
expect((result as { ok: boolean; message?: string }).message).toMatch(/→ 401/);
|
|
2878
2891
|
});
|
|
2879
2892
|
|
|
2880
2893
|
it("returns ok:false when the network layer throws", async () => {
|
|
@@ -2883,9 +2896,7 @@ describe("verifyToken", () => {
|
|
|
2883
2896
|
};
|
|
2884
2897
|
const result = await verifyToken("t", { fetch: throwing });
|
|
2885
2898
|
expect(result.ok).toBe(false);
|
|
2886
|
-
|
|
2887
|
-
expect(result.message).toMatch(/network down/);
|
|
2888
|
-
}
|
|
2899
|
+
expect((result as { ok: boolean; message?: string }).message).toMatch(/network down/);
|
|
2889
2900
|
});
|
|
2890
2901
|
});
|
|
2891
2902
|
`;
|
|
@@ -2959,7 +2970,7 @@ const TEMPLATES = [
|
|
|
2959
2970
|
render
|
|
2960
2971
|
},
|
|
2961
2972
|
{
|
|
2962
|
-
path: "eslint.config.
|
|
2973
|
+
path: "eslint.config.ts",
|
|
2963
2974
|
render: render$13
|
|
2964
2975
|
},
|
|
2965
2976
|
{
|