@mesh-tech/mesh-cli 0.35.1 → 0.35.2

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.
@@ -112,8 +112,9 @@ const config = new mesh.apps.AppConfig("config", {
112
112
  // Events and webhooks: the service, its own database, an app-scoped KMS key
113
113
  // for endpoint credentials, the publisher key, the Zitadel introspection app,
114
114
  // the delivery task queue, the worker registration that lists the app's
115
- // Temporal namespace on the Hub's Workflows page, and the registry surface —
116
- // all children of this one declaration.
115
+ // Temporal namespace on the Hub's Workflows page (with the service's own
116
+ // housekeeping workflows marked internal, so only staff see them there), and
117
+ // the registry surface — all children of this one declaration.
117
118
  //
118
119
  // Its database's schema migration gates the deploy: `pulumi up` waits for the
119
120
  // migration Job and fails when it fails, and the service's pods are held until
@@ -171,6 +172,9 @@ const api = new mesh.apps.Service("api", {
171
172
  // Drives readiness, liveness and the Hub uptime sampler on BOTH ingress kinds.
172
173
  // `ingress.healthCheckPath` sets the ALB target group's path only, and a
173
174
  // gateway (HTTPRoute) tenant has no target group — so the path belongs here.
175
+ // A boot that can outlast the liveness window (a busy node, a tight CPU
176
+ // limit) takes `healthCheck: { path: "/health", startup: {} }`: a startup
177
+ // probe that holds liveness for up to five minutes of boot.
174
178
  healthCheck: "/health",
175
179
  replicas: deploy ? 2 : 1,
176
180
  resources: { cpu: "100m", memory: "128Mi" },
@@ -219,7 +223,10 @@ const worker = new mesh.apps.TemporalWorker("worker", {
219
223
  // Hub on the ports it published and refuses it on every other one, so a
220
224
  // registry entry cannot walk the Hub onto a port the surface never opened.
221
225
  // Nothing here declares that — it rides the surface (`hubDialPolicy`, on by
222
- // default wherever the namespace is in the service mesh).
226
+ // default wherever the namespace is in the service mesh). A surface fronting
227
+ // a vendor the tenant already declares names that declaration with
228
+ // `externalService`; its registry entry publishes the name, and the Hub reads
229
+ // the app's Core connection from the vendor's shared health measurement.
223
230
  // =============================================================================
224
231
 
225
232
  new mesh.apps.ApiDocs("docs", {
@@ -17,10 +17,8 @@
17
17
  "@aws-sdk/client-s3": "^3.700.0",
18
18
  {{/if}}
19
19
  "@hono/node-server": "^1.13.0",
20
- "@mesh-tech/logger": "{{#if workspaceDeps}}workspace:*{{else}}{{meshRange}}{{/if}}",
21
- {{#if temporal}}
22
20
  "@mesh-tech/app-kit": "{{#if workspaceDeps}}workspace:*{{else}}{{meshRange}}{{/if}}",
23
- {{/if}}
21
+ "@mesh-tech/logger": "{{#if workspaceDeps}}workspace:*{{else}}{{meshRange}}{{/if}}",
24
22
  {{#if database}}
25
23
  "@prisma/adapter-pg": "^6.0.0",
26
24
  "@prisma/client": "^6.0.0",
@@ -19,6 +19,7 @@
19
19
 
20
20
  import { Hono } from "hono";
21
21
  import { serve } from "@hono/node-server";
22
+ import { configureServerKeepAlive } from "@mesh-tech/app-kit/outbound-http";
22
23
  import { createLogger, requestLogger } from "@mesh-tech/logger";
23
24
  {{#if temporal}}
24
25
  import { createTemporalClient } from "@mesh-tech/app-kit/temporal-client";
@@ -82,6 +83,13 @@ app.post("/things", async (c) => {
82
83
 
83
84
  {{/if}}
84
85
  // Start server
85
- serve({ fetch: app.fetch, port: PORT, hostname: "0.0.0.0" }, (info) => {
86
- log.info({ port: info.port }, "{{titleCase name}} API listening");
87
- });
86
+ // Hold idle keep-alive sockets for 75s rather than Node's 5s default. The
87
+ // proxy in front of this service pools connections to it and keeps them idle
88
+ // longer than 5s (the Istio gateway's Envoy, through ztunnel, or an ALB for
89
+ // 60s), so at the default the server keeps closing sockets the pool may be
90
+ // about to reuse.
91
+ configureServerKeepAlive(
92
+ serve({ fetch: app.fetch, port: PORT, hostname: "0.0.0.0" }, (info) => {
93
+ log.info({ port: info.port }, "{{titleCase name}} API listening");
94
+ }),
95
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mesh-tech/mesh-cli",
3
- "version": "0.35.1",
3
+ "version": "0.35.2",
4
4
  "description": "CLI for Mesh platform development utilities",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "devDependencies": {
44
44
  "@mesh-tech/agent-contracts": "0.2.0",
45
- "@mesh-tech/tsconfig": "2.35.0",
45
+ "@mesh-tech/tsconfig": "2.36.0",
46
46
  "@mesh-tech/workflow-viz": "0.3.1",
47
47
  "@tanstack/intent": "^0.3.6",
48
48
  "@types/node": "^20.0.0",
@@ -11,6 +11,7 @@
11
11
  "seed:authz": "tsx scripts/seed-authz.ts"
12
12
  },
13
13
  "dependencies": {
14
+ "@mesh-tech/app-kit": "latest",
14
15
  "@mesh-tech/authn": "latest",
15
16
  "@mesh-tech/authz": "latest",
16
17
  "@mesh-tech/logger": "latest",
@@ -15,6 +15,7 @@
15
15
 
16
16
  import { Hono } from "hono";
17
17
  import { serve } from "@hono/node-server";
18
+ import { configureServerKeepAlive } from "@mesh-tech/app-kit/outbound-http";
18
19
  import { createAuthn } from "@mesh-tech/authn";
19
20
  import { zitadelJwtScheme } from "@mesh-tech/authn/schemes/zitadel-jwt";
20
21
  import { createAuthnMiddleware, type AuthnVariables } from "@mesh-tech/authn/hono";
@@ -102,6 +103,13 @@ app.get("/api/report", m.require("report", "view", { id: () => "quarterly" }), (
102
103
  c.json({ id: "quarterly", title: "Quarterly Report", classification: "internal" }),
103
104
  );
104
105
 
105
- serve({ fetch: app.fetch, port: PORT }, () => {
106
- log.info({ port: PORT, issuer: ZITADEL_ISSUER }, "{{name}} api listening");
107
- });
106
+ // Hold idle keep-alive sockets for 75s rather than Node's 5s default. The
107
+ // proxy in front of this service pools connections to it and keeps them idle
108
+ // longer than 5s (the Istio gateway's Envoy, through ztunnel, or an ALB for
109
+ // 60s), so at the default the server keeps closing sockets the pool may be
110
+ // about to reuse.
111
+ configureServerKeepAlive(
112
+ serve({ fetch: app.fetch, port: PORT }, () => {
113
+ log.info({ port: PORT, issuer: ZITADEL_ISSUER }, "{{name}} api listening");
114
+ }),
115
+ );
@@ -109,6 +109,9 @@ const api = new mesh.apps.Service("api", {
109
109
  // Drives readiness, liveness and the Hub uptime sampler on BOTH ingress kinds.
110
110
  // `ingress.healthCheckPath` sets the ALB target group's path only, and a
111
111
  // gateway (HTTPRoute) tenant has no target group — so the path belongs here.
112
+ // A boot that can outlast the liveness window (a busy node, a tight CPU
113
+ // limit) takes `healthCheck: { path: "/health", startup: {} }`: a startup
114
+ // probe that holds liveness for up to five minutes of boot.
112
115
  healthCheck: "/health",
113
116
  replicas: 1,
114
117
  resources: { cpu: "100m", memory: "128Mi" },
@@ -14,6 +14,7 @@
14
14
  "check-types": "tsc --noEmit"
15
15
  },
16
16
  "dependencies": {
17
+ "@mesh-tech/app-kit": "latest",
17
18
  "@mesh-tech/authn": "latest",
18
19
  "@mesh-tech/authz": "latest",
19
20
  "@mesh-tech/logger": "latest",
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { serve } from "@hono/node-server";
10
10
  import { Hono } from "hono";
11
+ import { configureServerKeepAlive } from "@mesh-tech/app-kit/outbound-http";
11
12
  import { createAuthnMiddleware, type AuthnVariables } from "@mesh-tech/authn/hono";
12
13
  import { createAuthzMiddleware, type AuthzVariables } from "@mesh-tech/authz/hono";
13
14
  import { createLogger, requestLogger } from "@mesh-tech/logger";
@@ -58,6 +59,11 @@ export const createApp = (deps: { testAuth?: TestAuth } = {}) => {
58
59
 
59
60
  if (process.env.NODE_ENV !== "test" && process.argv[1]?.endsWith("main.js")) {
60
61
  const port = Number(process.env.PORT ?? 3000);
61
- serve({ fetch: createApp().fetch, port });
62
+ // Hold idle keep-alive sockets for 75s rather than Node's 5s default. The
63
+ // proxy in front of this service pools connections to it and keeps them idle
64
+ // longer than 5s (the Istio gateway's Envoy, through ztunnel, or an ALB for
65
+ // 60s), so at the default the server keeps closing sockets the pool may be
66
+ // about to reuse.
67
+ configureServerKeepAlive(serve({ fetch: createApp().fetch, port }));
62
68
  log.info({ port, mode: "role-gating" }, "{{name}} api listening");
63
69
  }
@@ -85,6 +85,9 @@ const api = new mesh.apps.Service("api", {
85
85
  // Drives readiness, liveness and the Hub uptime sampler on BOTH ingress kinds.
86
86
  // `ingress.healthCheckPath` sets the ALB target group's path only, and a
87
87
  // gateway (HTTPRoute) tenant has no target group — so the path belongs here.
88
+ // A boot that can outlast the liveness window (a busy node, a tight CPU
89
+ // limit) takes `healthCheck: { path: "/health", startup: {} }`: a startup
90
+ // probe that holds liveness for up to five minutes of boot.
88
91
  healthCheck: "/health",
89
92
  replicas: 1,
90
93
  resources: { cpu: "100m", memory: "128Mi" },
@@ -17,6 +17,7 @@
17
17
  import { serve } from "@hono/node-server";
18
18
  import { Hono } from "hono";
19
19
  import { resolveCredentials } from "@mesh-tech/app-kit/credentials";
20
+ import { configureServerKeepAlive } from "@mesh-tech/app-kit/outbound-http";
20
21
  import { traceExternalCall } from "@mesh-tech/app-kit/tracing";
21
22
  import { enhanceFetch } from "@mesh-tech/http-client";
22
23
  import { createLogger, requestLogger } from "@mesh-tech/logger";
@@ -81,6 +82,13 @@ app.post("/payments/quote", async (c) => {
81
82
  });
82
83
 
83
84
  const port = Number(process.env.PORT ?? 3000);
84
- serve({ fetch: app.fetch, port }, () => {
85
- log.info({ port }, "{{titleCase name}} API listening");
86
- });
85
+ // Hold idle keep-alive sockets for 75s rather than Node's 5s default. The
86
+ // proxy in front of this service pools connections to it and keeps them idle
87
+ // longer than 5s (the Istio gateway's Envoy, through ztunnel, or an ALB for
88
+ // 60s), so at the default the server keeps closing sockets the pool may be
89
+ // about to reuse.
90
+ configureServerKeepAlive(
91
+ serve({ fetch: app.fetch, port }, () => {
92
+ log.info({ port }, "{{titleCase name}} API listening");
93
+ }),
94
+ );
@@ -75,6 +75,9 @@ const api = new mesh.apps.Service("api", {
75
75
  // Drives readiness, liveness and the Hub uptime sampler on BOTH ingress kinds.
76
76
  // `ingress.healthCheckPath` sets the ALB target group's path only, and a
77
77
  // gateway (HTTPRoute) tenant has no target group — so the path belongs here.
78
+ // A boot that can outlast the liveness window (a busy node, a tight CPU
79
+ // limit) takes `healthCheck: { path: "/health", startup: {} }`: a startup
80
+ // probe that holds liveness for up to five minutes of boot.
78
81
  healthCheck: "/health",
79
82
  replicas: 1,
80
83
  resources: { cpu: "100m", memory: "128Mi" },
@@ -11,6 +11,7 @@
11
11
 
12
12
  import { Hono } from "hono";
13
13
  import { serve } from "@hono/node-server";
14
+ import { configureServerKeepAlive } from "@mesh-tech/app-kit/outbound-http";
14
15
  import { createTemporalClient, type WithClient } from "@mesh-tech/app-kit/temporal-client";
15
16
  import { createLogger, requestLogger } from "@mesh-tech/logger";
16
17
 
@@ -125,9 +126,16 @@ app.get("/workflows/:id", async (c) => {
125
126
  // Start server
126
127
  initClient()
127
128
  .then(() => {
128
- serve({ fetch: app.fetch, port: PORT, hostname: "0.0.0.0" }, () => {
129
- log.info({ port: PORT }, "{{titleCase name}} API listening");
130
- });
129
+ // Hold idle keep-alive sockets for 75s rather than Node's 5s default. The
130
+ // proxy in front of this service pools connections to it and keeps them idle
131
+ // longer than 5s (the Istio gateway's Envoy, through ztunnel, or an ALB for
132
+ // 60s), so at the default the server keeps closing sockets the pool may be
133
+ // about to reuse.
134
+ configureServerKeepAlive(
135
+ serve({ fetch: app.fetch, port: PORT, hostname: "0.0.0.0" }, () => {
136
+ log.info({ port: PORT }, "{{titleCase name}} API listening");
137
+ }),
138
+ );
131
139
  })
132
140
  .catch((err) => {
133
141
  log.error({ err }, "Failed to initialize");
@@ -47,6 +47,9 @@ const api = new mesh.apps.Service("api", {
47
47
  // Drives readiness, liveness and the Hub uptime sampler on BOTH ingress kinds.
48
48
  // `ingress.healthCheckPath` sets the ALB target group's path only, and a
49
49
  // gateway (HTTPRoute) tenant has no target group — so the path belongs here.
50
+ // A boot that can outlast the liveness window (a busy node, a tight CPU
51
+ // limit) takes `healthCheck: { path: "/health", startup: {} }`: a startup
52
+ // probe that holds liveness for up to five minutes of boot.
50
53
  healthCheck: "/health",
51
54
  replicas: 1,
52
55
  resources: { cpu: "100m", memory: "128Mi" },