@askrjs/cli 0.0.2 → 0.0.3

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.
Files changed (96) hide show
  1. package/README.md +9 -7
  2. package/dist/add.js +554 -1
  3. package/dist/add.js.map +1 -0
  4. package/dist/cli.js +43 -14
  5. package/dist/cli.js.map +1 -1
  6. package/dist/discovery-Difb7Y4G.js +325 -0
  7. package/dist/discovery-Difb7Y4G.js.map +1 -0
  8. package/dist/generate.js +396 -0
  9. package/dist/generate.js.map +1 -0
  10. package/dist/openapi.js +5 -4
  11. package/dist/openapi.js.map +1 -1
  12. package/dist/planner-VAj7qlxr.js +287 -0
  13. package/dist/planner-VAj7qlxr.js.map +1 -0
  14. package/dist/range-YUs9eimn.js +127 -0
  15. package/dist/range-YUs9eimn.js.map +1 -0
  16. package/dist/registry-CyrRHRG5.js +211 -0
  17. package/dist/registry-CyrRHRG5.js.map +1 -0
  18. package/dist/skills/askr-dashboard-charts/SKILL.md +61 -44
  19. package/dist/skills/askr-dashboard-charts/agents/openai.yaml +2 -2
  20. package/dist/specification-DXnDOC-0.js +51 -0
  21. package/dist/specification-DXnDOC-0.js.map +1 -0
  22. package/dist/templates/full-stack/AGENTS.md +0 -1
  23. package/dist/templates/full-stack/README.md +0 -1
  24. package/dist/templates/full-stack/index.html +0 -1
  25. package/dist/templates/full-stack/package.json +4 -2
  26. package/dist/templates/full-stack/server.ts +5 -9
  27. package/dist/templates/full-stack/src/action-authorizations.ts +2 -2
  28. package/dist/templates/full-stack/src/actions/create-message.ts +5 -5
  29. package/dist/templates/full-stack/src/i18n.ts +4 -4
  30. package/dist/templates/full-stack/src/main.tsx +4 -5
  31. package/dist/templates/full-stack/src/pages/home.tsx +5 -5
  32. package/dist/templates/full-stack/src/pages/layout.tsx +15 -5
  33. package/dist/templates/full-stack/src/pages/not-found.tsx +5 -2
  34. package/dist/templates/full-stack/src/routes.tsx +11 -12
  35. package/dist/templates/full-stack/src/schemas.ts +1 -2
  36. package/dist/templates/full-stack/src/server/action-registry.ts +6 -10
  37. package/dist/templates/full-stack/src/server/actions/create-message.ts +4 -4
  38. package/dist/templates/full-stack/src/server/app.ts +67 -57
  39. package/dist/templates/full-stack/src/server/dependencies.ts +10 -10
  40. package/dist/templates/full-stack/src/server/entry-server.ts +3 -3
  41. package/dist/templates/full-stack/src/telemetry.ts +1 -2
  42. package/dist/templates/full-stack/src/vite-server.d.ts +2 -3
  43. package/dist/templates/full-stack/tests/actions/create-message.test.ts +7 -8
  44. package/dist/templates/full-stack/tsconfig.json +0 -1
  45. package/dist/templates/full-stack/vite.config.ts +5 -6
  46. package/dist/templates/spa/AGENTS.md +1 -1
  47. package/dist/templates/spa/package.json +1 -1
  48. package/dist/templates/spa/src/adapters/operations-client.ts +7 -2
  49. package/dist/templates/spa/src/pages/app/admin-home.tsx +21 -3
  50. package/dist/templates/spa/src/styles.css +1 -1
  51. package/dist/templates/ssg/tests/ssg-config.test.ts +3 -3
  52. package/dist/templates/ssr/server.ts +4 -10
  53. package/dist/templates/ssr/src/entry-server.tsx +6 -4
  54. package/dist/update.js +284 -2
  55. package/dist/update.js.map +1 -0
  56. package/dist/writer-D8qe_7ud.js +228 -0
  57. package/dist/writer-D8qe_7ud.js.map +1 -0
  58. package/package.json +11 -15
  59. package/skills/askr-dashboard-charts/SKILL.md +61 -44
  60. package/skills/askr-dashboard-charts/agents/openai.yaml +2 -2
  61. package/templates/full-stack/AGENTS.md +0 -1
  62. package/templates/full-stack/README.md +0 -1
  63. package/templates/full-stack/index.html +0 -1
  64. package/templates/full-stack/package.json +4 -2
  65. package/templates/full-stack/server.ts +5 -9
  66. package/templates/full-stack/src/action-authorizations.ts +2 -2
  67. package/templates/full-stack/src/actions/create-message.ts +5 -5
  68. package/templates/full-stack/src/i18n.ts +4 -4
  69. package/templates/full-stack/src/main.tsx +4 -5
  70. package/templates/full-stack/src/pages/home.tsx +5 -5
  71. package/templates/full-stack/src/pages/layout.tsx +15 -5
  72. package/templates/full-stack/src/pages/not-found.tsx +5 -2
  73. package/templates/full-stack/src/routes.tsx +11 -12
  74. package/templates/full-stack/src/schemas.ts +1 -2
  75. package/templates/full-stack/src/server/action-registry.ts +6 -10
  76. package/templates/full-stack/src/server/actions/create-message.ts +4 -4
  77. package/templates/full-stack/src/server/app.ts +67 -57
  78. package/templates/full-stack/src/server/dependencies.ts +10 -10
  79. package/templates/full-stack/src/server/entry-server.ts +3 -3
  80. package/templates/full-stack/src/telemetry.ts +1 -2
  81. package/templates/full-stack/src/vite-server.d.ts +2 -3
  82. package/templates/full-stack/tests/actions/create-message.test.ts +7 -8
  83. package/templates/full-stack/tsconfig.json +0 -1
  84. package/templates/full-stack/vite.config.ts +5 -6
  85. package/templates/spa/AGENTS.md +1 -1
  86. package/templates/spa/package.json +1 -1
  87. package/templates/spa/src/adapters/operations-client.ts +7 -2
  88. package/templates/spa/src/pages/app/admin-home.tsx +21 -3
  89. package/templates/spa/src/styles.css +1 -1
  90. package/templates/ssg/tests/ssg-config.test.ts +3 -3
  91. package/templates/ssr/server.ts +4 -10
  92. package/templates/ssr/src/entry-server.tsx +6 -4
  93. package/dist/add-DqhqRzBQ.js +0 -555
  94. package/dist/add-DqhqRzBQ.js.map +0 -1
  95. package/dist/update-DyW1na5V.js +0 -1140
  96. package/dist/update-DyW1na5V.js.map +0 -1
@@ -0,0 +1,211 @@
1
+ import { t as parseDependencySpecification } from "./specification-DXnDOC-0.js";
2
+ import path from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ import { execFile } from "node:child_process";
5
+ import semver from "semver";
6
+ //#region src/update/registry.ts
7
+ function installedNpmCli(executable) {
8
+ const directories = /* @__PURE__ */ new Set([path.dirname(process.execPath)]);
9
+ if (executable && path.isAbsolute(executable)) directories.add(path.dirname(executable));
10
+ for (const directory of directories) for (const candidate of [path.join(directory, "node_modules", "npm", "bin", "npm-cli.js"), path.resolve(directory, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")]) if (existsSync(candidate)) return candidate;
11
+ return null;
12
+ }
13
+ function npmInvocation(env) {
14
+ const executable = env.npm_execpath;
15
+ const executableName = executable ? path.basename(executable).toLowerCase() : "";
16
+ const isNpmExecutable = executableName === "npm" || executableName === "npm.cmd" || /^npm(?:-cli)?\.(?:cjs|js|mjs)$/.test(executableName);
17
+ if (executable && isNpmExecutable) {
18
+ const extension = path.extname(executable).toLowerCase();
19
+ if ([
20
+ ".cjs",
21
+ ".js",
22
+ ".mjs"
23
+ ].includes(extension)) return {
24
+ executable: process.execPath,
25
+ prefix: [executable]
26
+ };
27
+ const npmCli = installedNpmCli(executable);
28
+ return npmCli ? {
29
+ executable: process.execPath,
30
+ prefix: [npmCli]
31
+ } : {
32
+ executable,
33
+ prefix: []
34
+ };
35
+ }
36
+ const npmCli = installedNpmCli();
37
+ if (npmCli) return {
38
+ executable: process.execPath,
39
+ prefix: [npmCli]
40
+ };
41
+ return {
42
+ executable: process.platform === "win32" ? "npm.cmd" : "npm",
43
+ prefix: []
44
+ };
45
+ }
46
+ function executionEnvironment(env) {
47
+ if (env === process.env) return { ...process.env };
48
+ const inherited = { ...process.env };
49
+ for (const name of Object.keys(inherited)) if (/^npm_config_/i.test(name)) delete inherited[name];
50
+ return {
51
+ ...inherited,
52
+ ...env
53
+ };
54
+ }
55
+ async function loadNpmConfiguration(root, env = process.env) {
56
+ const effectiveEnvironment = executionEnvironment(env);
57
+ return {
58
+ cwd: root,
59
+ env: effectiveEnvironment,
60
+ invocation: npmInvocation(effectiveEnvironment)
61
+ };
62
+ }
63
+ function npmError(error, stderr) {
64
+ const details = `${String(error.code ?? "")}\n${stderr}`;
65
+ const safe = /* @__PURE__ */ new Error("npm registry lookup failed");
66
+ safe.code = (details.match(/\bE(?:401|403|404|CONNRESET|TIMEDOUT|TIMEOUT)\b/i)?.[0])?.toUpperCase() ?? (typeof error.code === "string" ? error.code : "");
67
+ if (safe.code === "E401") safe.statusCode = 401;
68
+ if (safe.code === "E403") safe.statusCode = 403;
69
+ if (safe.code === "E404") safe.statusCode = 404;
70
+ return safe;
71
+ }
72
+ function executeNpmJson(args, configuration, preferOnline) {
73
+ const [command, ...positionals] = args;
74
+ return new Promise((resolve, reject) => {
75
+ execFile(configuration.invocation.executable, [
76
+ ...configuration.invocation.prefix,
77
+ command,
78
+ "--json",
79
+ preferOnline ? "--prefer-online" : "--prefer-offline",
80
+ "--offline=false",
81
+ "--loglevel=error",
82
+ "--update-notifier=false",
83
+ "--",
84
+ ...positionals
85
+ ], {
86
+ cwd: configuration.cwd,
87
+ encoding: "utf8",
88
+ env: configuration.env,
89
+ maxBuffer: 64 * 1024 * 1024,
90
+ windowsHide: true
91
+ }, (error, stdout, stderr) => {
92
+ if (error) {
93
+ reject(npmError(error, stderr));
94
+ return;
95
+ }
96
+ try {
97
+ resolve(JSON.parse(stdout));
98
+ } catch {
99
+ reject(/* @__PURE__ */ new Error("npm registry returned malformed JSON"));
100
+ }
101
+ });
102
+ });
103
+ }
104
+ function isRecord(value) {
105
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
106
+ }
107
+ function basePackument(value) {
108
+ if (!isRecord(value) || !Array.isArray(value.versions) || !isRecord(value["dist-tags"])) return null;
109
+ const versions = value.versions.filter((version) => typeof version === "string");
110
+ if (versions.length === 0) return null;
111
+ return {
112
+ "dist-tags": value["dist-tags"],
113
+ versions: Object.fromEntries(versions.map((version) => [version, { version }]))
114
+ };
115
+ }
116
+ function selectedVersions(packument, specifications) {
117
+ const versions = Object.keys(packument.versions ?? {}).filter((version) => semver.valid(version) !== null);
118
+ const selected = new Set(Object.values(packument["dist-tags"] ?? {}).filter((version) => typeof version === "string" && semver.valid(version) !== null && versions.includes(version)));
119
+ for (const specification of specifications) {
120
+ const parsed = parseDependencySpecification(specification);
121
+ if (parsed.type === "tag") {
122
+ const tagged = packument["dist-tags"]?.[parsed.rawSpec];
123
+ if (typeof tagged === "string" && versions.includes(tagged)) selected.add(tagged);
124
+ } else if (parsed.type === "version") {
125
+ if (versions.includes(parsed.rawSpec)) selected.add(parsed.rawSpec);
126
+ } else if (parsed.type === "range") {
127
+ const matching = semver.maxSatisfying(versions, parsed.rawSpec);
128
+ if (matching) selected.add(matching);
129
+ }
130
+ }
131
+ return [...selected].sort(semver.compare);
132
+ }
133
+ function mergeVersionMetadata(packument, value, selected) {
134
+ const entries = Array.isArray(value) ? value : [value];
135
+ const merged = /* @__PURE__ */ new Set();
136
+ for (const entry of entries) {
137
+ if (typeof entry === "string") {
138
+ if (packument.versions?.[entry]) merged.add(entry);
139
+ continue;
140
+ }
141
+ if (!isRecord(entry) || typeof entry.version !== "string") continue;
142
+ const metadata = packument.versions?.[entry.version];
143
+ if (!isRecord(metadata)) continue;
144
+ if (isRecord(entry.peerDependencies)) metadata.peerDependencies = entry.peerDependencies;
145
+ merged.add(entry.version);
146
+ }
147
+ return selected.every((version) => merged.has(version));
148
+ }
149
+ async function executeNpmView(packageName, configuration, specifications) {
150
+ const packument = basePackument(await executeNpmJson([
151
+ "view",
152
+ packageName,
153
+ "versions",
154
+ "dist-tags"
155
+ ], configuration, true));
156
+ if (!packument) return null;
157
+ const versions = selectedVersions(packument, specifications);
158
+ if (versions.length === 0) return packument;
159
+ return mergeVersionMetadata(packument, await executeNpmJson([
160
+ "view",
161
+ `${packageName}@${versions.join(" || ")}`,
162
+ "version",
163
+ "peerDependencies"
164
+ ], configuration, false), versions) ? packument : null;
165
+ }
166
+ function isPackument(value) {
167
+ return isRecord(value) && isRecord(value["dist-tags"]) && isRecord(value.versions);
168
+ }
169
+ function sanitizeRegistryError(error) {
170
+ const value = error && typeof error === "object" ? error : {};
171
+ if (value.statusCode === 404 || value.code === "E404") return "package was not found in the selected registry";
172
+ if (value.statusCode === 401 || value.statusCode === 403 || value.code === "E401" || value.code === "E403") return "registry authentication or authorization failed";
173
+ const code = typeof value.code === "string" ? value.code.toUpperCase() : "";
174
+ if ([
175
+ "ETIMEDOUT",
176
+ "ETIMEOUT",
177
+ "FETCH_ERROR",
178
+ "ECONNRESET"
179
+ ].includes(code)) return "registry request timed out or was interrupted";
180
+ if (/^[A-Z][A-Z0-9_]{1,30}$/.test(code)) return `registry request failed (${code})`;
181
+ return "registry request failed";
182
+ }
183
+ async function fetchPackuments(packageNames, configuration, options = {}) {
184
+ const names = [...new Set(packageNames)].sort((left, right) => left.localeCompare(right));
185
+ const packuments = /* @__PURE__ */ new Map();
186
+ const failures = /* @__PURE__ */ new Map();
187
+ const viewPackage = options.viewPackage ?? executeNpmView;
188
+ let nextIndex = 0;
189
+ const worker = async () => {
190
+ while (nextIndex < names.length) {
191
+ const packageName = names[nextIndex];
192
+ nextIndex += 1;
193
+ try {
194
+ const result = await viewPackage(packageName, configuration, options.requirements?.specifications?.get(packageName) ?? []);
195
+ if (isPackument(result)) packuments.set(packageName, result);
196
+ else failures.set(packageName, "registry returned malformed package metadata");
197
+ } catch (error) {
198
+ failures.set(packageName, sanitizeRegistryError(error));
199
+ }
200
+ }
201
+ };
202
+ await Promise.all(Array.from({ length: Math.min(8, names.length) }, worker));
203
+ return {
204
+ packuments,
205
+ failures
206
+ };
207
+ }
208
+ //#endregion
209
+ export { fetchPackuments, loadNpmConfiguration };
210
+
211
+ //# sourceMappingURL=registry-CyrRHRG5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-CyrRHRG5.js","names":[],"sources":["../src/update/registry.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport semver from \"semver\";\nimport { parseDependencySpecification } from \"./specification\";\nimport type { Packument } from \"./types\";\n\ninterface NpmInvocation {\n executable: string;\n prefix: string[];\n}\n\nexport interface NpmConfiguration {\n cwd: string;\n env: NodeJS.ProcessEnv;\n invocation: NpmInvocation;\n}\n\nexport interface RegistryRequirements {\n specifications?: ReadonlyMap<string, readonly string[]>;\n}\n\ninterface FetchPackumentOptions {\n requirements?: RegistryRequirements;\n viewPackage?: ViewPackage;\n}\n\ntype ViewPackage = (\n packageName: string,\n configuration: NpmConfiguration,\n specifications: readonly string[],\n) => Promise<unknown>;\n\nfunction installedNpmCli(executable?: string): string | null {\n const directories = new Set([path.dirname(process.execPath)]);\n if (executable && path.isAbsolute(executable)) directories.add(path.dirname(executable));\n for (const directory of directories) {\n for (const candidate of [\n path.join(directory, \"node_modules\", \"npm\", \"bin\", \"npm-cli.js\"),\n path.resolve(directory, \"..\", \"lib\", \"node_modules\", \"npm\", \"bin\", \"npm-cli.js\"),\n ]) {\n if (existsSync(candidate)) return candidate;\n }\n }\n return null;\n}\n\nfunction npmInvocation(env: NodeJS.ProcessEnv): NpmInvocation {\n const executable = env.npm_execpath;\n const executableName = executable ? path.basename(executable).toLowerCase() : \"\";\n const isNpmExecutable =\n executableName === \"npm\" ||\n executableName === \"npm.cmd\" ||\n /^npm(?:-cli)?\\.(?:cjs|js|mjs)$/.test(executableName);\n if (executable && isNpmExecutable) {\n const extension = path.extname(executable).toLowerCase();\n if ([\".cjs\", \".js\", \".mjs\"].includes(extension)) {\n return { executable: process.execPath, prefix: [executable] };\n }\n const npmCli = installedNpmCli(executable);\n return npmCli ? { executable: process.execPath, prefix: [npmCli] } : { executable, prefix: [] };\n }\n const npmCli = installedNpmCli();\n if (npmCli) return { executable: process.execPath, prefix: [npmCli] };\n return { executable: process.platform === \"win32\" ? \"npm.cmd\" : \"npm\", prefix: [] };\n}\n\nfunction executionEnvironment(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n if (env === process.env) return { ...process.env };\n const inherited = { ...process.env };\n for (const name of Object.keys(inherited)) {\n if (/^npm_config_/i.test(name)) delete inherited[name];\n }\n return { ...inherited, ...env };\n}\n\nexport async function loadNpmConfiguration(\n root: string,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<NpmConfiguration> {\n const effectiveEnvironment = executionEnvironment(env);\n return {\n cwd: root,\n env: effectiveEnvironment,\n invocation: npmInvocation(effectiveEnvironment),\n };\n}\n\nfunction npmError(error: { code?: string | number | null }, stderr: string): Error {\n const details = `${String(error.code ?? \"\")}\\n${stderr}`;\n const safe = new Error(\"npm registry lookup failed\") as Error & {\n code?: string;\n statusCode?: number;\n };\n const npmCode = details.match(/\\bE(?:401|403|404|CONNRESET|TIMEDOUT|TIMEOUT)\\b/i)?.[0];\n safe.code = npmCode?.toUpperCase() ?? (typeof error.code === \"string\" ? error.code : \"\");\n if (safe.code === \"E401\") safe.statusCode = 401;\n if (safe.code === \"E403\") safe.statusCode = 403;\n if (safe.code === \"E404\") safe.statusCode = 404;\n return safe;\n}\n\nfunction executeNpmJson(\n args: string[],\n configuration: NpmConfiguration,\n preferOnline: boolean,\n): Promise<unknown> {\n const [command, ...positionals] = args;\n return new Promise((resolve, reject) => {\n execFile(\n configuration.invocation.executable,\n [\n ...configuration.invocation.prefix,\n command,\n \"--json\",\n preferOnline ? \"--prefer-online\" : \"--prefer-offline\",\n \"--offline=false\",\n \"--loglevel=error\",\n \"--update-notifier=false\",\n \"--\",\n ...positionals,\n ],\n {\n cwd: configuration.cwd,\n encoding: \"utf8\",\n env: configuration.env,\n maxBuffer: 64 * 1024 * 1024,\n windowsHide: true,\n },\n (error, stdout, stderr) => {\n if (error) {\n reject(npmError(error, stderr));\n return;\n }\n try {\n resolve(JSON.parse(stdout));\n } catch {\n reject(new Error(\"npm registry returned malformed JSON\"));\n }\n },\n );\n });\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction basePackument(value: unknown): Packument | null {\n if (!isRecord(value) || !Array.isArray(value.versions) || !isRecord(value[\"dist-tags\"])) {\n return null;\n }\n const versions = value.versions.filter(\n (version): version is string => typeof version === \"string\",\n );\n if (versions.length === 0) return null;\n return {\n \"dist-tags\": value[\"dist-tags\"],\n versions: Object.fromEntries(versions.map((version) => [version, { version }])),\n };\n}\n\nfunction selectedVersions(packument: Packument, specifications: readonly string[]): string[] {\n const versions = Object.keys(packument.versions ?? {}).filter(\n (version) => semver.valid(version) !== null,\n );\n const selected = new Set(\n Object.values(packument[\"dist-tags\"] ?? {}).filter(\n (version): version is string =>\n typeof version === \"string\" && semver.valid(version) !== null && versions.includes(version),\n ),\n );\n\n for (const specification of specifications) {\n const parsed = parseDependencySpecification(specification);\n if (parsed.type === \"tag\") {\n const tagged = packument[\"dist-tags\"]?.[parsed.rawSpec];\n if (typeof tagged === \"string\" && versions.includes(tagged)) selected.add(tagged);\n } else if (parsed.type === \"version\") {\n if (versions.includes(parsed.rawSpec)) selected.add(parsed.rawSpec);\n } else if (parsed.type === \"range\") {\n const matching = semver.maxSatisfying(versions, parsed.rawSpec);\n if (matching) selected.add(matching);\n }\n }\n return [...selected].sort(semver.compare);\n}\n\nfunction mergeVersionMetadata(\n packument: Packument,\n value: unknown,\n selected: readonly string[],\n): boolean {\n const entries = Array.isArray(value) ? value : [value];\n const merged = new Set<string>();\n for (const entry of entries) {\n if (typeof entry === \"string\") {\n if (packument.versions?.[entry]) merged.add(entry);\n continue;\n }\n if (!isRecord(entry) || typeof entry.version !== \"string\") continue;\n const metadata = packument.versions?.[entry.version];\n if (!isRecord(metadata)) continue;\n if (isRecord(entry.peerDependencies)) metadata.peerDependencies = entry.peerDependencies;\n merged.add(entry.version);\n }\n return selected.every((version) => merged.has(version));\n}\n\nasync function executeNpmView(\n packageName: string,\n configuration: NpmConfiguration,\n specifications: readonly string[],\n): Promise<Packument | null> {\n const packument = basePackument(\n await executeNpmJson([\"view\", packageName, \"versions\", \"dist-tags\"], configuration, true),\n );\n if (!packument) return null;\n\n const versions = selectedVersions(packument, specifications);\n if (versions.length === 0) return packument;\n const selector = `${packageName}@${versions.join(\" || \")}`;\n const metadata = await executeNpmJson(\n [\"view\", selector, \"version\", \"peerDependencies\"],\n configuration,\n false,\n );\n return mergeVersionMetadata(packument, metadata, versions) ? packument : null;\n}\n\nfunction isPackument(value: unknown): value is Packument {\n return isRecord(value) && isRecord(value[\"dist-tags\"]) && isRecord(value.versions);\n}\n\nexport function sanitizeRegistryError(error: unknown): string {\n const value =\n error && typeof error === \"object\" ? (error as { code?: unknown; statusCode?: unknown }) : {};\n if (value.statusCode === 404 || value.code === \"E404\") {\n return \"package was not found in the selected registry\";\n }\n if (\n value.statusCode === 401 ||\n value.statusCode === 403 ||\n value.code === \"E401\" ||\n value.code === \"E403\"\n ) {\n return \"registry authentication or authorization failed\";\n }\n const code = typeof value.code === \"string\" ? value.code.toUpperCase() : \"\";\n if ([\"ETIMEDOUT\", \"ETIMEOUT\", \"FETCH_ERROR\", \"ECONNRESET\"].includes(code)) {\n return \"registry request timed out or was interrupted\";\n }\n if (/^[A-Z][A-Z0-9_]{1,30}$/.test(code)) return `registry request failed (${code})`;\n return \"registry request failed\";\n}\n\nexport interface PackumentResults {\n packuments: Map<string, Packument>;\n failures: Map<string, string>;\n}\n\nexport async function fetchPackuments(\n packageNames: string[],\n configuration: NpmConfiguration,\n options: FetchPackumentOptions = {},\n): Promise<PackumentResults> {\n const names = [...new Set(packageNames)].sort((left, right) => left.localeCompare(right));\n const packuments = new Map<string, Packument>();\n const failures = new Map<string, string>();\n const viewPackage = options.viewPackage ?? executeNpmView;\n let nextIndex = 0;\n\n const worker = async (): Promise<void> => {\n while (nextIndex < names.length) {\n const packageName = names[nextIndex];\n nextIndex += 1;\n try {\n const result = await viewPackage(\n packageName,\n configuration,\n options.requirements?.specifications?.get(packageName) ?? [],\n );\n if (isPackument(result)) {\n packuments.set(packageName, result);\n } else {\n failures.set(packageName, \"registry returned malformed package metadata\");\n }\n } catch (error) {\n failures.set(packageName, sanitizeRegistryError(error));\n }\n }\n };\n\n await Promise.all(Array.from({ length: Math.min(8, names.length) }, worker));\n return { packuments, failures };\n}\n"],"mappings":"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"}
@@ -1,79 +1,96 @@
1
1
  ---
2
2
  name: askr-dashboard-charts
3
- description: Use when building askr dashboards, stat cards, activity feeds, product metrics, tables, async feedback, and @askrjs/charts visualizations such as area, bar, line, donut, heatmap, timeline, gauges, and chart chrome.
3
+ description: Build product dashboards and metric-heavy Askr routes with route-owned async state, shared formatting, and typed @askrjs/charts plots. Use for stat cards, mixed charts, live or follow-latest data, zoom, brush, selection, accessible summaries, and chart exports.
4
4
  ---
5
5
 
6
6
  # Askr Dashboard Charts
7
7
 
8
- Use this for product dashboards and metric-heavy screens. The goal is route-owned data, deterministic metric formatting, and charts that answer product questions without inventing parallel UI systems.
9
-
10
- ## Use This When
11
-
12
- - You are building a dashboard, metrics screen, stat-card surface, or chart-heavy route.
13
- - The page needs resource-owned async loading plus chart composition.
14
- - Charts and tables must share the same formatter logic.
15
- - You need loading, empty, and error truth on a dense metrics screen.
8
+ Build dashboards that answer product questions without inventing parallel data, UI, or formatting systems.
16
9
 
17
10
  ## Inspect First
18
11
 
19
- - `templates/startkit/src/pages/workspace/dashboard.tsx`
20
- - Existing stat card, table, empty state, and chart styles
21
- - Existing format helpers in `src/shared`
22
- - Existing route or feature owners for the metrics you need
12
+ - Inspect the route or feature that owns the data.
13
+ - Inspect existing cards, tables, empty states, chart tokens, and format helpers.
14
+ - Inspect the nearest existing typed plot. In a generated SPA, start with `src/pages/app/admin-home.tsx`; in the CLI source, use `templates/spa/src/pages/app/admin-home.tsx`.
23
15
 
24
- ## Choose The Owner
16
+ ## Keep Ownership Clear
25
17
 
26
- - The route or page owns high-level loading and composition.
18
+ - Keep loading, empty, error, refresh, and composition state in the route or feature container.
27
19
  - `src/components/shared` owns reusable stat cards, page headers, tables, and empty states.
28
- - `src/features/<domain>` owns domain-specific dashboard panels and metric workflows.
20
+ - `src/features/<domain>` owns metric workflows and transforms.
29
21
  - `src/shared` owns formatting helpers so cards, tables, and charts stay consistent.
22
+ - Keep row types app-owned; do not import package-owned datum types.
30
23
 
31
- ## Do This In Order
24
+ ## Work In Order
32
25
 
33
- 1. Load dashboard data in the route or feature container with `resource()` unless the data must be shared across screens.
34
- 2. Keep metric formatting in shared helpers.
35
- 3. Use `@askrjs/charts` components for chart visuals and keep CSS imported at the app boundary.
36
- 4. Pair charts with labels, summaries, or tables when precision matters.
37
- 5. Keep loading, empty, error, and refresh states explicit.
26
+ 1. Load dashboard data with `resource()` unless a wider owner already exists.
27
+ 2. Model stable row keys and missing values before choosing marks.
28
+ 3. Create each typed plot namespace once at module scope.
29
+ 4. Compose the smallest plot that answers the product question.
30
+ 5. Import `@askrjs/charts/styles` once at the app boundary.
31
+ 6. Pair plots with summaries or on-demand data tables when precision matters.
38
32
 
39
- ## Copy This Shape
33
+ ## Compose A Typed Plot
40
34
 
41
35
  ```tsx
42
- import { resource } from "@askrjs/askr/resources";
43
- import { AreaChart, ChartPanel, ChartShell } from "@askrjs/charts/components";
44
-
45
- const dashboard = resource(({ signal }) => loadDashboard({ signal }), []);
36
+ import { createPlot } from "@askrjs/charts";
37
+
38
+ type ThroughputRow = {
39
+ id: string;
40
+ timestamp: Date;
41
+ completed: number;
42
+ movingAverage: number;
43
+ };
44
+
45
+ const ThroughputPlot = createPlot<ThroughputRow>();
46
+
47
+ <ThroughputPlot.Root
48
+ data={rows}
49
+ rowKey="id"
50
+ label="Completed runs over time"
51
+ title="Run throughput"
52
+ description="Completed runs with a moving average."
53
+ >
54
+ <ThroughputPlot.Bar x="timestamp" y="completed" />
55
+ <ThroughputPlot.Line x="timestamp" y="movingAverage" />
56
+ <ThroughputPlot.Point x="timestamp" y="movingAverage" />
57
+ <ThroughputPlot.Zoom axes="x" />
58
+ <ThroughputPlot.Brush axis="x" modifier="shift" />
59
+ </ThroughputPlot.Root>;
60
+ ```
46
61
 
47
- if (dashboard.pending && !dashboard.value) return <p>Loading dashboard...</p>;
48
- if (dashboard.error) return <p role="alert">Unable to load dashboard.</p>;
62
+ ## Handle Mixed, Live, And Interactive Data
49
63
 
50
- <ChartShell title="Revenue" description="Last 7 days">
51
- <ChartPanel title="Trend">
52
- <AreaChart label="Revenue trend" data={dashboard.value.revenue} />
53
- </ChartPanel>
54
- </ChartShell>;
55
- ```
64
+ - Use named scales and dual axes only when a mixed plot compares different units.
65
+ - Pass a reactive data getter for changing rows and update arrays with `appendPlotRows`, `upsertPlotRows`, `removePlotRows`, or `trimPlotRows`.
66
+ - Configure follow-latest by row count or time; let user pan or zoom pause following and provide an explicit `resumeLive()` action.
67
+ - Keep `view` and `selection` controlled when route or shared state owns them; otherwise use plot-local state.
68
+ - Add zoom, brush, legend filtering, or `onActivate` only when the workflow needs that interaction.
69
+ - Keep source and transformed exports distinguishable, and preserve access to visible or selected rows.
56
70
 
57
- ## Never Do These
71
+ ## Avoid
58
72
 
59
73
  - Inline mock metrics inside page JSX.
60
74
  - Hardcoded chart colors in runtime code.
61
75
  - Decorative charts that do not answer a product question.
76
+ - Calling `createPlot()` inside a component or using unstable row keys.
77
+ - Treating missing or non-finite values as zero.
62
78
  - Dense dashboards without loading, empty, and error states.
63
79
 
64
80
  ## Validate
65
81
 
66
- - Cards and charts share formatter logic.
67
- - The page scans cleanly at mobile, tablet, and desktop widths.
68
- - Chart data has stable labels and keys.
69
- - Loading, empty, error, and refresh states are visible.
82
+ - Verify cards, tables, tooltips, and exports share formatter logic; keep summaries and tooltip formatters valid for empty or missing rows.
83
+ - Verify mobile and desktop layouts in light and dark themes.
84
+ - Verify keyboard inspection, selection, zoom, brush, and the on-demand data table when used.
85
+ - Verify live updates retain stable selections and pause follow-latest after user navigation.
86
+ - Run the app's targeted tests and typecheck before its full acceptance command.
70
87
 
71
88
  ## Done When
72
89
 
73
- - Route-owned loading and chart composition are clear.
74
- - Metric formatting is shared and deterministic.
75
- - Charts support the page's product question instead of decorating it.
76
- - Async states remain explicit on the dashboard surface.
90
+ - Route-owned async states remain explicit.
91
+ - Typed rows and stable keys drive the plot.
92
+ - The plot supports the product question without becoming a component gallery.
93
+ - Accessible summaries and precise data remain available without relying on canvas pixels.
77
94
 
78
95
  ## Handoff
79
96
 
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "Askr Dashboard Charts"
3
- short_description: "Build dashboards and charts"
4
- default_prompt: "Use $askr-dashboard-charts to build dashboard metrics and charts."
3
+ short_description: "Compose typed Askr dashboard plots"
4
+ default_prompt: "Use $askr-dashboard-charts to build an accessible typed dashboard plot."
@@ -0,0 +1,51 @@
1
+ import semver from "semver";
2
+ //#region src/update/specification.ts
3
+ const WINDOWS_PATH = /^[A-Za-z]:[\\/]/;
4
+ const HOSTED_GIT = /^(?:bitbucket|gist|github|gitlab):/i;
5
+ const GIT_URL = /^(?:git(?:\+[^:]+)?:|git@|ssh:)/i;
6
+ const GIT_SHORTHAND = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+(?:#.*)?$/;
7
+ const REGISTRY_TAG = /^[A-Za-z][A-Za-z0-9._-]*$/;
8
+ const SCOPED_PATH = /^@[^/]+\/[^/]+$/;
9
+ function parseDependencySpecification(specification) {
10
+ const rawSpec = specification.trim();
11
+ if (!rawSpec) return {
12
+ type: "unsupported",
13
+ rawSpec
14
+ };
15
+ if (/^npm:/i.test(rawSpec)) return {
16
+ type: "alias",
17
+ rawSpec
18
+ };
19
+ if (/^(?:file|link|workspace):/i.test(rawSpec) || rawSpec.startsWith("./") || rawSpec.startsWith("../") || rawSpec.startsWith("/") || rawSpec.startsWith("~/") || /\.(?:tar\.gz|tgz)$/i.test(rawSpec) || WINDOWS_PATH.test(rawSpec) || /^[A-Za-z]:/.test(rawSpec) || SCOPED_PATH.test(rawSpec)) return {
20
+ type: "file",
21
+ rawSpec
22
+ };
23
+ if (HOSTED_GIT.test(rawSpec) || GIT_URL.test(rawSpec) || GIT_SHORTHAND.test(rawSpec)) return {
24
+ type: "git",
25
+ rawSpec
26
+ };
27
+ if (/^https?:/i.test(rawSpec) || /^[a-z][a-z0-9+.-]*:\/\//i.test(rawSpec)) return {
28
+ type: "remote",
29
+ rawSpec
30
+ };
31
+ if (semver.valid(rawSpec)) return {
32
+ type: "version",
33
+ rawSpec
34
+ };
35
+ if (semver.validRange(rawSpec)) return {
36
+ type: "range",
37
+ rawSpec
38
+ };
39
+ if (REGISTRY_TAG.test(rawSpec)) return {
40
+ type: "tag",
41
+ rawSpec
42
+ };
43
+ return {
44
+ type: "unsupported",
45
+ rawSpec
46
+ };
47
+ }
48
+ //#endregion
49
+ export { parseDependencySpecification as t };
50
+
51
+ //# sourceMappingURL=specification-DXnDOC-0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specification-DXnDOC-0.js","names":[],"sources":["../src/update/specification.ts"],"sourcesContent":["import semver from \"semver\";\n\nexport type DependencySpecification =\n | { type: \"alias\" | \"file\" | \"git\" | \"remote\" | \"unsupported\"; rawSpec: string }\n | { type: \"range\" | \"tag\" | \"version\"; rawSpec: string };\n\nconst WINDOWS_PATH = /^[A-Za-z]:[\\\\/]/;\nconst HOSTED_GIT = /^(?:bitbucket|gist|github|gitlab):/i;\nconst GIT_URL = /^(?:git(?:\\+[^:]+)?:|git@|ssh:)/i;\nconst GIT_SHORTHAND = /^[A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+(?:#.*)?$/;\nconst REGISTRY_TAG = /^[A-Za-z][A-Za-z0-9._-]*$/;\nconst SCOPED_PATH = /^@[^/]+\\/[^/]+$/;\n\nexport function parseDependencySpecification(specification: string): DependencySpecification {\n const rawSpec = specification.trim();\n if (!rawSpec) return { type: \"unsupported\", rawSpec };\n if (/^npm:/i.test(rawSpec)) return { type: \"alias\", rawSpec };\n if (\n /^(?:file|link|workspace):/i.test(rawSpec) ||\n rawSpec.startsWith(\"./\") ||\n rawSpec.startsWith(\"../\") ||\n rawSpec.startsWith(\"/\") ||\n rawSpec.startsWith(\"~/\") ||\n /\\.(?:tar\\.gz|tgz)$/i.test(rawSpec) ||\n WINDOWS_PATH.test(rawSpec) ||\n /^[A-Za-z]:/.test(rawSpec) ||\n SCOPED_PATH.test(rawSpec)\n ) {\n return { type: \"file\", rawSpec };\n }\n if (HOSTED_GIT.test(rawSpec) || GIT_URL.test(rawSpec) || GIT_SHORTHAND.test(rawSpec)) {\n return { type: \"git\", rawSpec };\n }\n if (/^https?:/i.test(rawSpec) || /^[a-z][a-z0-9+.-]*:\\/\\//i.test(rawSpec)) {\n return { type: \"remote\", rawSpec };\n }\n if (semver.valid(rawSpec)) return { type: \"version\", rawSpec };\n if (semver.validRange(rawSpec)) return { type: \"range\", rawSpec };\n if (REGISTRY_TAG.test(rawSpec)) return { type: \"tag\", rawSpec };\n return { type: \"unsupported\", rawSpec };\n}\n"],"mappings":";;AAMA,MAAM,eAAe;AACrB,MAAM,aAAa;AACnB,MAAM,UAAU;AAChB,MAAM,gBAAgB;AACtB,MAAM,eAAe;AACrB,MAAM,cAAc;AAEpB,SAAgB,6BAA6B,eAAgD;CAC3F,MAAM,UAAU,cAAc,KAAK;CACnC,IAAI,CAAC,SAAS,OAAO;EAAE,MAAM;EAAe;CAAQ;CACpD,IAAI,SAAS,KAAK,OAAO,GAAG,OAAO;EAAE,MAAM;EAAS;CAAQ;CAC5D,IACE,6BAA6B,KAAK,OAAO,KACzC,QAAQ,WAAW,IAAI,KACvB,QAAQ,WAAW,KAAK,KACxB,QAAQ,WAAW,GAAG,KACtB,QAAQ,WAAW,IAAI,KACvB,sBAAsB,KAAK,OAAO,KAClC,aAAa,KAAK,OAAO,KACzB,aAAa,KAAK,OAAO,KACzB,YAAY,KAAK,OAAO,GAExB,OAAO;EAAE,MAAM;EAAQ;CAAQ;CAEjC,IAAI,WAAW,KAAK,OAAO,KAAK,QAAQ,KAAK,OAAO,KAAK,cAAc,KAAK,OAAO,GACjF,OAAO;EAAE,MAAM;EAAO;CAAQ;CAEhC,IAAI,YAAY,KAAK,OAAO,KAAK,2BAA2B,KAAK,OAAO,GACtE,OAAO;EAAE,MAAM;EAAU;CAAQ;CAEnC,IAAI,OAAO,MAAM,OAAO,GAAG,OAAO;EAAE,MAAM;EAAW;CAAQ;CAC7D,IAAI,OAAO,WAAW,OAAO,GAAG,OAAO;EAAE,MAAM;EAAS;CAAQ;CAChE,IAAI,aAAa,KAAK,OAAO,GAAG,OAAO;EAAE,MAAM;EAAO;CAAQ;CAC9D,OAAO;EAAE,MAAM;EAAe;CAAQ;AACxC"}
@@ -9,4 +9,3 @@ This project is the progressive full-stack stage of an Askr application.
9
9
  - Keep `index.html` as the only document source. Preserve exactly one `<!--askr-head-->` and one `<!--askr-app-->` marker.
10
10
  - Use native forms first; enhanced submission must preserve the same validation and authorization behavior.
11
11
  - Never log cookies, authorization values, tokens, form fields, request bodies, or personal data.
12
-
@@ -15,4 +15,3 @@ npm run preview
15
15
  Use `askr add action <name> --route <path>` to generate a browser-safe descriptor, server handler, composition-root registration, route authorization, and focused test.
16
16
 
17
17
  `index.html` owns the static document. `@askrjs/vite/server` injects only Askr-owned head nodes and composes the streamed app response at `<!--askr-app-->`.
18
-
@@ -11,4 +11,3 @@
11
11
  <script type="module" src="/src/main.tsx"></script>
12
12
  </body>
13
13
  </html>
14
-
@@ -14,8 +14,8 @@
14
14
  "check": "npm run lint && npm run typecheck && npm test && npm run build"
15
15
  },
16
16
  "dependencies": {
17
- "@askrjs/auth": ">=0.0.1 <0.1.0",
18
17
  "@askrjs/askr": ">=0.0.53 <0.1.0",
18
+ "@askrjs/auth": ">=0.0.1 <0.1.0",
19
19
  "@askrjs/i18n": ">=0.0.1 <0.1.0",
20
20
  "@askrjs/node": ">=0.0.1 <0.1.0",
21
21
  "@askrjs/otel": ">=0.0.1 <0.1.0",
@@ -33,5 +33,7 @@
33
33
  "vite-plus": "^0.2.4",
34
34
  "vitest": "^4.1.10"
35
35
  },
36
- "engines": { "node": "^20.19.0 || >=22.12.0" }
36
+ "engines": {
37
+ "node": "^20.19.0 || >=22.12.0"
38
+ }
37
39
  }
@@ -1,13 +1,9 @@
1
- import { listen } from '@askrjs/node';
2
- import app from 'virtual:askr-server';
1
+ import { serve } from "@askrjs/node";
2
+ import app from "virtual:askr-server";
3
3
 
4
4
  const port = Number(process.env.PORT ?? 3000);
5
- const server = await listen(app, {
6
- host: process.env.HOST ?? '127.0.0.1',
5
+ await serve(app, {
6
+ host: process.env.HOST ?? "127.0.0.1",
7
7
  port,
8
+ assets: { root: new URL("./dist", import.meta.url).pathname },
8
9
  });
9
-
10
- const shutdown = () => server.close();
11
- process.once('SIGINT', shutdown);
12
- process.once('SIGTERM', shutdown);
13
-
@@ -1,5 +1,5 @@
1
- import type { ActionDescriptor } from '@askrjs/askr/actions';
2
- import { createMessageAction } from './actions/create-message';
1
+ import type { ActionDescriptor } from "@askrjs/askr/actions";
2
+ import { createMessageAction } from "./actions/create-message";
3
3
 
4
4
  const actionsByRoute: Readonly<Record<string, readonly ActionDescriptor[]>> = Object.freeze({
5
5
  "/": [createMessageAction],
@@ -1,10 +1,10 @@
1
- import { defineAction } from '@askrjs/askr/actions';
2
- import { MessageInput } from '../schemas';
1
+ import { defineAction } from "@askrjs/askr/actions";
2
+ import { MessageInput } from "../schemas";
3
3
 
4
- export const createMessageActionRoute = '/';
4
+ export const createMessageActionRoute = "/";
5
5
 
6
6
  export const createMessageAction = defineAction({
7
- id: 'create-message',
7
+ id: "create-message",
8
8
  input: MessageInput,
9
- invalidates: ['messages'],
9
+ invalidates: ["messages"],
10
10
  });
@@ -1,8 +1,8 @@
1
- import { createI18n } from '@askrjs/i18n';
1
+ import { createI18n } from "@askrjs/i18n";
2
2
 
3
- export const messages = createI18n('en', {
3
+ export const messages = createI18n("en", {
4
4
  en: {
5
- title: () => '{{appName}}',
6
- intro: () => 'A progressive full-stack Askr application.',
5
+ title: () => "{{appName}}",
6
+ intro: () => "A progressive full-stack Askr application.",
7
7
  },
8
8
  });
@@ -1,12 +1,11 @@
1
- import { createSPA, hydrateSPA } from '@askrjs/askr/boot';
2
- import { pageRegistry } from './routes';
1
+ import { createSPA, hydrateSPA } from "@askrjs/askr/boot";
2
+ import { pageRegistry } from "./routes";
3
3
 
4
- const root = document.getElementById('app');
5
- if (!root) throw new Error('Missing #app root element.');
4
+ const root = document.getElementById("app");
5
+ if (!root) throw new Error("Missing #app root element.");
6
6
 
7
7
  if (root.hasChildNodes()) {
8
8
  await hydrateSPA({ root, registry: pageRegistry });
9
9
  } else {
10
10
  await createSPA({ root, registry: pageRegistry });
11
11
  }
12
-
@@ -1,12 +1,12 @@
1
- import { ActionForm } from '@askrjs/askr/actions';
2
- import { createMessageAction } from '../actions/create-message';
3
- import { messages } from '../i18n';
1
+ import { ActionForm } from "@askrjs/askr/actions";
2
+ import { createMessageAction } from "../actions/create-message";
3
+ import { messages } from "../i18n";
4
4
 
5
5
  export function HomePage() {
6
6
  return (
7
7
  <main>
8
- <h1>{messages.text('title')}</h1>
9
- <p>{messages.text('intro')}</p>
8
+ <h1>{messages.text("title")}</h1>
9
+ <p>{messages.text("intro")}</p>
10
10
  <form method="post" action="/api/session">
11
11
  <button type="submit">Start demo session</button>
12
12
  </form>
@@ -1,7 +1,15 @@
1
- import { Link } from '@askrjs/askr/router';
2
- import { Block, Container, Header, Navbar, NavBrand, NavGroup, NavLink } from '@askrjs/themes/components';
3
- import { ThemeScope, ThemeToggle } from '@askrjs/themes/theme';
4
- import { messages } from '../i18n';
1
+ import { Link } from "@askrjs/askr/router";
2
+ import {
3
+ Block,
4
+ Container,
5
+ Header,
6
+ Navbar,
7
+ NavBrand,
8
+ NavGroup,
9
+ NavLink,
10
+ } from "@askrjs/themes/components";
11
+ import { ThemeScope, ThemeToggle } from "@askrjs/themes/theme";
12
+ import { messages } from "../i18n";
5
13
 
6
14
  function ScopedLayout({ children }: { children?: unknown }) {
7
15
  return (
@@ -10,7 +18,9 @@ function ScopedLayout({ children }: { children?: unknown }) {
10
18
  <Header sticky>
11
19
  <Container paddingY="sm">
12
20
  <Navbar aria-label="Primary navigation" width="full">
13
- <NavBrand><Link href="/">{messages.text('title')}</Link></NavBrand>
21
+ <NavBrand>
22
+ <Link href="/">{messages.text("title")}</Link>
23
+ </NavBrand>
14
24
  <NavGroup align="end">
15
25
  <NavLink href="/">Home</NavLink>
16
26
  <ThemeToggle aria-label="Toggle theme" />
@@ -1,4 +1,7 @@
1
1
  export function NotFoundPage() {
2
- return <main><h1>Not found</h1></main>;
2
+ return (
3
+ <main>
4
+ <h1>Not found</h1>
5
+ </main>
6
+ );
3
7
  }
4
-