@getstrata/cli 1.1.7 → 1.1.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @getstrata/cli changelog
2
2
 
3
+ ## 1.1.9
4
+
5
+ - Defer model events until the surrounding transaction commits
6
+
7
+ ## 1.1.8
8
+
9
+ - Exclude HTML and SPA routes from generated OpenAPI specs
10
+
11
+ ## Unreleased
12
+
13
+ - `openapi:generate` (and validate/check) keep JSON API and health routes and drop HTML-only (`web` without `api`) and SPA-prefix paths instead of restamping the full hybrid table as API.
14
+
3
15
  ## 1.1.7
4
16
 
5
17
  - Fix generated webhook SQL, billing hook, and hybrid SPA smoke
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/cli",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "description": "Strata CLI: framework commands for Bun apps (dev, start, migrate, run)",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/openapi.ts CHANGED
@@ -1,22 +1,65 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
3
  import { registerOpenApiRouteMap } from "@getstrata/bootstrap/buildModuleRoutes";
4
- import { routeRegistry } from "@getstrata/bootstrap/routeRegistry";
4
+ import { type RegisteredRoute, routeRegistry } from "@getstrata/bootstrap/routeRegistry";
5
5
  import { generateOpenApiSpec, renderOpenApiDocument } from "@getstrata/core/openapi/generator";
6
6
  import { validateOpenApiSpec } from "@getstrata/core/openapi/validate";
7
+ import { readSpaPrefix } from "@getstrata/core/runtime/frontendMode";
7
8
 
8
9
  type AppBootstrap = () => Promise<{ routes: Record<string, unknown> }>;
9
10
 
10
- function withoutSpaCatchAll(routes: Record<string, unknown>): Record<string, unknown> {
11
- const next = { ...routes };
12
- delete next["/*"];
13
- return next;
11
+ function isSpaDocumentedPath(path: string, spaPrefix: string): boolean {
12
+ return path === "/*" || path === spaPrefix || path.startsWith(`${spaPrefix}/`);
13
+ }
14
+
15
+ function isWebOnlyPath(metadata: RegisteredRoute[]): boolean {
16
+ return (
17
+ metadata.some((route) => route.middleware.includes("web")) &&
18
+ !metadata.some((route) => route.middleware.includes("api"))
19
+ );
20
+ }
21
+
22
+ function shouldKeepOpenApiPath(
23
+ path: string,
24
+ registered: RegisteredRoute[],
25
+ spaPrefix: string,
26
+ ): boolean {
27
+ if (isSpaDocumentedPath(path, spaPrefix)) {
28
+ return false;
29
+ }
30
+
31
+ const metadata = registered.filter((route) => route.path === path);
32
+ return !isWebOnlyPath(metadata);
14
33
  }
15
34
 
16
35
  async function registerAppOpenApiRoutes(bootstrap: AppBootstrap): Promise<void> {
36
+ routeRegistry.clear();
17
37
  const { routes } = await bootstrap();
38
+ const registered = routeRegistry.list();
39
+ const spaPrefix = readSpaPrefix();
40
+ const keepPaths = new Set<string>();
41
+
42
+ for (const path of Object.keys(routes)) {
43
+ if (shouldKeepOpenApiPath(path, registered, spaPrefix)) {
44
+ keepPaths.add(path);
45
+ }
46
+ }
47
+
48
+ const keptRegistered = registered.filter((route) => keepPaths.has(route.path));
49
+ const registeredKeepPaths = new Set(keptRegistered.map((route) => route.path));
50
+ const unregisteredKeepers: Record<string, unknown> = {};
51
+
52
+ for (const path of keepPaths) {
53
+ if (!registeredKeepPaths.has(path)) {
54
+ unregisteredKeepers[path] = routes[path];
55
+ }
56
+ }
57
+
18
58
  routeRegistry.clear();
19
- registerOpenApiRouteMap(withoutSpaCatchAll(routes), ["global", "api"]);
59
+ for (const route of keptRegistered) {
60
+ routeRegistry.register(route);
61
+ }
62
+ registerOpenApiRouteMap(unregisteredKeepers, ["global", "api"]);
20
63
  }
21
64
 
22
65
  function createOpenApiGenerateCommand(bootstrap: AppBootstrap) {