@askrjs/cli 0.0.15 → 0.0.16
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/README.md +15 -24
- package/dist/analyze.js +1 -1
- package/dist/cli.js +7 -7
- package/dist/database.d.ts +18 -0
- package/dist/database.js +53 -0
- package/dist/generate.js +10 -4
- package/dist/{guardrails-D6KCKS-H.js → guardrails-XypvEZOT.js} +1 -1
- package/dist/{planner-BEfWRd0x.js → planner-CYi2Y-2W.js} +1 -5
- package/dist/{runner-BSs9Ow3t.js → runner-42nqhW9u.js} +16 -557
- package/dist/{runner-DetGSvGf.js → runner-CEt07ieG.js} +27 -5
- package/dist/skills/askr-ssr-ssg/SKILL.md +1 -3
- package/dist/templates/spa/src/pages/app/_layout.tsx +8 -11
- package/dist/templates/spa/src/pages/app/admin-home.tsx +132 -142
- package/dist/templates/spa/src/pages/app/agent-runs.tsx +26 -33
- package/dist/templates/spa/src/pages/public/home.tsx +9 -12
- package/dist/templates/ssg/src/components/site-shell.tsx +3 -4
- package/dist/templates/ssg/src/pages/about.tsx +11 -14
- package/dist/templates/ssg/src/pages/content.tsx +7 -10
- package/dist/templates/ssg/src/pages/home.tsx +3 -6
- package/dist/templates/startkit/src/components/app-sidebar.tsx +18 -23
- package/dist/templates/startkit/src/components/data-table.tsx +39 -41
- package/dist/templates/startkit/src/pages/workspace/dashboard.tsx +13 -16
- package/dist/update.js +1 -1
- package/package.json +2 -11
- package/dist/verify-hydration.d.ts +0 -35
- package/dist/verify-hydration.js +0 -325
package/README.md
CHANGED
|
@@ -45,11 +45,12 @@ unless you opt out with `--no-skills`.
|
|
|
45
45
|
- `askr doctor [--cwd <dir>] [--workspace <pattern>]... [--json]`
|
|
46
46
|
- `askr repair [--cwd <dir>] [--workspace <pattern>]... [--json]`
|
|
47
47
|
- `askr check [--cwd <dir>] [--workspace <pattern>]... [--json]`
|
|
48
|
+
- `askr database validate|generate [--database <name>] [--json]`
|
|
49
|
+
- `askr database migration create|status|plan|apply|resolve ...`
|
|
48
50
|
- `askr skills list`
|
|
49
51
|
- `askr skills install [--cwd <dir>] [--force]`
|
|
50
52
|
- `askr skills sync [--cwd <dir>]`
|
|
51
53
|
- `askr ssg --config <path> --output <dir> [--incremental]`
|
|
52
|
-
- `askr verify-hydration [--output ./dist] [--route <path>]...`
|
|
53
54
|
- `askr openapi [--entry ./src/api.ts] [--output ./openapi.yml] [--check]`
|
|
54
55
|
- `askr outdated [packages...] [--workspace <glob>] [--tag <tag>] [--json]`
|
|
55
56
|
- `askr update [packages...] [--workspace <glob>] [--tag <tag>] [--json]`
|
|
@@ -74,10 +75,8 @@ askr analyze --json --check
|
|
|
74
75
|
|
|
75
76
|
All diagnostics include a stable rule ID and workspace-relative source
|
|
76
77
|
location. The analyzer distinguishes canonical Askr imports from unrelated
|
|
77
|
-
same-named functions and recommends `<For>`
|
|
78
|
-
|
|
79
|
-
It reports eager `<For>`/`<Show>`/`<Case>` controls behind changing ternaries
|
|
80
|
-
while accepting conditionally mounted components with their own render scope.
|
|
78
|
+
same-named functions and only recommends `<For>` for state-backed reactive JSX
|
|
79
|
+
collections, so static transforms with `.map()` remain valid.
|
|
81
80
|
|
|
82
81
|
By default it transactionally applies only mechanical route-parameter and
|
|
83
82
|
plain-JSON JSX configuration fixes. `--check` is read-only for CI. Semantic
|
|
@@ -99,7 +98,17 @@ askr check
|
|
|
99
98
|
and static-analysis inspection. `repair` transactionally applies only safe
|
|
100
99
|
mechanical fixes and reports remaining semantic work. `check` requires clean
|
|
101
100
|
analysis before running the project's declared lint, typecheck, test, and build
|
|
102
|
-
scripts in order.
|
|
101
|
+
scripts in order. When `database/index.ts` exists, `check` first delegates
|
|
102
|
+
database validation to the project's installed `@askrjs/orm` tooling.
|
|
103
|
+
Generated projects expose that final gate as `npm run check`.
|
|
104
|
+
|
|
105
|
+
## Database tooling
|
|
106
|
+
|
|
107
|
+
`askr database ...` is a lazy front end. The CLI resolves
|
|
108
|
+
`@askrjs/orm/tooling` from the target project and delegates the complete
|
|
109
|
+
command, so schema generation and migration semantics always match the
|
|
110
|
+
project's installed ORM version. See the
|
|
111
|
+
[database command reference](./docs/database.md).
|
|
103
112
|
|
|
104
113
|
See the [project guardrails reference](./docs/guardrails.md) for command and JSON
|
|
105
114
|
contracts.
|
|
@@ -141,24 +150,6 @@ owned files so stale chunks and previous output paths are removed. Full and
|
|
|
141
150
|
incremental builds publish through a sibling staging directory, so route output,
|
|
142
151
|
metadata, assets, and sitemap artifacts change together or not at all.
|
|
143
152
|
|
|
144
|
-
## Hydration verification
|
|
145
|
-
|
|
146
|
-
`askr verify-hydration` builds SSG output, serves the generated route set, and
|
|
147
|
-
loads every successful metadata route in a real headless browser both with and
|
|
148
|
-
without JavaScript. It compares normalized tag-and-child topology under `#app`
|
|
149
|
-
after hydration, so text, classes, and mutable ARIA state do not create noise
|
|
150
|
-
while nodes migrating into the wrong sibling container fail with an actionable
|
|
151
|
-
static-versus-hydrated path diff.
|
|
152
|
-
|
|
153
|
-
```bash
|
|
154
|
-
askr verify-hydration
|
|
155
|
-
askr verify-hydration --route / --route /docs
|
|
156
|
-
askr verify-hydration --no-build --output ./dist
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
See the [hydration verification reference](./docs/verify-hydration.md) for
|
|
160
|
-
browser installation, timeout, route, and root-selector options.
|
|
161
|
-
|
|
162
153
|
## OpenAPI artifacts
|
|
163
154
|
|
|
164
155
|
`askr openapi` loads a TypeScript module whose default export exposes
|
package/dist/analyze.js
CHANGED
|
@@ -71,7 +71,7 @@ async function runAnalyzeCli(args = process.argv.slice(2), io = console, runtime
|
|
|
71
71
|
io.log(helpText.trimEnd());
|
|
72
72
|
return 0;
|
|
73
73
|
}
|
|
74
|
-
const report = await (runtime.analyze ?? (await import("./runner-
|
|
74
|
+
const report = await (runtime.analyze ?? (await import("./runner-42nqhW9u.js")).runAnalysis)({
|
|
75
75
|
cwd: parsed.cwd,
|
|
76
76
|
workspacePatterns: parsed.workspacePatterns,
|
|
77
77
|
check: parsed.check
|
package/dist/cli.js
CHANGED
|
@@ -36,12 +36,12 @@ function printHelp(io = console) {
|
|
|
36
36
|
io.log(" analyze Analyze Askr correctness and performance");
|
|
37
37
|
io.log(" check Run the complete project validation path");
|
|
38
38
|
io.log(" create Create a new Askr app from a template or product prompt");
|
|
39
|
+
io.log(" database Generate, validate, and migrate project databases");
|
|
39
40
|
io.log(" doctor Diagnose environment and Askr project health");
|
|
40
41
|
io.log(" generate Generate an @askrjs/fetch client from OpenAPI");
|
|
41
42
|
io.log(" openapi Generate or check an OpenAPI YAML artifact");
|
|
42
43
|
io.log(" skills Install or sync Askr agent skills");
|
|
43
44
|
io.log(" ssg Run static-site generation");
|
|
44
|
-
io.log(" verify-hydration Verify SSG DOM structure in a real browser");
|
|
45
45
|
io.log(" outdated List available dependency updates");
|
|
46
46
|
io.log(" repair Apply safe fixes and identify remaining semantic work");
|
|
47
47
|
io.log(" update Apply safe dependency updates");
|
|
@@ -60,11 +60,11 @@ function printHelp(io = console) {
|
|
|
60
60
|
io.log(" askr doctor");
|
|
61
61
|
io.log(" askr repair");
|
|
62
62
|
io.log(" askr check");
|
|
63
|
+
io.log(" askr database validate");
|
|
63
64
|
io.log(" askr skills install");
|
|
64
65
|
io.log(" askr openapi --check");
|
|
65
66
|
io.log(" askr skills review foundation --cwd ./candidate-app");
|
|
66
67
|
io.log(" askr ssg --config ./ssg.config.ts --output ./dist/static");
|
|
67
|
-
io.log(" askr verify-hydration --output ./dist --route /");
|
|
68
68
|
io.log(" askr outdated");
|
|
69
69
|
io.log(" askr update");
|
|
70
70
|
io.log(" askr upgrade");
|
|
@@ -87,12 +87,16 @@ async function runCli(args = process.argv.slice(2), io = console) {
|
|
|
87
87
|
const { runAddCli } = await import("./add.js");
|
|
88
88
|
return runAddCli(args.slice(1), io);
|
|
89
89
|
}
|
|
90
|
+
if (command === "database") {
|
|
91
|
+
const { runDatabaseCommand } = await import("./database.js");
|
|
92
|
+
return runDatabaseCommand(args.slice(1), io);
|
|
93
|
+
}
|
|
90
94
|
if (command === "analyze") {
|
|
91
95
|
const { runAnalyzeCli } = await import("./analyze.js");
|
|
92
96
|
return runAnalyzeCli(args.slice(1), io);
|
|
93
97
|
}
|
|
94
98
|
if (command === "check" || command === "doctor" || command === "repair") {
|
|
95
|
-
const { runGuardrailCli } = await import("./guardrails-
|
|
99
|
+
const { runGuardrailCli } = await import("./guardrails-XypvEZOT.js");
|
|
96
100
|
return runGuardrailCli(command, args.slice(1), io);
|
|
97
101
|
}
|
|
98
102
|
if (command === "generate") {
|
|
@@ -103,10 +107,6 @@ async function runCli(args = process.argv.slice(2), io = console) {
|
|
|
103
107
|
const { runSsgCli } = await import("./ssg.js");
|
|
104
108
|
return runSsgCli(args.slice(1), void 0, io);
|
|
105
109
|
}
|
|
106
|
-
if (command === "verify-hydration") {
|
|
107
|
-
const { runVerifyHydrationCli } = await import("./verify-hydration.js");
|
|
108
|
-
return runVerifyHydrationCli(args.slice(1), void 0, io);
|
|
109
|
-
}
|
|
110
110
|
if (command === "openapi") {
|
|
111
111
|
const { runOpenApiCli } = await import("./openapi.js");
|
|
112
112
|
return runOpenApiCli(args.slice(1), io);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/bin/database.d.ts
|
|
2
|
+
type CliIo = Pick<Console, "error" | "log">;
|
|
3
|
+
interface OrmTooling {
|
|
4
|
+
runDatabaseCli(args: readonly string[], options: {
|
|
5
|
+
readonly cwd: string;
|
|
6
|
+
readonly io: CliIo;
|
|
7
|
+
}): Promise<number>;
|
|
8
|
+
}
|
|
9
|
+
declare function loadOrmTooling(cwd: string): Promise<OrmTooling>;
|
|
10
|
+
declare function runDatabaseCommand(args: readonly string[], io?: CliIo, cwd?: string, loader?: (cwd: string) => Promise<OrmTooling>): Promise<number>;
|
|
11
|
+
declare function runDatabaseValidation(cwd: string, loader?: (cwd: string) => Promise<OrmTooling>): Promise<{
|
|
12
|
+
readonly status: "passed" | "failed";
|
|
13
|
+
readonly exitCode: number;
|
|
14
|
+
readonly stdout: string;
|
|
15
|
+
readonly stderr: string;
|
|
16
|
+
}>;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { OrmTooling, loadOrmTooling, runDatabaseCommand, runDatabaseValidation };
|
package/dist/database.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
//#region src/bin/database.ts
|
|
7
|
+
async function loadOrmTooling(cwd) {
|
|
8
|
+
const require = createRequire(path.join(path.resolve(cwd), "package.json"));
|
|
9
|
+
let entry;
|
|
10
|
+
try {
|
|
11
|
+
const manifestPath = require.resolve("@askrjs/orm/package.json");
|
|
12
|
+
const toolingExport = JSON.parse(await fs.readFile(manifestPath, "utf8")).exports?.["./tooling"];
|
|
13
|
+
const relative = typeof toolingExport === "string" ? toolingExport : toolingExport?.import;
|
|
14
|
+
if (!relative) throw new Error("Missing import export for @askrjs/orm/tooling.");
|
|
15
|
+
entry = path.resolve(path.dirname(manifestPath), relative);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
throw new Error("This project does not have @askrjs/orm installed. Install it before running `askr database`.", { cause: error });
|
|
18
|
+
}
|
|
19
|
+
const tooling = await import(pathToFileURL(entry).href);
|
|
20
|
+
if (typeof tooling.runDatabaseCli !== "function") throw new Error("The installed @askrjs/orm package does not expose compatible database tooling.");
|
|
21
|
+
return tooling;
|
|
22
|
+
}
|
|
23
|
+
async function runDatabaseCommand(args, io = console, cwd = process.cwd(), loader = loadOrmTooling) {
|
|
24
|
+
try {
|
|
25
|
+
return (await loader(cwd)).runDatabaseCli(args, {
|
|
26
|
+
cwd,
|
|
27
|
+
io
|
|
28
|
+
});
|
|
29
|
+
} catch (error) {
|
|
30
|
+
io.error(error instanceof Error ? error.message : String(error));
|
|
31
|
+
return 1;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function runDatabaseValidation(cwd, loader = loadOrmTooling) {
|
|
35
|
+
const stdout = [];
|
|
36
|
+
const stderr = [];
|
|
37
|
+
const code = await runDatabaseCommand([
|
|
38
|
+
"validate",
|
|
39
|
+
"--cwd",
|
|
40
|
+
cwd
|
|
41
|
+
], {
|
|
42
|
+
log: (...values) => stdout.push(values.join(" ")),
|
|
43
|
+
error: (...values) => stderr.push(values.join(" "))
|
|
44
|
+
}, cwd, loader);
|
|
45
|
+
return {
|
|
46
|
+
status: code === 0 ? "passed" : "failed",
|
|
47
|
+
exitCode: code,
|
|
48
|
+
stdout: stdout.join("\n"),
|
|
49
|
+
stderr: stderr.join("\n")
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { loadOrmTooling, runDatabaseCommand, runDatabaseValidation };
|
package/dist/generate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
2
|
-
import { basename, dirname, isAbsolute, join, relative, resolve
|
|
2
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
4
|
import { lookup } from "node:dns/promises";
|
|
5
5
|
import { request } from "node:https";
|
|
@@ -296,10 +296,16 @@ async function fetchSource(uri, options) {
|
|
|
296
296
|
}
|
|
297
297
|
async function readSource(uri, options) {
|
|
298
298
|
if (uri.startsWith("http://") || uri.startsWith("https://")) return fetchSource(uri, options);
|
|
299
|
-
|
|
299
|
+
let path;
|
|
300
|
+
try {
|
|
301
|
+
path = fileURLToPath(uri);
|
|
302
|
+
} catch {
|
|
303
|
+
throw new GenerationError(`Local OpenAPI reference escapes the specification directory: ${uri}`);
|
|
304
|
+
}
|
|
300
305
|
const canonical = await realpath(path);
|
|
301
|
-
|
|
302
|
-
|
|
306
|
+
if (!options.localRootDirectory) throw new GenerationError(`Local OpenAPI reference escapes the specification directory: ${path}`);
|
|
307
|
+
const relativePath = relative(options.localRootDirectory, canonical);
|
|
308
|
+
if (relativePath.startsWith("..") || isAbsolute(relativePath)) throw new GenerationError(`Local OpenAPI reference escapes the specification directory: ${path}`);
|
|
303
309
|
const contents = await readFile(canonical);
|
|
304
310
|
if (contents.byteLength > options.maxBytes) throw new GenerationError(`OpenAPI reference exceeds ${options.maxBytes} bytes: ${path}`);
|
|
305
311
|
return {
|
|
@@ -98,7 +98,7 @@ async function runGuardrailCli(command, args = process.argv.slice(2), io = conso
|
|
|
98
98
|
cwd: parsed.cwd,
|
|
99
99
|
workspacePatterns: parsed.workspacePatterns
|
|
100
100
|
};
|
|
101
|
-
const { runCheck, runDoctor, runRepair } = await import("./runner-
|
|
101
|
+
const { runCheck, runDoctor, runRepair } = await import("./runner-CEt07ieG.js");
|
|
102
102
|
const report = command === "doctor" ? await runDoctor(options, runtime) : command === "repair" ? await runRepair(options) : await runCheck(options, runtime);
|
|
103
103
|
if (parsed.json) io.log(JSON.stringify(report));
|
|
104
104
|
else if (report.command === "doctor") printDoctor(report, io);
|
|
@@ -294,11 +294,7 @@ function solveWorkspace(workspace, selectedOccurrences, context, packuments, tag
|
|
|
294
294
|
return;
|
|
295
295
|
}
|
|
296
296
|
const next = remaining.map((name) => [name, pruned.get(name)]).sort(([leftName, left], [rightName, right]) => left.length - right.length || leftName.localeCompare(rightName))[0];
|
|
297
|
-
const signature = component.map((name) => {
|
|
298
|
-
const assignedVersion = assigned.get(name);
|
|
299
|
-
const domain = pruned.get(name) ?? [];
|
|
300
|
-
return `${name}:assigned=${JSON.stringify(assignedVersion ?? null)}:domain=${JSON.stringify(domain)}`;
|
|
301
|
-
}).join("|");
|
|
297
|
+
const signature = component.map((name) => assigned.has(name) ? `${name}=assigned:${assigned.get(name)}` : `${name}=domain:${(pruned.get(name) ?? []).join(",")}`).join("|");
|
|
302
298
|
if (memo.has(signature)) return;
|
|
303
299
|
memo.add(signature);
|
|
304
300
|
for (const version of next[1]) {
|