@cat-factory/app 0.261.4 → 0.261.6

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.
@@ -65,6 +65,9 @@ const form = reactive({
65
65
  imageTemplate: '',
66
66
  urlSource: 'ingressTemplate' as UrlSource,
67
67
  hostTemplate: '',
68
+ // The ingress-template port, separate from `servicePort`: each url field belongs to exactly ONE
69
+ // variant, so a value entered for one source can never populate a config built for another.
70
+ ingressPort: '',
68
71
  ingressName: '',
69
72
  serviceName: '',
70
73
  servicePort: '',
@@ -121,28 +124,34 @@ watch(
121
124
  form.insecureSkipTlsVerify = k.insecureSkipTlsVerify === true
122
125
  form.namespaceTemplate = k.namespaceTemplate ?? ''
123
126
  form.imageTemplate = k.imageTemplate ?? ''
124
- // Each url field is read off the ONE variant that carries it, so a field belonging to a
125
- // different `source` cannot silently populate the form.
126
- //
127
- // Typed as present with an on-union `source`, read as neither: both were true when the
128
- // connect form admitted this config, and the value has been through storage since — which is
129
- // exactly why the backend re-parses a stored `providerConfig` rather than asserting it, and
130
- // this form is where an operator REPAIRS one that drifted. An unrecognised source falls back
131
- // to the form's default, because `buildUrl` has no branch to build a config out of one.
132
- const url: KubeUrlSource | undefined = k.url
133
- const source = url?.source
134
- form.urlSource = isKubernetesUrlSource(source) ? source : 'ingressTemplate'
135
- form.hostTemplate = url?.source === 'ingressTemplate' ? url.hostTemplate : ''
136
- form.ingressName = url?.source === 'ingressStatus' ? (url.ingressName ?? '') : ''
137
- form.serviceName = url?.source === 'serviceStatus' ? url.serviceName : ''
138
- form.servicePort = url?.source === 'serviceStatus' && url.port != null ? String(url.port) : ''
139
- form.gatewayName = url?.source === 'gatewayStatus' ? (url.gatewayName ?? '') : ''
140
- form.httpRouteName = url?.source === 'httpRouteStatus' ? (url.httpRouteName ?? '') : ''
141
- form.urlScheme = url?.scheme ?? 'default'
127
+ applyStoredUrlSource(k.url)
142
128
  },
143
129
  { immediate: true },
144
130
  )
145
131
 
132
+ /**
133
+ * Prefill the URL-derivation fields from a stored config. Each field is read off the ONE variant
134
+ * that carries it, so a field belonging to a different `source` cannot silently populate the form.
135
+ *
136
+ * Typed as present with an on-union `source`, read as neither: both were true when the connect form
137
+ * admitted this config, and the value has been through storage since, which is exactly why the
138
+ * backend re-parses a stored `providerConfig` rather than asserting it, and this form is where an
139
+ * operator REPAIRS one that drifted. An unrecognised source falls back to the form's default,
140
+ * because `buildUrl` has no branch to build a config out of one.
141
+ */
142
+ function applyStoredUrlSource(url: KubeUrlSource | undefined): void {
143
+ const source = url?.source
144
+ form.urlSource = isKubernetesUrlSource(source) ? source : 'ingressTemplate'
145
+ form.hostTemplate = url?.source === 'ingressTemplate' ? url.hostTemplate : ''
146
+ form.ingressPort = url?.source === 'ingressTemplate' && url.port != null ? String(url.port) : ''
147
+ form.ingressName = url?.source === 'ingressStatus' ? (url.ingressName ?? '') : ''
148
+ form.serviceName = url?.source === 'serviceStatus' ? url.serviceName : ''
149
+ form.servicePort = url?.source === 'serviceStatus' && url.port != null ? String(url.port) : ''
150
+ form.gatewayName = url?.source === 'gatewayStatus' ? (url.gatewayName ?? '') : ''
151
+ form.httpRouteName = url?.source === 'httpRouteStatus' ? (url.httpRouteName ?? '') : ''
152
+ form.urlScheme = url?.scheme ?? 'default'
153
+ }
154
+
146
155
  // The local-cluster apiserver address every loopback distro (k3s / k3d / kind / minikube)
147
156
  // exposes by default — see `seedForEngine`.
148
157
  const LOCAL_K3S_API_SERVER = 'https://127.0.0.1:6443'
@@ -185,22 +194,33 @@ watch(
185
194
  if (prefill.insecureSkipTlsVerify !== undefined)
186
195
  form.insecureSkipTlsVerify = prefill.insecureSkipTlsVerify
187
196
  if (prefill.namespaceTemplate.trim()) form.namespaceTemplate = prefill.namespaceTemplate.trim()
197
+ // An EMPTY host template is meaningful, not a gap the link forgot to fill: the CLI withholds
198
+ // it when it could not establish that the cluster serves an ingress-derived URL, and leaving
199
+ // the required field blank is what stops a URL nothing answers being saved.
188
200
  if (prefill.hostTemplate.trim()) {
189
201
  form.urlSource = 'ingressTemplate'
190
202
  form.hostTemplate = prefill.hostTemplate.trim()
203
+ // Carried alongside the template, never inside it: a local cluster publishing its controller
204
+ // on 18080 needs the port in the URL and NOT in the Ingress host the manifests declare.
205
+ form.ingressPort = prefill.ingressPort.trim()
191
206
  }
207
+ if (prefill.urlScheme) form.urlScheme = prefill.urlScheme
192
208
  },
193
209
  { immediate: true },
194
210
  )
195
211
 
196
- const servicePortValid = computed(() => {
197
- const raw = form.servicePort.trim()
198
- if (!raw) return true
199
- const port = Number(raw)
212
+ /** A blank port is valid (the scheme's default); anything typed has to be a real port number. */
213
+ function portValid(raw: string): boolean {
214
+ const trimmed = raw.trim()
215
+ if (!trimmed) return true
216
+ const port = Number(trimmed)
200
217
  return Number.isInteger(port) && port >= 1 && port <= 65535
201
- })
218
+ }
219
+ const servicePortValid = computed(() => portValid(form.servicePort))
220
+ const ingressPortValid = computed(() => portValid(form.ingressPort))
202
221
  const urlValid = computed(() => {
203
- if (form.urlSource === 'ingressTemplate') return !!form.hostTemplate.trim()
222
+ if (form.urlSource === 'ingressTemplate')
223
+ return !!form.hostTemplate.trim() && ingressPortValid.value
204
224
  if (form.urlSource === 'serviceStatus') return !!form.serviceName.trim() && servicePortValid.value
205
225
  return true // ingressStatus / gatewayStatus / httpRouteStatus have no required field
206
226
  })
@@ -257,8 +277,15 @@ const connectBlockedReason = computed(() => {
257
277
  function buildUrl(): KubeUrlSource {
258
278
  const scheme = form.urlScheme === 'default' ? {} : { scheme: form.urlScheme }
259
279
  switch (form.urlSource) {
260
- case 'ingressTemplate':
261
- return { source: 'ingressTemplate', hostTemplate: form.hostTemplate.trim(), ...scheme }
280
+ case 'ingressTemplate': {
281
+ const port = Number(form.ingressPort)
282
+ return {
283
+ source: 'ingressTemplate',
284
+ hostTemplate: form.hostTemplate.trim(),
285
+ ...(form.ingressPort.trim() && Number.isInteger(port) ? { port } : {}),
286
+ ...scheme,
287
+ }
288
+ }
262
289
  case 'ingressStatus': {
263
290
  const ingressName = form.ingressName.trim()
264
291
  return { source: 'ingressStatus', ...(ingressName ? { ingressName } : {}), ...scheme }
@@ -456,6 +483,24 @@ async function copyAutoSetupCommand() {
456
483
  />
457
484
  </UFormField>
458
485
 
486
+ <!-- The host port the controller answers on, when it is not the scheme's default. Separate from
487
+ the template on purpose: the rendered template is also the Ingress `host` the manifests
488
+ declare, and Kubernetes rejects a `host` with a port in it. -->
489
+ <UFormField
490
+ v-if="form.urlSource === 'ingressTemplate'"
491
+ :label="optional(t('settings.infrastructure.kubernetesEngine.port'))"
492
+ :help="t('settings.infrastructure.kubernetesEngine.ingressPortHelp')"
493
+ >
494
+ <UInput
495
+ v-model="form.ingressPort"
496
+ type="number"
497
+ :min="1"
498
+ :max="65535"
499
+ class="font-mono"
500
+ placeholder="80"
501
+ />
502
+ </UFormField>
503
+
459
504
  <UFormField
460
505
  v-if="form.urlSource === 'ingressStatus'"
461
506
  :label="optional(t('settings.infrastructure.kubernetesEngine.ingressName'))"
@@ -46,6 +46,9 @@ const form = reactive({
46
46
  // url derivation
47
47
  urlSource: 'ingressTemplate' as 'ingressTemplate' | 'ingressStatus' | 'serviceStatus',
48
48
  hostTemplate: '',
49
+ // The ingress-template port, its own field because the rendered template is also the Ingress
50
+ // `host` a service's manifests declare, and Kubernetes rejects a `host` carrying a port.
51
+ ingressPort: '',
49
52
  ingressName: '',
50
53
  serviceName: '',
51
54
  servicePort: '',
@@ -111,6 +114,7 @@ function applyUrl(k: Record<string, unknown>): void {
111
114
  if (url?.source === 'ingressTemplate') {
112
115
  form.urlSource = 'ingressTemplate'
113
116
  form.hostTemplate = readString(url.hostTemplate)
117
+ form.ingressPort = typeof url.port === 'number' ? String(url.port) : ''
114
118
  } else if (url?.source === 'ingressStatus') {
115
119
  form.urlSource = 'ingressStatus'
116
120
  form.ingressName = readString(url.ingressName)
@@ -154,16 +158,20 @@ const manifestSourceValid = computed(() =>
154
158
  ? repoShapeValid.value && !!form.manifestPath.trim()
155
159
  : !!form.manifestPath.trim(),
156
160
  )
157
- // serviceStatus.port is an optional integer 1..65535 (kubernetesUrlSourceSchema). Validate
158
- // it here so a decimal isn't silently dropped and an out-of-range value isn't sent then 422'd.
159
- const servicePortValid = computed(() => {
160
- const raw = form.servicePort.trim()
161
- if (!raw) return true
162
- const port = Number(raw)
161
+ // Both `ingressTemplate.port` and `serviceStatus.port` are optional integers 1..65535
162
+ // (kubernetesUrlSourceSchema). Validate here so a decimal isn't silently dropped and an
163
+ // out-of-range value isn't sent then 422'd.
164
+ function portValid(raw: string): boolean {
165
+ const trimmed = raw.trim()
166
+ if (!trimmed) return true
167
+ const port = Number(trimmed)
163
168
  return Number.isInteger(port) && port >= 1 && port <= 65535
164
- })
169
+ }
170
+ const servicePortValid = computed(() => portValid(form.servicePort))
171
+ const ingressPortValid = computed(() => portValid(form.ingressPort))
165
172
  const urlValid = computed(() => {
166
- if (form.urlSource === 'ingressTemplate') return !!form.hostTemplate.trim()
173
+ if (form.urlSource === 'ingressTemplate')
174
+ return !!form.hostTemplate.trim() && ingressPortValid.value
167
175
  if (form.urlSource === 'serviceStatus') return !!form.serviceName.trim() && servicePortValid.value
168
176
  return true // ingressStatus has no required field
169
177
  })
@@ -219,6 +227,8 @@ function buildUrl(): Record<string, unknown> {
219
227
  const url: Record<string, unknown> = { source: form.urlSource }
220
228
  if (form.urlSource === 'ingressTemplate') {
221
229
  url.hostTemplate = form.hostTemplate.trim()
230
+ const port = Number(form.ingressPort)
231
+ if (form.ingressPort.trim() && Number.isInteger(port)) url.port = port
222
232
  } else if (form.urlSource === 'ingressStatus') {
223
233
  if (form.ingressName.trim()) url.ingressName = form.ingressName.trim()
224
234
  } else {
@@ -338,6 +348,23 @@ function optional(label: string): string {
338
348
  />
339
349
  </UFormField>
340
350
 
351
+ <!-- The host port the controller answers on, when it is not the scheme's default. Kept out of
352
+ the template because that value is also the Ingress `host` the manifests declare. -->
353
+ <UFormField
354
+ v-if="form.urlSource === 'ingressTemplate'"
355
+ :label="optional(t('settings.providerConnection.kubernetesEnv.port'))"
356
+ :help="t('settings.providerConnection.kubernetesEnv.ingressPortHelp')"
357
+ >
358
+ <UInput
359
+ v-model="form.ingressPort"
360
+ type="number"
361
+ :min="1"
362
+ :max="65535"
363
+ class="font-mono"
364
+ placeholder="80"
365
+ />
366
+ </UFormField>
367
+
341
368
  <UFormField
342
369
  v-if="form.urlSource === 'ingressStatus'"
343
370
  :label="optional(t('settings.providerConnection.kubernetesEnv.ingressName'))"
@@ -17,7 +17,15 @@ function openWith(search: string): void {
17
17
  const K3S_LINK =
18
18
  '?infraSetup=local-k3s&label=Local+k3s&apiServerUrl=https%3A%2F%2F127.0.0.1%3A6443' +
19
19
  '&namespaceTemplate=cf-env-%7B%7BpullNumber%7D%7D&hostTemplate=%7B%7Bbranch%7D%7D.127.0.0.1.nip.io' +
20
- '&insecureSkipTlsVerify=1'
20
+ '&scheme=http&insecureSkipTlsVerify=1'
21
+
22
+ /** The link a cluster published on a NON-default host port produces. */
23
+ const CUSTOM_PORT_LINK = `${K3S_LINK}&ingressPort=18080`
24
+
25
+ /** The link the CLI emits when it could NOT establish that the cluster serves ingress URLs. */
26
+ const NO_INGRESS_LINK =
27
+ '?infraSetup=local-k3s&label=Local+k3s&apiServerUrl=https%3A%2F%2F127.0.0.1%3A6443' +
28
+ '&namespaceTemplate=cf-env-%7B%7BpullNumber%7D%7D&insecureSkipTlsVerify=1'
21
29
 
22
30
  describe('consumeK3sSetupDeepLink', () => {
23
31
  beforeEach(() => {
@@ -39,10 +47,44 @@ describe('consumeK3sSetupDeepLink', () => {
39
47
  apiServerUrl: 'https://127.0.0.1:6443',
40
48
  namespaceTemplate: 'cf-env-{{pullNumber}}',
41
49
  hostTemplate: '{{branch}}.127.0.0.1.nip.io',
50
+ ingressPort: '',
51
+ urlScheme: 'http',
42
52
  insecureSkipTlsVerify: true,
43
53
  })
44
54
  })
45
55
 
56
+ it('carries a non-default ingress port SEPARATELY from the host template', () => {
57
+ // The template is also the Ingress `host` a service's manifests declare, and Kubernetes rejects
58
+ // a `host` with a port, so the port cannot ride inside it.
59
+ const ui = createUiModals()
60
+ openWith(CUSTOM_PORT_LINK)
61
+ ui.consumeK3sSetupDeepLink()
62
+
63
+ expect(ui.k3sSetupPrefill.value?.ingressPort).toBe('18080')
64
+ expect(ui.k3sSetupPrefill.value?.hostTemplate).toBe('{{branch}}.127.0.0.1.nip.io')
65
+ })
66
+
67
+ it('strips the ingress-port param too, so a reload does not re-seed it', () => {
68
+ const ui = createUiModals()
69
+ openWith(CUSTOM_PORT_LINK)
70
+ ui.consumeK3sSetupDeepLink()
71
+ expect(window.location.search).toBe('')
72
+ })
73
+
74
+ it('carries NO host template when the CLI could not verify the cluster serves one', () => {
75
+ // The CLI omits the param rather than prefilling a template the cluster cannot serve; the
76
+ // form treats it as required, so an empty value is what stops an unserved URL being saved.
77
+ const ui = createUiModals()
78
+ openWith(NO_INGRESS_LINK)
79
+ ui.consumeK3sSetupDeepLink()
80
+
81
+ expect(ui.k3sSetupPrefill.value?.hostTemplate).toBe('')
82
+ expect(ui.k3sSetupPrefill.value?.ingressPort).toBe('')
83
+ expect(ui.k3sSetupPrefill.value?.urlScheme).toBeUndefined()
84
+ // The rest of the prefill still lands: a withheld field is not a withheld form.
85
+ expect(ui.k3sSetupPrefill.value?.apiServerUrl).toBe('https://127.0.0.1:6443')
86
+ })
87
+
46
88
  it('strips the params so a reload neither re-opens the window nor re-anchors it', () => {
47
89
  const ui = createUiModals()
48
90
  openWith(K3S_LINK)
@@ -33,7 +33,21 @@ export interface K3sSetupPrefill {
33
33
  label: string
34
34
  apiServerUrl: string
35
35
  namespaceTemplate: string
36
+ /**
37
+ * Empty when the CLI could not establish that the cluster serves an ingress-derived URL, in
38
+ * which case it deliberately omits the param so this form does NOT prefill a host template
39
+ * nothing would answer. The field is required for an `ingressTemplate` source, so an empty
40
+ * value stops the unserved promise being saved.
41
+ */
36
42
  hostTemplate: string
43
+ /**
44
+ * The verified host port, as typed into the form's port field, or empty for the scheme default.
45
+ * Kept out of `hostTemplate` because that value is also the Ingress `host` a service's manifests
46
+ * declare, and Kubernetes rejects a `host` with a port in it.
47
+ */
48
+ ingressPort: string
49
+ /** Scheme the CLI verified. Absent ⇒ the form keeps its own default. */
50
+ urlScheme?: 'http' | 'https'
37
51
  // Absent when the link omitted the param, so the form keeps its engine default rather than
38
52
  // forcing verification back on (which would break a self-signed local cluster).
39
53
  insecureSkipTlsVerify?: boolean
@@ -795,6 +809,14 @@ function createInfraModals(resetHubReturn: ResetHubReturn) {
795
809
  apiServerUrl: params.get('apiServerUrl') ?? '',
796
810
  namespaceTemplate: params.get('namespaceTemplate') ?? '',
797
811
  hostTemplate: params.get('hostTemplate') ?? '',
812
+ // The host port the controller answers on, when it is not the scheme's default. It rides its
813
+ // own param rather than the host template because the rendered template is also the Ingress
814
+ // `host` the manifests declare, and Kubernetes rejects a `host` carrying a port.
815
+ ingressPort: params.get('ingressPort') ?? '',
816
+ // A local ingress controller serves TLS with a self-signed cert, so the CLI verifies (and
817
+ // links) a plain-HTTP environment URL. Without this the form would keep its `https`
818
+ // default and save a URL that fails on the certificate rather than on the connection.
819
+ urlScheme: params.get('scheme') === 'http' ? 'http' : undefined,
798
820
  // Only carry the flag the link actually set — a missing param leaves the form's engine
799
821
  // default (skip-TLS on for a local self-signed cluster) untouched.
800
822
  insecureSkipTlsVerify: params.has('insecureSkipTlsVerify')
@@ -814,6 +836,8 @@ function createInfraModals(resetHubReturn: ResetHubReturn) {
814
836
  'apiServerUrl',
815
837
  'namespaceTemplate',
816
838
  'hostTemplate',
839
+ 'ingressPort',
840
+ 'scheme',
817
841
  'insecureSkipTlsVerify',
818
842
  ]) {
819
843
  params.delete(key)
@@ -175,6 +175,7 @@
175
175
  "urlHttpRouteStatus": "HTTPRoute-Status (Gateway API)",
176
176
  "hostTemplate": "Host-Vorlage",
177
177
  "hostTemplateHelp": "Die Host-Vorlage, gerendert mit den Provisioning-Variablen (z. B. branch, sha).",
178
+ "ingressPortHelp": "Host-Port, auf dem der Ingress-Controller antwortet; leer bedeutet den Standardport des Schemas. Er gehört hierher und nicht in die Host-Vorlage, da ein Kubernetes-Ingress-Host keinen Port enthalten darf.",
178
179
  "ingressName": "Ingress-Name",
179
180
  "serviceName": "Service-Name",
180
181
  "port": "Port",
@@ -305,6 +306,7 @@
305
306
  "urlServiceStatus": "Service-Status lesen",
306
307
  "hostTemplate": "Host-Vorlage",
307
308
  "hostTemplateHelp": "Host-Vorlage, gerendert mit den Provisioning-Variablen wie branch und pullNumber; der gerenderte Host wird zur Umgebungs-URL.",
309
+ "ingressPortHelp": "Host-Port, auf dem der Ingress-Controller antwortet; leer bedeutet den Standardport des Schemas. Er gehört hierher und nicht in die Host-Vorlage, da ein Kubernetes-Ingress-Host keinen Port enthalten darf.",
308
310
  "ingressName": "Ingress-Name",
309
311
  "ingressNameHelp": "Ingress, aus dem der Load-Balancer-Host gelesen wird. Leer verwendet den einzigen angewendeten Ingress.",
310
312
  "serviceName": "Service-Name",
@@ -2897,6 +2897,7 @@
2897
2897
  "urlHttpRouteStatus": "HTTPRoute status (Gateway API)",
2898
2898
  "hostTemplate": "Host template",
2899
2899
  "hostTemplateHelp": "The host template, rendered with the provision vars (e.g. branch, sha).",
2900
+ "ingressPortHelp": "Host port the ingress controller answers on; empty means the scheme default. It belongs here rather than in the host template, because a Kubernetes Ingress host cannot carry a port.",
2900
2901
  "ingressName": "Ingress name",
2901
2902
  "serviceName": "Service name",
2902
2903
  "port": "Port",
@@ -3027,6 +3028,7 @@
3027
3028
  "urlServiceStatus": "Read Service status",
3028
3029
  "hostTemplate": "Host template",
3029
3030
  "hostTemplateHelp": "Host template rendered with the provision vars such as branch and pullNumber; the rendered host becomes the environment URL.",
3031
+ "ingressPortHelp": "Host port the ingress controller answers on; empty means the scheme default. It belongs here rather than in the host template, because a Kubernetes Ingress host cannot carry a port.",
3030
3032
  "ingressName": "Ingress name",
3031
3033
  "ingressNameHelp": "Ingress to read the load-balancer host from. Empty uses the only Ingress applied.",
3032
3034
  "serviceName": "Service name",
@@ -2821,6 +2821,7 @@
2821
2821
  "urlServiceStatus": "Leer estado del Service",
2822
2822
  "hostTemplate": "Plantilla de host",
2823
2823
  "hostTemplateHelp": "Plantilla de host renderizada con las variables de aprovisionamiento como branch y pullNumber; el host resultante se convierte en la URL del entorno.",
2824
+ "ingressPortHelp": "Puerto del host en el que responde el controlador de ingress; vacío significa el puerto predeterminado del esquema. Va aquí y no en la plantilla de host, porque un host de Ingress de Kubernetes no puede llevar un puerto.",
2824
2825
  "ingressName": "Nombre del Ingress",
2825
2826
  "ingressNameHelp": "Ingress del que leer el host del balanceador. Vacío usa el único Ingress aplicado.",
2826
2827
  "serviceName": "Nombre del Service",
@@ -3663,6 +3664,7 @@
3663
3664
  "urlHttpRouteStatus": "Estado de HTTPRoute (Gateway API)",
3664
3665
  "hostTemplate": "Plantilla de host",
3665
3666
  "hostTemplateHelp": "La plantilla de host, renderizada con las variables de aprovisionamiento (p. ej. branch, sha).",
3667
+ "ingressPortHelp": "Puerto del host en el que responde el controlador de ingress; vacío significa el puerto predeterminado del esquema. Va aquí y no en la plantilla de host, porque un host de Ingress de Kubernetes no puede llevar un puerto.",
3666
3668
  "ingressName": "Nombre del Ingress",
3667
3669
  "serviceName": "Nombre del Service",
3668
3670
  "port": "Puerto",
@@ -2821,6 +2821,7 @@
2821
2821
  "urlServiceStatus": "Lire l'état du Service",
2822
2822
  "hostTemplate": "Modèle d'hôte",
2823
2823
  "hostTemplateHelp": "Modèle d'hôte rendu avec les variables de provisionnement telles que branch et pullNumber ; l'hôte obtenu devient l'URL de l'environnement.",
2824
+ "ingressPortHelp": "Port hôte sur lequel le contrôleur ingress répond ; vide signifie le port par défaut du schéma. Il se place ici et non dans le modèle d'hôte, car un hôte Ingress Kubernetes ne peut pas contenir de port.",
2824
2825
  "ingressName": "Nom de l'Ingress",
2825
2826
  "ingressNameHelp": "Ingress dont lire l'hôte du load-balancer. Vide utilise le seul Ingress appliqué.",
2826
2827
  "serviceName": "Nom du Service",
@@ -3663,6 +3664,7 @@
3663
3664
  "urlHttpRouteStatus": "Statut de l'HTTPRoute (Gateway API)",
3664
3665
  "hostTemplate": "Modèle d'hôte",
3665
3666
  "hostTemplateHelp": "Le modèle d'hôte, rendu avec les variables de provisionnement (p. ex. branch, sha).",
3667
+ "ingressPortHelp": "Port hôte sur lequel le contrôleur ingress répond ; vide signifie le port par défaut du schéma. Il se place ici et non dans le modèle d'hôte, car un hôte Ingress Kubernetes ne peut pas contenir de port.",
3666
3668
  "ingressName": "Nom de l'Ingress",
3667
3669
  "serviceName": "Nom du Service",
3668
3670
  "port": "Port",
@@ -2772,6 +2772,7 @@
2772
2772
  "urlHttpRouteStatus": "סטטוס HTTPRoute (Gateway API)",
2773
2773
  "hostTemplate": "תבנית מארח",
2774
2774
  "hostTemplateHelp": "תבנית המארח, מרונדרת עם משתני האספקה (לדוגמה branch, sha).",
2775
+ "ingressPortHelp": "פורט המארח שבו בקר ה-Ingress עונה; ריק פירושו פורט ברירת המחדל של הסכימה. הוא שייך לכאן ולא לתבנית המארח, כי מארח Ingress של Kubernetes אינו יכול לכלול פורט.",
2775
2776
  "ingressName": "שם ה-Ingress",
2776
2777
  "serviceName": "שם ה-Service",
2777
2778
  "port": "פורט",
@@ -2902,6 +2903,7 @@
2902
2903
  "urlServiceStatus": "קרא סטטוס Service",
2903
2904
  "hostTemplate": "תבנית מארח",
2904
2905
  "hostTemplateHelp": "תבנית מארח שעוברת עיבוד עם משתני ההקצאה כגון branch ו-pullNumber; המארח המעובד הופך לכתובת הסביבה.",
2906
+ "ingressPortHelp": "פורט המארח שבו בקר ה-Ingress עונה; ריק פירושו פורט ברירת המחדל של הסכימה. הוא שייך לכאן ולא לתבנית המארח, כי מארח Ingress של Kubernetes אינו יכול לכלול פורט.",
2905
2907
  "ingressName": "שם Ingress",
2906
2908
  "ingressNameHelp": "ה-Ingress שממנו לקרוא את מארח מאזן העומסים. ריק משתמש ב-Ingress היחיד שהוחל.",
2907
2909
  "serviceName": "שם Service",
@@ -175,6 +175,7 @@
175
175
  "urlHttpRouteStatus": "Stato dell'HTTPRoute (Gateway API)",
176
176
  "hostTemplate": "Template dell'host",
177
177
  "hostTemplateHelp": "Il template dell'host, renderizzato con le variabili di provisioning (es. branch, sha).",
178
+ "ingressPortHelp": "Porta host su cui risponde il controller ingress; vuoto indica la porta predefinita dello schema. Va qui e non nel template dell'host, perché un host Ingress di Kubernetes non può contenere una porta.",
178
179
  "ingressName": "Nome dell'Ingress",
179
180
  "serviceName": "Nome del Service",
180
181
  "port": "Porta",
@@ -305,6 +306,7 @@
305
306
  "urlServiceStatus": "Leggi lo stato del Service",
306
307
  "hostTemplate": "Template dell'host",
307
308
  "hostTemplateHelp": "Template dell'host renderizzato con le variabili di provisioning come branch e pullNumber; l'host renderizzato diventa l'URL dell'ambiente.",
309
+ "ingressPortHelp": "Porta host su cui risponde il controller ingress; vuoto indica la porta predefinita dello schema. Va qui e non nel template dell'host, perché un host Ingress di Kubernetes non può contenere una porta.",
308
310
  "ingressName": "Nome dell'Ingress",
309
311
  "ingressNameHelp": "Ingress da cui leggere l'host del load balancer. Se vuoto, usa l'unico Ingress applicato.",
310
312
  "serviceName": "Nome del Service",
@@ -2772,6 +2772,7 @@
2772
2772
  "urlHttpRouteStatus": "HTTPRoute ステータス (Gateway API)",
2773
2773
  "hostTemplate": "ホストテンプレート",
2774
2774
  "hostTemplateHelp": "ホストテンプレート。プロビジョニング変数(例: branch、sha)で描画されます。",
2775
+ "ingressPortHelp": "Ingress コントローラーが応答するホストポート。空の場合はスキームの既定ポートです。Kubernetes の Ingress host にはポートを含められないため、ホストテンプレートではなくここに指定します。",
2775
2776
  "ingressName": "Ingress 名",
2776
2777
  "serviceName": "Service 名",
2777
2778
  "port": "ポート",
@@ -2902,6 +2903,7 @@
2902
2903
  "urlServiceStatus": "Service ステータスを読み取る",
2903
2904
  "hostTemplate": "ホストテンプレート",
2904
2905
  "hostTemplateHelp": "branch や pullNumber などのプロビジョニング変数でレンダリングされるホストテンプレート。レンダリングされたホストが環境 URL になります。",
2906
+ "ingressPortHelp": "Ingress コントローラーが応答するホストポート。空の場合はスキームの既定ポートです。Kubernetes の Ingress host にはポートを含められないため、ホストテンプレートではなくここに指定します。",
2905
2907
  "ingressName": "Ingress 名",
2906
2908
  "ingressNameHelp": "ロードバランサーのホストを読み取る Ingress。空の場合は適用された唯一の Ingress を使用します。",
2907
2909
  "serviceName": "Service 名",
@@ -2821,6 +2821,7 @@
2821
2821
  "urlServiceStatus": "Odczytaj stan Service",
2822
2822
  "hostTemplate": "Szablon hosta",
2823
2823
  "hostTemplateHelp": "Szablon hosta renderowany ze zmiennymi provisioningu, takimi jak branch i pullNumber; wynikowy host staje się URL-em środowiska.",
2824
+ "ingressPortHelp": "Port hosta, na którym odpowiada kontroler ingress; puste oznacza domyślny port schematu. Należy podać go tutaj, a nie w szablonie hosta, ponieważ host Ingress w Kubernetes nie może zawierać portu.",
2824
2825
  "ingressName": "Nazwa Ingress",
2825
2826
  "ingressNameHelp": "Ingress, z którego odczytać host load-balancera. Puste używa jedynego zastosowanego Ingress.",
2826
2827
  "serviceName": "Nazwa Service",
@@ -3663,6 +3664,7 @@
3663
3664
  "urlHttpRouteStatus": "Status HTTPRoute (Gateway API)",
3664
3665
  "hostTemplate": "Szablon hosta",
3665
3666
  "hostTemplateHelp": "Szablon hosta renderowany ze zmiennymi provisioningu (np. branch, sha).",
3667
+ "ingressPortHelp": "Port hosta, na którym odpowiada kontroler ingress; puste oznacza domyślny port schematu. Należy podać go tutaj, a nie w szablonie hosta, ponieważ host Ingress w Kubernetes nie może zawierać portu.",
3666
3668
  "ingressName": "Nazwa Ingress",
3667
3669
  "serviceName": "Nazwa Service",
3668
3670
  "port": "Port",
@@ -2772,6 +2772,7 @@
2772
2772
  "urlHttpRouteStatus": "HTTPRoute durumu (Gateway API)",
2773
2773
  "hostTemplate": "Ana bilgisayar şablonu",
2774
2774
  "hostTemplateHelp": "Sağlama değişkenleriyle (ör. branch, sha) işlenen ana bilgisayar şablonu.",
2775
+ "ingressPortHelp": "Ingress denetleyicisinin yanıt verdiği ana bilgisayar portu; boş bırakılırsa şemanın varsayılan portu kullanılır. Kubernetes Ingress host değeri port içeremediği için burada, host şablonunda değil, belirtilir.",
2775
2776
  "ingressName": "Ingress adı",
2776
2777
  "serviceName": "Service adı",
2777
2778
  "port": "Bağlantı noktası",
@@ -2902,6 +2903,7 @@
2902
2903
  "urlServiceStatus": "Service durumunu oku",
2903
2904
  "hostTemplate": "Host şablonu",
2904
2905
  "hostTemplateHelp": "branch ve pullNumber gibi sağlama değişkenleriyle işlenen host şablonu; işlenen host, ortam URL'si olur.",
2906
+ "ingressPortHelp": "Ingress denetleyicisinin yanıt verdiği ana bilgisayar portu; boş bırakılırsa şemanın varsayılan portu kullanılır. Kubernetes Ingress host değeri port içeremediği için burada, host şablonunda değil, belirtilir.",
2905
2907
  "ingressName": "Ingress adı",
2906
2908
  "ingressNameHelp": "Load balancer host'unun okunacağı Ingress. Boş bırakılırsa uygulanan tek Ingress kullanılır.",
2907
2909
  "serviceName": "Service adı",
@@ -2821,6 +2821,7 @@
2821
2821
  "urlServiceStatus": "Зчитати стан Service",
2822
2822
  "hostTemplate": "Шаблон хоста",
2823
2823
  "hostTemplateHelp": "Шаблон хоста, відрендерений зі змінними провіженингу, такими як branch і pullNumber; отриманий хост стає URL середовища.",
2824
+ "ingressPortHelp": "Порт хоста, на якому відповідає контролер ingress; порожнє значення означає стандартний порт схеми. Його вказують тут, а не в шаблоні хоста, бо host в Ingress Kubernetes не може містити порт.",
2824
2825
  "ingressName": "Назва Ingress",
2825
2826
  "ingressNameHelp": "Ingress, з якого зчитати хост балансувальника. Порожнє використовує єдиний застосований Ingress.",
2826
2827
  "serviceName": "Назва Service",
@@ -3663,6 +3664,7 @@
3663
3664
  "urlHttpRouteStatus": "Статус HTTPRoute (Gateway API)",
3664
3665
  "hostTemplate": "Шаблон хоста",
3665
3666
  "hostTemplateHelp": "Шаблон хоста, відрендерений зі змінними провіженінгу (напр. branch, sha).",
3667
+ "ingressPortHelp": "Порт хоста, на якому відповідає контролер ingress; порожнє значення означає стандартний порт схеми. Його вказують тут, а не в шаблоні хоста, бо host в Ingress Kubernetes не може містити порт.",
3666
3668
  "ingressName": "Назва Ingress",
3667
3669
  "serviceName": "Назва Service",
3668
3670
  "port": "Порт",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.261.4",
3
+ "version": "0.261.6",
4
4
  "description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -40,7 +40,7 @@
40
40
  "valibot": "^1.4.2",
41
41
  "vue": "3.5.41",
42
42
  "wretch": "^3.0.9",
43
- "@cat-factory/contracts": "0.293.0"
43
+ "@cat-factory/contracts": "0.295.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@toad-contracts/testing": "0.3.2",