@ultimat3/cli 27.14.1 → 27.16.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/cli",
3
- "version": "27.14.1",
3
+ "version": "27.16.0",
4
4
  "description": "The `x` binary: new, dev, build, verify, generate, db, mcp, doctor, deploy",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -39,33 +39,33 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@babel/core": "^8.0.1",
42
- "@ultimat3/action": "27.14.1",
43
- "@ultimat3/admin": "27.14.1",
44
- "@ultimat3/ai": "27.14.1",
45
- "@ultimat3/auth": "27.14.1",
46
- "@ultimat3/cache": "27.14.1",
47
- "@ultimat3/core": "27.14.1",
48
- "@ultimat3/db": "27.14.1",
49
- "@ultimat3/entity": "27.14.1",
50
- "@ultimat3/flags": "27.14.1",
51
- "@ultimat3/http": "27.14.1",
52
- "@ultimat3/i18n": "27.14.1",
53
- "@ultimat3/jobs": "27.14.1",
54
- "@ultimat3/mail": "27.14.1",
55
- "@ultimat3/manifest": "27.14.1",
56
- "@ultimat3/mcp": "27.14.1",
57
- "@ultimat3/money": "27.14.1",
58
- "@ultimat3/notify": "27.14.1",
59
- "@ultimat3/policy": "27.14.1",
60
- "@ultimat3/pwa": "27.14.1",
61
- "@ultimat3/query": "27.14.1",
62
- "@ultimat3/realtime": "27.14.1",
63
- "@ultimat3/render": "27.14.1",
64
- "@ultimat3/schema": "27.14.1",
65
- "@ultimat3/seo": "27.14.1",
66
- "@ultimat3/storage": "27.14.1",
67
- "@ultimat3/testing": "27.14.1",
68
- "@ultimat3/time": "27.14.1",
42
+ "@ultimat3/action": "27.16.0",
43
+ "@ultimat3/admin": "27.16.0",
44
+ "@ultimat3/ai": "27.16.0",
45
+ "@ultimat3/auth": "27.16.0",
46
+ "@ultimat3/cache": "27.16.0",
47
+ "@ultimat3/core": "27.16.0",
48
+ "@ultimat3/db": "27.16.0",
49
+ "@ultimat3/entity": "27.16.0",
50
+ "@ultimat3/flags": "27.16.0",
51
+ "@ultimat3/http": "27.16.0",
52
+ "@ultimat3/i18n": "27.16.0",
53
+ "@ultimat3/jobs": "27.16.0",
54
+ "@ultimat3/mail": "27.16.0",
55
+ "@ultimat3/manifest": "27.16.0",
56
+ "@ultimat3/mcp": "27.16.0",
57
+ "@ultimat3/money": "27.16.0",
58
+ "@ultimat3/notify": "27.16.0",
59
+ "@ultimat3/policy": "27.16.0",
60
+ "@ultimat3/pwa": "27.16.0",
61
+ "@ultimat3/query": "27.16.0",
62
+ "@ultimat3/realtime": "27.16.0",
63
+ "@ultimat3/render": "27.16.0",
64
+ "@ultimat3/schema": "27.16.0",
65
+ "@ultimat3/seo": "27.16.0",
66
+ "@ultimat3/storage": "27.16.0",
67
+ "@ultimat3/testing": "27.16.0",
68
+ "@ultimat3/time": "27.16.0",
69
69
  "babel-preset-solid": "^1.9.15"
70
70
  }
71
71
  }
package/src/cmd-mcp.ts CHANGED
@@ -114,6 +114,9 @@ export async function startMcpHttp(host: CliMcpServer, port: number): Promise<Mc
114
114
  handle = Bun.serve({
115
115
  port,
116
116
  hostname: 'localhost',
117
+ // A tool call (`verify_run`, `tests_run`) runs for minutes and writes nothing until it
118
+ // answers; Bun's 10 s default cut it. No deadline here, so no idle cut (0 = disabled).
119
+ idleTimeout: 0,
117
120
  fetch: (request: Request, server): Response | Promise<Response> => {
118
121
  const url = new URL(request.url);
119
122
  if (request.method !== route.method || url.pathname !== route.path) {
@@ -18,6 +18,8 @@ export const FOLLOW_ONLY_EXPORTS: readonly string[] = [
18
18
  'usePresence',
19
19
  'useConnection',
20
20
  'hasPageSocket',
21
+ // Hears THAT records moved and renders none of them, so a restored store contradicts nothing.
22
+ 'useRecordChanges',
21
23
  'readPresence',
22
24
  'channel',
23
25
  'channelRef',
package/src/seo-routes.ts CHANGED
@@ -20,7 +20,7 @@ import { ROBOTS_PATH, SITEMAP_PATH, siteSeo } from './site-seo';
20
20
 
21
21
  export interface SeoRoutesOptions {
22
22
  readonly env: Readonly<Record<string, string | undefined>>;
23
- /** `site.origin`, `seo.robots.disallow` and `seo.sitemap` from `app.config.ts` (`loadSiteSettings`). */
23
+ /** `site.origin`, `seo.robots` and `seo.sitemap` from `app.config.ts` (`loadSiteSettings`). */
24
24
  readonly site?: SiteSettings;
25
25
  /** The app root route files are relative to — `seo.sitemap.lastmod: 'git' | 'mtime'` reads them. */
26
26
  readonly root?: string;
@@ -84,6 +84,7 @@ export function seoRoutes(options: SeoRoutesOptions): readonly Route[] {
84
84
  baseUrl,
85
85
  environment,
86
86
  disallow: options.site?.disallow ?? [],
87
+ robotsGroups: options.site?.robotsGroups ?? [],
87
88
  ...(options.site === undefined ? {} : { sitemap: options.site.sitemap }),
88
89
  ...(options.root === undefined ? {} : { root: options.root }),
89
90
  });
@@ -1,9 +1,9 @@
1
- // The public origin and the crawler rules out of `app.config.ts` — `site.origin` and
2
- // `seo.robots.disallow` — and the one resolution of "which origin is this site served on" every
1
+ // The public origin and the crawler rules out of `app.config.ts` — `site.origin`,
2
+ // `seo.robots.disallow` and `seo.robots.groups` — and the one resolution of "which origin is this site served on" every
3
3
  // absolute URL a document, a sitemap or a robots file carries is built against. Read off the one
4
4
  // loader (`app-config-load.ts`).
5
5
 
6
- import type { Environment, SeoSitemapConfig } from '@ultimat3/core';
6
+ import type { Environment, SeoRobotsGroup, SeoSitemapConfig } from '@ultimat3/core';
7
7
  import { loadAppConfig } from './app-config-load';
8
8
 
9
9
  export interface SiteSettings {
@@ -11,6 +11,11 @@ export interface SiteSettings {
11
11
  readonly origin: string | null;
12
12
  /** `seo.robots.disallow` — the paths a production `robots.txt` keeps crawlers out of. */
13
13
  readonly disallow: readonly string[];
14
+ /**
15
+ * `seo.robots.groups` — per-crawler `User-agent` groups; empty means one `*` group. Optional so
16
+ * a `SiteSettings` literal written before it existed still compiles.
17
+ */
18
+ readonly robotsGroups?: readonly SeoRobotsGroup[] | undefined;
14
19
  /** `seo.sitemap` — public `app/` pages to list, where `<lastmod>` comes from, sections, page size. */
15
20
  readonly sitemap: SeoSitemapConfig;
16
21
  }
@@ -18,6 +23,7 @@ export interface SiteSettings {
18
23
  export const NO_SITE_SETTINGS: SiteSettings = {
19
24
  origin: null,
20
25
  disallow: [],
26
+ robotsGroups: [],
21
27
  sitemap: { extra: [], lastmod: 'none', sections: [], pageSize: 50_000 },
22
28
  };
23
29
 
@@ -30,6 +36,7 @@ export async function loadSiteSettings(root: string): Promise<SiteSettings> {
30
36
  return {
31
37
  origin: origin === null ? null : trimSlash(origin),
32
38
  disallow: [...config.seo.robots.disallow],
39
+ robotsGroups: [...(config.seo.robots.groups ?? [])],
33
40
  sitemap: config.seo.sitemap,
34
41
  };
35
42
  }
package/src/site-seo.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // (`apps/web/prerender.ts` writes it into the artifact) and the running web role (`seo-routes.ts`
3
3
  // serves it), so a crawler reads the same two files from a CDN and from a container.
4
4
 
5
- import type { Environment, SeoSitemapConfig } from '@ultimat3/core';
5
+ import type { Environment, SeoRobotsGroup, SeoSitemapConfig } from '@ultimat3/core';
6
6
  import { UltimateError } from '@ultimat3/core';
7
7
  import { localizedPath, unlocalizedPath } from '@ultimat3/i18n';
8
8
  import { appLocaleSet } from '@ultimat3/i18n/app-catalogs';
@@ -34,6 +34,11 @@ export interface SiteSeoOptions {
34
34
  * `User-agent: *` group; a non-production `robots.txt` still disallows everything.
35
35
  */
36
36
  readonly disallow?: readonly string[] | undefined;
37
+ /**
38
+ * `seo.robots.groups` from `app.config.ts` (`loadSiteSettings`): the production file's per-crawler
39
+ * groups. They shape `robots.txt` alone — the sitemap is filtered by `disallow`, every crawler's.
40
+ */
41
+ readonly robotsGroups?: readonly SeoRobotsGroup[] | undefined;
37
42
  /**
38
43
  * `seo.sitemap` from `app.config.ts` (`loadSiteSettings`): the public `app/` pages to list beside
39
44
  * the `site/` ones, where each `<lastmod>` comes from, and the sections and page size of an
@@ -237,6 +242,7 @@ export async function siteSeo(options: SiteSeoOptions): Promise<SiteSeo> {
237
242
  baseUrl: options.baseUrl,
238
243
  sitemaps: [SITEMAP_PATH],
239
244
  ...(options.disallow === undefined ? {} : { disallow: options.disallow }),
245
+ ...(options.robotsGroups === undefined ? {} : { groups: options.robotsGroups }),
240
246
  ...(options.environment === undefined ? {} : { environment: options.environment }),
241
247
  });
242
248
  return { robots, sitemaps };
@@ -101,13 +101,14 @@ const origin = Bun.env.SITE_ORIGIN;
101
101
  * \`Disallow: /\` and advertises no sitemap — so a preview build cannot outrank the real site.
102
102
  */
103
103
  async function writeSeoFiles(report: PrerenderReport): Promise<readonly string[]> {
104
- // The same disallow list and sitemap settings the running server answers with
105
- // (app.config.ts seo.robots.disallow and seo.sitemap).
104
+ // The same crawler rules and sitemap settings the running server answers with
105
+ // (app.config.ts seo.robots.disallow, seo.robots.groups and seo.sitemap).
106
106
  const settings = await loadSiteSettings(root);
107
107
  const seo = await siteSeo({
108
108
  // The origin the pages were built against, so the sitemap and every canonical agree.
109
109
  baseUrl: report.origin,
110
110
  disallow: settings.disallow,
111
+ robotsGroups: settings.robotsGroups,
111
112
  sitemap: settings.sitemap,
112
113
  root,
113
114
  pagesFor: (route) =>