typekro 0.10.1 → 0.12.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/dist/.tsbuildinfo +1 -1
- package/dist/compositions/typekro-runtime/typekro-runtime.d.ts +1 -1
- package/dist/compositions/typekro-runtime/typekro-runtime.js +4 -4
- package/dist/core/aspects/apply.d.ts.map +1 -1
- package/dist/core/aspects/apply.js +42 -1
- package/dist/core/aspects/apply.js.map +1 -1
- package/dist/core/aspects/values-merge.d.ts +8 -0
- package/dist/core/aspects/values-merge.d.ts.map +1 -0
- package/dist/core/aspects/values-merge.js +21 -0
- package/dist/core/aspects/values-merge.js.map +1 -0
- package/dist/core/deployment/direct-factory.d.ts +5 -1
- package/dist/core/deployment/direct-factory.d.ts.map +1 -1
- package/dist/core/deployment/direct-factory.js +21 -2
- package/dist/core/deployment/direct-factory.js.map +1 -1
- package/dist/core/deployment/engine.d.ts +1 -0
- package/dist/core/deployment/engine.d.ts.map +1 -1
- package/dist/core/deployment/engine.js +28 -7
- package/dist/core/deployment/engine.js.map +1 -1
- package/dist/core/deployment/kro-factory.d.ts.map +1 -1
- package/dist/core/deployment/kro-factory.js +26 -3
- package/dist/core/deployment/kro-factory.js.map +1 -1
- package/dist/core/deployment/strategies/base-strategy.d.ts +1 -0
- package/dist/core/deployment/strategies/base-strategy.d.ts.map +1 -1
- package/dist/core/deployment/strategies/base-strategy.js +35 -33
- package/dist/core/deployment/strategies/base-strategy.js.map +1 -1
- package/dist/core/deployment/strategies/direct-strategy.d.ts +4 -0
- package/dist/core/deployment/strategies/direct-strategy.d.ts.map +1 -1
- package/dist/core/deployment/strategies/direct-strategy.js +40 -29
- package/dist/core/deployment/strategies/direct-strategy.js.map +1 -1
- package/dist/core/references/cel.d.ts +13 -0
- package/dist/core/references/cel.d.ts.map +1 -1
- package/dist/core/references/cel.js +42 -3
- package/dist/core/references/cel.js.map +1 -1
- package/dist/core/references/external-refs.d.ts +13 -0
- package/dist/core/references/external-refs.d.ts.map +1 -1
- package/dist/core/references/external-refs.js +18 -0
- package/dist/core/references/external-refs.js.map +1 -1
- package/dist/core/references/index.d.ts +1 -1
- package/dist/core/references/index.d.ts.map +1 -1
- package/dist/core/references/index.js +1 -1
- package/dist/core/references/index.js.map +1 -1
- package/dist/core/references/schema-proxy.d.ts.map +1 -1
- package/dist/core/references/schema-proxy.js +7 -3
- package/dist/core/references/schema-proxy.js.map +1 -1
- package/dist/core/serialization/cel-references.d.ts +2 -7
- package/dist/core/serialization/cel-references.d.ts.map +1 -1
- package/dist/core/serialization/cel-references.js +451 -35
- package/dist/core/serialization/cel-references.js.map +1 -1
- package/dist/core/serialization/core.d.ts.map +1 -1
- package/dist/core/serialization/core.js +95 -62
- package/dist/core/serialization/core.js.map +1 -1
- package/dist/core/serialization/yaml.d.ts +2 -2
- package/dist/core/serialization/yaml.d.ts.map +1 -1
- package/dist/core/serialization/yaml.js +14 -4
- package/dist/core/serialization/yaml.js.map +1 -1
- package/dist/core/types/common.d.ts +41 -0
- package/dist/core/types/common.d.ts.map +1 -1
- package/dist/core/types/deployment.d.ts +2 -0
- package/dist/core/types/deployment.d.ts.map +1 -1
- package/dist/core/types/deployment.js +1 -1
- package/dist/core/types/deployment.js.map +1 -1
- package/dist/core/types/references.d.ts +1 -1
- package/dist/core/types/references.d.ts.map +1 -1
- package/dist/core/validation/cel-validator.d.ts.map +1 -1
- package/dist/core/validation/cel-validator.js +26 -8
- package/dist/core/validation/cel-validator.js.map +1 -1
- package/dist/factories/apisix/resources/helm.d.ts +3 -2
- package/dist/factories/apisix/resources/helm.d.ts.map +1 -1
- package/dist/factories/apisix/resources/helm.js +1 -1
- package/dist/factories/apisix/resources/helm.js.map +1 -1
- package/dist/factories/cert-manager/types.d.ts +2 -1
- package/dist/factories/cert-manager/types.d.ts.map +1 -1
- package/dist/factories/cert-manager/types.js.map +1 -1
- package/dist/factories/cilium/resources/helm.d.ts +1 -1
- package/dist/factories/cilium/resources/helm.d.ts.map +1 -1
- package/dist/factories/cilium/types.d.ts +2 -1
- package/dist/factories/cilium/types.d.ts.map +1 -1
- package/dist/factories/cilium/types.js.map +1 -1
- package/dist/factories/dagster/compositions/dagster-bootstrap.d.ts +894 -0
- package/dist/factories/dagster/compositions/dagster-bootstrap.d.ts.map +1 -0
- package/dist/factories/dagster/compositions/dagster-bootstrap.js +75 -0
- package/dist/factories/dagster/compositions/dagster-bootstrap.js.map +1 -0
- package/dist/factories/dagster/compositions/index.d.ts +2 -0
- package/dist/factories/dagster/compositions/index.d.ts.map +1 -0
- package/dist/factories/dagster/compositions/index.js +2 -0
- package/dist/factories/dagster/compositions/index.js.map +1 -0
- package/dist/factories/dagster/index.d.ts +31 -0
- package/dist/factories/dagster/index.d.ts.map +1 -0
- package/dist/factories/dagster/index.js +31 -0
- package/dist/factories/dagster/index.js.map +1 -0
- package/dist/factories/dagster/resources/helm.d.ts +32 -0
- package/dist/factories/dagster/resources/helm.d.ts.map +1 -0
- package/dist/factories/dagster/resources/helm.js +60 -0
- package/dist/factories/dagster/resources/helm.js.map +1 -0
- package/dist/factories/dagster/resources/index.d.ts +2 -0
- package/dist/factories/dagster/resources/index.d.ts.map +1 -0
- package/dist/factories/dagster/resources/index.js +2 -0
- package/dist/factories/dagster/resources/index.js.map +1 -0
- package/dist/factories/dagster/types.d.ts +2048 -0
- package/dist/factories/dagster/types.d.ts.map +1 -0
- package/dist/factories/dagster/types.js +354 -0
- package/dist/factories/dagster/types.js.map +1 -0
- package/dist/factories/dagster/utils/helm-values-mapper.d.ts +21 -0
- package/dist/factories/dagster/utils/helm-values-mapper.d.ts.map +1 -0
- package/dist/factories/dagster/utils/helm-values-mapper.js +470 -0
- package/dist/factories/dagster/utils/helm-values-mapper.js.map +1 -0
- package/dist/factories/dagster/utils/index.d.ts +2 -0
- package/dist/factories/dagster/utils/index.d.ts.map +1 -0
- package/dist/factories/dagster/utils/index.js +2 -0
- package/dist/factories/dagster/utils/index.js.map +1 -0
- package/dist/factories/external-dns/resources/helm.d.ts +2 -1
- package/dist/factories/external-dns/resources/helm.d.ts.map +1 -1
- package/dist/factories/external-dns/resources/helm.js +6 -1
- package/dist/factories/external-dns/resources/helm.js.map +1 -1
- package/dist/factories/external-dns/types.d.ts +2 -2
- package/dist/factories/external-dns/types.d.ts.map +1 -1
- package/dist/factories/helm/helm-release.d.ts +9 -4
- package/dist/factories/helm/helm-release.d.ts.map +1 -1
- package/dist/factories/helm/helm-release.js.map +1 -1
- package/dist/factories/helm/types.d.ts +7 -2
- package/dist/factories/helm/types.d.ts.map +1 -1
- package/dist/factories/index.d.ts +1 -0
- package/dist/factories/index.d.ts.map +1 -1
- package/dist/factories/index.js +4 -0
- package/dist/factories/index.js.map +1 -1
- package/dist/factories/inngest/types.d.ts +5 -1
- package/dist/factories/inngest/types.d.ts.map +1 -1
- package/dist/factories/inngest/types.js.map +1 -1
- package/dist/factories/kro/resource-graph-definition.d.ts.map +1 -1
- package/dist/factories/kro/resource-graph-definition.js +27 -11
- package/dist/factories/kro/resource-graph-definition.js.map +1 -1
- package/dist/factories/ory/compositions/index.d.ts +3 -0
- package/dist/factories/ory/compositions/index.d.ts.map +1 -0
- package/dist/factories/ory/compositions/index.js +3 -0
- package/dist/factories/ory/compositions/index.js.map +1 -0
- package/dist/factories/ory/compositions/ory-identity-stack.d.ts +2073 -0
- package/dist/factories/ory/compositions/ory-identity-stack.d.ts.map +1 -0
- package/dist/factories/ory/compositions/ory-identity-stack.js +432 -0
- package/dist/factories/ory/compositions/ory-identity-stack.js.map +1 -0
- package/dist/factories/ory/compositions/ory-platform-stack.d.ts +185 -0
- package/dist/factories/ory/compositions/ory-platform-stack.d.ts.map +1 -0
- package/dist/factories/ory/compositions/ory-platform-stack.js +536 -0
- package/dist/factories/ory/compositions/ory-platform-stack.js.map +1 -0
- package/dist/factories/ory/index.d.ts +5 -0
- package/dist/factories/ory/index.d.ts.map +1 -0
- package/dist/factories/ory/index.js +5 -0
- package/dist/factories/ory/index.js.map +1 -0
- package/dist/factories/ory/resources/helm.d.ts +20 -0
- package/dist/factories/ory/resources/helm.d.ts.map +1 -0
- package/dist/factories/ory/resources/helm.js +52 -0
- package/dist/factories/ory/resources/helm.js.map +1 -0
- package/dist/factories/ory/resources/index.d.ts +4 -0
- package/dist/factories/ory/resources/index.d.ts.map +1 -0
- package/dist/factories/ory/resources/index.js +4 -0
- package/dist/factories/ory/resources/index.js.map +1 -0
- package/dist/factories/ory/resources/oathkeeper-rule.d.ts +4 -0
- package/dist/factories/ory/resources/oathkeeper-rule.d.ts.map +1 -0
- package/dist/factories/ory/resources/oathkeeper-rule.js +19 -0
- package/dist/factories/ory/resources/oathkeeper-rule.js.map +1 -0
- package/dist/factories/ory/resources/oauth2-client.d.ts +4 -0
- package/dist/factories/ory/resources/oauth2-client.d.ts.map +1 -0
- package/dist/factories/ory/resources/oauth2-client.js +20 -0
- package/dist/factories/ory/resources/oauth2-client.js.map +1 -0
- package/dist/factories/ory/schemas/chart-values.d.ts +253 -0
- package/dist/factories/ory/schemas/chart-values.d.ts.map +1 -0
- package/dist/factories/ory/schemas/chart-values.js +10 -0
- package/dist/factories/ory/schemas/chart-values.js.map +1 -0
- package/dist/factories/ory/schemas/hydra-maester.d.ts +152 -0
- package/dist/factories/ory/schemas/hydra-maester.d.ts.map +1 -0
- package/dist/factories/ory/schemas/hydra-maester.js +3 -0
- package/dist/factories/ory/schemas/hydra-maester.js.map +1 -0
- package/dist/factories/ory/schemas/hydra.d.ts +115 -0
- package/dist/factories/ory/schemas/hydra.d.ts.map +1 -0
- package/dist/factories/ory/schemas/hydra.js +3 -0
- package/dist/factories/ory/schemas/hydra.js.map +1 -0
- package/dist/factories/ory/schemas/index.d.ts +20 -0
- package/dist/factories/ory/schemas/index.d.ts.map +1 -0
- package/dist/factories/ory/schemas/index.js +2 -0
- package/dist/factories/ory/schemas/index.js.map +1 -0
- package/dist/factories/ory/schemas/keto.d.ts +86 -0
- package/dist/factories/ory/schemas/keto.d.ts.map +1 -0
- package/dist/factories/ory/schemas/keto.js +3 -0
- package/dist/factories/ory/schemas/keto.js.map +1 -0
- package/dist/factories/ory/schemas/kratos.d.ts +124 -0
- package/dist/factories/ory/schemas/kratos.d.ts.map +1 -0
- package/dist/factories/ory/schemas/kratos.js +3 -0
- package/dist/factories/ory/schemas/kratos.js.map +1 -0
- package/dist/factories/ory/schemas/maester-crds.d.ts +9 -0
- package/dist/factories/ory/schemas/maester-crds.d.ts.map +1 -0
- package/dist/factories/ory/schemas/maester-crds.js +8 -0
- package/dist/factories/ory/schemas/maester-crds.js.map +1 -0
- package/dist/factories/ory/schemas/oathkeeper-maester.d.ts +80 -0
- package/dist/factories/ory/schemas/oathkeeper-maester.d.ts.map +1 -0
- package/dist/factories/ory/schemas/oathkeeper-maester.js +3 -0
- package/dist/factories/ory/schemas/oathkeeper-maester.js.map +1 -0
- package/dist/factories/ory/schemas/oathkeeper.d.ts +105 -0
- package/dist/factories/ory/schemas/oathkeeper.d.ts.map +1 -0
- package/dist/factories/ory/schemas/oathkeeper.js +3 -0
- package/dist/factories/ory/schemas/oathkeeper.js.map +1 -0
- package/dist/factories/ory/types.d.ts +4948 -0
- package/dist/factories/ory/types.d.ts.map +1 -0
- package/dist/factories/ory/types.js +581 -0
- package/dist/factories/ory/types.js.map +1 -0
- package/dist/factories/ory/utils/helm-values-mapper.d.ts +7 -0
- package/dist/factories/ory/utils/helm-values-mapper.d.ts.map +1 -0
- package/dist/factories/ory/utils/helm-values-mapper.js +534 -0
- package/dist/factories/ory/utils/helm-values-mapper.js.map +1 -0
- package/dist/factories/ory/utils/index.d.ts +2 -0
- package/dist/factories/ory/utils/index.d.ts.map +1 -0
- package/dist/factories/ory/utils/index.js +2 -0
- package/dist/factories/ory/utils/index.js.map +1 -0
- package/dist/factories/pebble/resources/helm.d.ts +2 -2
- package/dist/factories/pebble/resources/helm.d.ts.map +1 -1
- package/dist/factories/pebble/resources/helm.js.map +1 -1
- package/dist/factories/pebble/types.d.ts +2 -1
- package/dist/factories/pebble/types.d.ts.map +1 -1
- package/dist/factories/pebble/types.js.map +1 -1
- package/dist/factories/searxng/compositions/searxng-bootstrap.d.ts.map +1 -1
- package/dist/factories/searxng/compositions/searxng-bootstrap.js +152 -42
- package/dist/factories/searxng/compositions/searxng-bootstrap.js.map +1 -1
- package/dist/factories/searxng/types.d.ts.map +1 -1
- package/dist/factories/searxng/types.js +9 -1
- package/dist/factories/searxng/types.js.map +1 -1
- package/dist/factories/simple/helm/index.d.ts +2 -1
- package/dist/factories/simple/helm/index.d.ts.map +1 -1
- package/dist/factories/simple/helm/index.js.map +1 -1
- package/dist/factories/valkey/types.d.ts +5 -1
- package/dist/factories/valkey/types.d.ts.map +1 -1
- package/dist/factories/valkey/types.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/shared/brands.d.ts +7 -5
- package/dist/shared/brands.d.ts.map +1 -1
- package/dist/shared/brands.js +5 -3
- package/dist/shared/brands.js.map +1 -1
- package/package.json +10 -1
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
* This module is the **single authority** for KubernetesRef → CEL string
|
|
9
9
|
* conversion. No other module should duplicate this logic.
|
|
10
10
|
*/
|
|
11
|
-
import { isCelExpression, isKubernetesRef } from '../../utils/type-guards.js';
|
|
12
|
-
import { escapeCelString } from '../../utils/cel-escape.js';
|
|
13
11
|
import { KUBERNETES_REF_MARKER_SOURCE } from '../../shared/brands.js';
|
|
12
|
+
import { escapeCelString } from '../../utils/cel-escape.js';
|
|
13
|
+
import { isCelExpression, isKubernetesRef } from '../../utils/type-guards.js';
|
|
14
|
+
import { isValuesMergeExpression } from '../aspects/values-merge.js';
|
|
14
15
|
import { remapVariableNames } from '../composition/nested-status-cel.js';
|
|
15
16
|
import { getComponentLogger } from '../logging/index.js';
|
|
16
17
|
import { copyResourceMetadata } from '../metadata/index.js';
|
|
@@ -18,6 +19,52 @@ const logger = getComponentLogger('cel-references');
|
|
|
18
19
|
function escapeRegExpLiteral(value) {
|
|
19
20
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
20
21
|
}
|
|
22
|
+
export function normalizeCelArrayIndexPaths(expr) {
|
|
23
|
+
let result = '';
|
|
24
|
+
let quote = null;
|
|
25
|
+
let escaped = false;
|
|
26
|
+
for (let i = 0; i < expr.length; i++) {
|
|
27
|
+
const char = expr[i];
|
|
28
|
+
if (!char)
|
|
29
|
+
continue;
|
|
30
|
+
if (quote) {
|
|
31
|
+
result += char;
|
|
32
|
+
if (escaped) {
|
|
33
|
+
escaped = false;
|
|
34
|
+
}
|
|
35
|
+
else if (char === '\\') {
|
|
36
|
+
escaped = true;
|
|
37
|
+
}
|
|
38
|
+
else if (char === quote) {
|
|
39
|
+
quote = null;
|
|
40
|
+
}
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (char === '"' || char === "'") {
|
|
44
|
+
quote = char;
|
|
45
|
+
result += char;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (char === '.') {
|
|
49
|
+
const digitStart = i + 1;
|
|
50
|
+
let digitEnd = digitStart;
|
|
51
|
+
while (digitEnd < expr.length && /\d/.test(expr[digitEnd] ?? '')) {
|
|
52
|
+
digitEnd++;
|
|
53
|
+
}
|
|
54
|
+
const previous = expr[i - 1] ?? '';
|
|
55
|
+
const next = expr[digitEnd] ?? '';
|
|
56
|
+
if (digitEnd > digitStart &&
|
|
57
|
+
/[A-Za-z_$\]]/.test(previous) &&
|
|
58
|
+
(next === '' || /[.\])}\s?:,+\-*/<>=!&|]/.test(next))) {
|
|
59
|
+
result += `[${expr.slice(digitStart, digitEnd)}]`;
|
|
60
|
+
i = digitEnd - 1;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
result += char;
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
21
68
|
// ---------------------------------------------------------------------------
|
|
22
69
|
// Primitive helpers
|
|
23
70
|
// ---------------------------------------------------------------------------
|
|
@@ -32,7 +79,7 @@ function escapeRegExpLiteral(value) {
|
|
|
32
79
|
*/
|
|
33
80
|
export function getInnerCelPath(ref) {
|
|
34
81
|
const resourceId = ref.resourceId === '__schema__' ? 'schema' : ref.resourceId;
|
|
35
|
-
return `${resourceId}.${ref.fieldPath}
|
|
82
|
+
return normalizeCelArrayIndexPaths(`${resourceId}.${ref.fieldPath}`);
|
|
36
83
|
}
|
|
37
84
|
function resolveResourceIdAlias(resourceId, context) {
|
|
38
85
|
if (resourceId === '__schema__') {
|
|
@@ -72,13 +119,17 @@ function resolveResourceIdAlias(resourceId, context) {
|
|
|
72
119
|
*/
|
|
73
120
|
function maybeWrapWithOmit(celPath, stringWrap, omitFields) {
|
|
74
121
|
const value = stringWrap ? `string(${celPath})` : celPath;
|
|
122
|
+
const guard = optionalSchemaSpecGuard(celPath, omitFields);
|
|
123
|
+
return guard ? `${guard} ? ${value} : omit()` : value;
|
|
124
|
+
}
|
|
125
|
+
function optionalSchemaSpecGuard(celPath, omitFields) {
|
|
75
126
|
if (!omitFields || omitFields.size === 0)
|
|
76
|
-
return
|
|
127
|
+
return undefined;
|
|
77
128
|
// Only handle refs rooted at `schema.spec.`.
|
|
78
129
|
const refMatch = /^schema\.spec\.([A-Za-z_$][\w$.]*)$/.exec(celPath);
|
|
79
130
|
const refPath = refMatch?.[1];
|
|
80
131
|
if (!refPath)
|
|
81
|
-
return
|
|
132
|
+
return undefined;
|
|
82
133
|
// Walk the full path and collect every optional prefix that applies.
|
|
83
134
|
// We chain from the SHALLOWEST optional ancestor through the leaf.
|
|
84
135
|
//
|
|
@@ -105,9 +156,9 @@ function maybeWrapWithOmit(celPath, stringWrap, omitFields) {
|
|
|
105
156
|
const guardPath = `schema.spec.${segments.slice(0, i).join('.')}`;
|
|
106
157
|
guards.push(`has(${guardPath})`);
|
|
107
158
|
}
|
|
108
|
-
return
|
|
159
|
+
return guards.join(' && ');
|
|
109
160
|
}
|
|
110
|
-
return
|
|
161
|
+
return undefined;
|
|
111
162
|
}
|
|
112
163
|
/**
|
|
113
164
|
* Wrap a {@link KubernetesRef} in `${…}` for Kro YAML output.
|
|
@@ -133,7 +184,7 @@ function generateCelExpression(ref, context) {
|
|
|
133
184
|
return finalizeCelForKro(innerExpr, context.nestedStatusCel, context);
|
|
134
185
|
}
|
|
135
186
|
}
|
|
136
|
-
const expression = `${resolveResourceIdAlias(ref.resourceId, context)}.${ref.fieldPath}
|
|
187
|
+
const expression = normalizeCelArrayIndexPaths(`${resolveResourceIdAlias(ref.resourceId, context)}.${ref.fieldPath}`);
|
|
137
188
|
const body = maybeWrapWithOmit(expression, false, context?.omitFields);
|
|
138
189
|
return `\${${body}}`;
|
|
139
190
|
}
|
|
@@ -174,7 +225,7 @@ const MARKER_PATTERN_FULL = new RegExp(`^${MARKER_PATTERN_SOURCE}$`);
|
|
|
174
225
|
* emits `<resourceId>.<fieldPath>`.
|
|
175
226
|
*/
|
|
176
227
|
function markerToCelPath(resourceId, fieldPath, context) {
|
|
177
|
-
return `${resolveResourceIdAlias(resourceId, context)}.${fieldPath}
|
|
228
|
+
return normalizeCelArrayIndexPaths(`${resolveResourceIdAlias(resourceId, context)}.${fieldPath}`);
|
|
178
229
|
}
|
|
179
230
|
/**
|
|
180
231
|
* Normalize any `__KUBERNETES_REF__` markers in `str` to their bare CEL paths
|
|
@@ -545,7 +596,7 @@ function innerExprToYamlSegment(innerExpr, nestedStatusCel, context) {
|
|
|
545
596
|
}
|
|
546
597
|
if (resolved.includes('${')) {
|
|
547
598
|
// Already a KRO template (from Cel.template) — pass through.
|
|
548
|
-
return resolved;
|
|
599
|
+
return normalizeCelArrayIndexPaths(resolved);
|
|
549
600
|
}
|
|
550
601
|
// Bare literal reached via a template-literal coercion — wrap with
|
|
551
602
|
// `string(...)` so KRO evaluates it to a string value. This matches
|
|
@@ -575,16 +626,19 @@ function innerExprToYamlSegment(innerExpr, nestedStatusCel, context) {
|
|
|
575
626
|
* `${…}` so KRO recognizes it as an expression to evaluate.
|
|
576
627
|
*/
|
|
577
628
|
export function finalizeCelForKro(expr, nestedStatusCel, context) {
|
|
578
|
-
const resolved = resolveNestedCompositionRefs(expr, nestedStatusCel, context?.resourceIds);
|
|
629
|
+
const resolved = normalizeCelArrayIndexPaths(resolveNestedCompositionRefs(expr, nestedStatusCel, context?.resourceIds));
|
|
579
630
|
if (resolved.includes('__KUBERNETES_REF_')) {
|
|
580
631
|
return convertKubernetesRefMarkersTocel(resolved, context);
|
|
581
632
|
}
|
|
582
633
|
if (resolved.includes('${')) {
|
|
583
634
|
// Already contains KRO template placeholders — pass through.
|
|
584
|
-
return resolved;
|
|
635
|
+
return normalizeCelArrayIndexPaths(resolved);
|
|
585
636
|
}
|
|
586
637
|
return `\${${resolved}}`;
|
|
587
638
|
}
|
|
639
|
+
function normalizeSchemaSpecShorthand(expr) {
|
|
640
|
+
return expr.replace(/(^|[^A-Za-z0-9_$.])spec\./g, '$1schema.spec.');
|
|
641
|
+
}
|
|
588
642
|
// ---------------------------------------------------------------------------
|
|
589
643
|
// __KUBERNETES_REF__ marker → KRO CEL conversion
|
|
590
644
|
// ---------------------------------------------------------------------------
|
|
@@ -656,13 +710,363 @@ function convertKubernetesRefMarkersTocel(str, context) {
|
|
|
656
710
|
*
|
|
657
711
|
* This is the **only** function that should perform this transformation.
|
|
658
712
|
*/
|
|
659
|
-
|
|
713
|
+
function valueTreePath(path) {
|
|
714
|
+
return path === 'spec.values' || path.includes('.values.') || path.endsWith('.values');
|
|
715
|
+
}
|
|
716
|
+
function childPath(parent, key) {
|
|
717
|
+
return parent ? `${parent}.${key}` : key;
|
|
718
|
+
}
|
|
719
|
+
function arrayChildPath(parent, index) {
|
|
720
|
+
return `${parent}[${index}]`;
|
|
721
|
+
}
|
|
722
|
+
function isPlainSerializableObject(value) {
|
|
723
|
+
const prototype = Object.getPrototypeOf(value);
|
|
724
|
+
return prototype === Object.prototype || prototype === null;
|
|
725
|
+
}
|
|
726
|
+
function valueTreeSerializationError(path, reason) {
|
|
727
|
+
return new Error(`Cannot serialize ${path} in graph mode. ${reason} Provide a serializable value or move this logic to a direct-only API.`);
|
|
728
|
+
}
|
|
729
|
+
function unwrapKroExpression(value) {
|
|
730
|
+
if (typeof value !== 'string')
|
|
731
|
+
return undefined;
|
|
732
|
+
if (!value.startsWith('${') || !value.endsWith('}'))
|
|
733
|
+
return undefined;
|
|
734
|
+
return value.slice(2, -1);
|
|
735
|
+
}
|
|
736
|
+
function celValueTreeBranch(expression) {
|
|
737
|
+
return expression.includes(' ? ') && expression.includes(' : ') ? `(${expression})` : expression;
|
|
738
|
+
}
|
|
739
|
+
function celStringTemplateExpression(template) {
|
|
740
|
+
const parts = [];
|
|
741
|
+
let lastIndex = 0;
|
|
742
|
+
for (const match of template.matchAll(/\$\{([^}]+)\}/g)) {
|
|
743
|
+
const index = match.index ?? 0;
|
|
744
|
+
if (index > lastIndex) {
|
|
745
|
+
parts.push(JSON.stringify(template.slice(lastIndex, index)));
|
|
746
|
+
}
|
|
747
|
+
const innerExpression = match[1]?.trim();
|
|
748
|
+
if (innerExpression) {
|
|
749
|
+
parts.push(innerExpression.startsWith('string(') ? innerExpression : `string(${innerExpression})`);
|
|
750
|
+
}
|
|
751
|
+
lastIndex = index + match[0].length;
|
|
752
|
+
}
|
|
753
|
+
if (lastIndex < template.length) {
|
|
754
|
+
parts.push(JSON.stringify(template.slice(lastIndex)));
|
|
755
|
+
}
|
|
756
|
+
return parts.length > 0 ? parts.join(' + ') : JSON.stringify(template);
|
|
757
|
+
}
|
|
758
|
+
function celMarkerStringLiteralForValueTree(value, context) {
|
|
759
|
+
const resolved = resolveNestedRefMarkers(value, context?.nestedStatusCel, context?.resourceIds, context);
|
|
760
|
+
if (!resolved.includes('__KUBERNETES_REF_')) {
|
|
761
|
+
return resolved.includes('${') ? celStringTemplateExpression(resolved) : JSON.stringify(resolved);
|
|
762
|
+
}
|
|
763
|
+
const parts = [];
|
|
764
|
+
let lastIndex = 0;
|
|
765
|
+
for (const match of resolved.matchAll(new RegExp(MARKER_PATTERN_SOURCE, 'g'))) {
|
|
766
|
+
const index = match.index ?? 0;
|
|
767
|
+
if (index > lastIndex) {
|
|
768
|
+
parts.push(JSON.stringify(resolved.slice(lastIndex, index)));
|
|
769
|
+
}
|
|
770
|
+
const [, resourceId, fieldPath] = match;
|
|
771
|
+
if (resourceId && fieldPath) {
|
|
772
|
+
parts.push(`string(${markerToCelPath(resourceId, fieldPath, context)})`);
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
parts.push(JSON.stringify(match[0]));
|
|
776
|
+
}
|
|
777
|
+
lastIndex = index + match[0].length;
|
|
778
|
+
}
|
|
779
|
+
if (lastIndex < resolved.length) {
|
|
780
|
+
parts.push(JSON.stringify(resolved.slice(lastIndex)));
|
|
781
|
+
}
|
|
782
|
+
return parts.length > 0 ? parts.join(' + ') : JSON.stringify(resolved);
|
|
783
|
+
}
|
|
784
|
+
function finalizeValueTreeCelForKro(expr, context) {
|
|
785
|
+
const resolved = normalizeCelArrayIndexPaths(resolveNestedCompositionRefs(expr, context?.nestedStatusCel, context?.resourceIds));
|
|
786
|
+
return `\${${resolved}}`;
|
|
787
|
+
}
|
|
788
|
+
function celLiteralForValueTree(value, context, path) {
|
|
789
|
+
if (isValuesMergeExpression(value)) {
|
|
790
|
+
return celValuesMergeExpression(value.base, value.overlays, context, path);
|
|
791
|
+
}
|
|
792
|
+
if (isKubernetesRef(value) || isCelExpression(value)) {
|
|
793
|
+
const expression = unwrapKroExpression(processResourceReferences(value, context, path)) ?? 'null';
|
|
794
|
+
return celValueTreeBranch(expression);
|
|
795
|
+
}
|
|
796
|
+
if (typeof value === 'string') {
|
|
797
|
+
return value.includes('__KUBERNETES_REF_') || value.includes('${')
|
|
798
|
+
? celMarkerStringLiteralForValueTree(value, context)
|
|
799
|
+
: JSON.stringify(value);
|
|
800
|
+
}
|
|
801
|
+
if (value === null || typeof value === 'number' || typeof value === 'boolean') {
|
|
802
|
+
return JSON.stringify(value);
|
|
803
|
+
}
|
|
804
|
+
if (value === undefined)
|
|
805
|
+
return 'null';
|
|
806
|
+
if (Array.isArray(value)) {
|
|
807
|
+
return `[${value
|
|
808
|
+
.filter((entry) => entry !== undefined)
|
|
809
|
+
.map((entry, index) => celLiteralForValueTree(entry, context, arrayChildPath(path, index)))
|
|
810
|
+
.join(', ')}]`;
|
|
811
|
+
}
|
|
812
|
+
if (typeof value === 'object' && isPlainSerializableObject(value)) {
|
|
813
|
+
const entries = Object.entries(value).flatMap(([key, child]) => {
|
|
814
|
+
if (child === undefined)
|
|
815
|
+
return [];
|
|
816
|
+
return [
|
|
817
|
+
`${JSON.stringify(key)}: ${celLiteralForValueTree(child, context, childPath(path, key))}`,
|
|
818
|
+
];
|
|
819
|
+
});
|
|
820
|
+
return `{${entries.join(', ')}}`;
|
|
821
|
+
}
|
|
822
|
+
throw valueTreeSerializationError(path, 'Only plain data, refs, CEL expressions, and templates can be merged.');
|
|
823
|
+
}
|
|
824
|
+
function mergeValueTrees(base, overlay, path) {
|
|
825
|
+
if (overlay === undefined)
|
|
826
|
+
return base;
|
|
827
|
+
if (isValuesMergeExpression(overlay)) {
|
|
828
|
+
let mergedOverlay = overlay.base;
|
|
829
|
+
const normalizedOverlays = Array.isArray(overlay.overlays)
|
|
830
|
+
? overlay.overlays
|
|
831
|
+
: [overlay.overlays];
|
|
832
|
+
normalizedOverlays.forEach((nestedOverlay, index) => {
|
|
833
|
+
mergedOverlay = mergeValueTrees(mergedOverlay, nestedOverlay, `${path}.overlay[${index}]`);
|
|
834
|
+
});
|
|
835
|
+
return mergeValueTrees(base, mergedOverlay, path);
|
|
836
|
+
}
|
|
837
|
+
if (Array.isArray(overlay))
|
|
838
|
+
return overlay;
|
|
839
|
+
if (isKubernetesRef(overlay) || isCelExpression(overlay)) {
|
|
840
|
+
if (path === 'spec.values') {
|
|
841
|
+
throw valueTreeSerializationError(path, 'Kro CEL map merge support is required for whole-object values refs or CEL maps.');
|
|
842
|
+
}
|
|
843
|
+
return overlay;
|
|
844
|
+
}
|
|
845
|
+
if (typeof base === 'object' &&
|
|
846
|
+
base !== null &&
|
|
847
|
+
isPlainSerializableObject(base) &&
|
|
848
|
+
typeof overlay === 'object' &&
|
|
849
|
+
overlay !== null &&
|
|
850
|
+
isPlainSerializableObject(overlay)) {
|
|
851
|
+
return Object.fromEntries(Array.from(new Set([...Object.keys(base), ...Object.keys(overlay)])).flatMap((key) => {
|
|
852
|
+
const merged = mergeValueTrees(base[key], overlay[key], childPath(path, key));
|
|
853
|
+
return merged === undefined ? [] : [[key, merged]];
|
|
854
|
+
}));
|
|
855
|
+
}
|
|
856
|
+
return overlay;
|
|
857
|
+
}
|
|
858
|
+
function needsRuntimeMapMerge(value) {
|
|
859
|
+
if (isKubernetesRef(value) || isCelExpression(value))
|
|
860
|
+
return true;
|
|
861
|
+
if (isValuesMergeExpression(value)) {
|
|
862
|
+
const normalizedOverlays = Array.isArray(value.overlays) ? value.overlays : [value.overlays];
|
|
863
|
+
return (needsRuntimeMapMerge(value.base) ||
|
|
864
|
+
normalizedOverlays.some((overlay) => needsRuntimeMapMerge(overlay)));
|
|
865
|
+
}
|
|
866
|
+
return false;
|
|
867
|
+
}
|
|
868
|
+
function isKnownMergeObject(value) {
|
|
869
|
+
return (!!value &&
|
|
870
|
+
typeof value === 'object' &&
|
|
871
|
+
!Array.isArray(value) &&
|
|
872
|
+
isPlainSerializableObject(value) &&
|
|
873
|
+
!isKubernetesRef(value) &&
|
|
874
|
+
!isCelExpression(value) &&
|
|
875
|
+
!isValuesMergeExpression(value));
|
|
876
|
+
}
|
|
877
|
+
function mapKeyAccess(mapExpression, key) {
|
|
878
|
+
return `(${mapExpression})[${JSON.stringify(key)}]`;
|
|
879
|
+
}
|
|
880
|
+
function mapHasKey(mapExpression, key) {
|
|
881
|
+
return `${JSON.stringify(key)} in (${mapExpression})`;
|
|
882
|
+
}
|
|
883
|
+
function celMapMergeOperand(value, context, path) {
|
|
884
|
+
if (isValuesMergeExpression(value)) {
|
|
885
|
+
return celValuesMergeExpression(value.base, value.overlays, context, path);
|
|
886
|
+
}
|
|
887
|
+
if (isKubernetesRef(value)) {
|
|
888
|
+
const celPath = getInnerCelPath(value);
|
|
889
|
+
const guard = optionalSchemaSpecGuard(celPath, context?.omitFields);
|
|
890
|
+
const operand = `json.unmarshal(json.marshal(${celPath}))`;
|
|
891
|
+
return guard ? `${guard} ? ${operand} : {}` : operand;
|
|
892
|
+
}
|
|
893
|
+
const expression = celLiteralForValueTree(value, context, path);
|
|
894
|
+
return `json.unmarshal(json.marshal(${expression}))`;
|
|
895
|
+
}
|
|
896
|
+
function celRuntimeMapMergeExpression(base, overlays, context, path) {
|
|
897
|
+
let expression;
|
|
898
|
+
let knownBase = base;
|
|
899
|
+
const normalizedOverlays = Array.isArray(overlays) ? overlays : [overlays];
|
|
900
|
+
normalizedOverlays.forEach((overlay, index) => {
|
|
901
|
+
if (overlay === undefined)
|
|
902
|
+
return;
|
|
903
|
+
const overlayPath = path || `spec.values.overlay[${index}]`;
|
|
904
|
+
if (expression === undefined) {
|
|
905
|
+
if (isKnownMergeObject(knownBase) && isKnownMergeObject(overlay)) {
|
|
906
|
+
knownBase = mergeValueTrees(knownBase, overlay, overlayPath);
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
if (isKnownMergeObject(knownBase)) {
|
|
910
|
+
expression = isKnownMergeObject(overlay)
|
|
911
|
+
? celDeepMergeKnownOverlay(celMapMergeOperand(knownBase, context, path), overlay, context, overlayPath)
|
|
912
|
+
: celDeepMergeRuntimeOverlay(knownBase, celMapMergeOperand(overlay, context, overlayPath), context, overlayPath);
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
expression = isKnownMergeObject(overlay)
|
|
916
|
+
? celDeepMergeKnownOverlay(celMapMergeOperand(knownBase, context, path), overlay, context, overlayPath)
|
|
917
|
+
: `(${celMapMergeOperand(knownBase, context, path)}).merge(${celMapMergeOperand(overlay, context, overlayPath)})`;
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
expression = isKnownMergeObject(overlay)
|
|
921
|
+
? celDeepMergeKnownOverlay(expression, overlay, context, overlayPath)
|
|
922
|
+
: `(${expression}).merge(${celMapMergeOperand(overlay, context, overlayPath)})`;
|
|
923
|
+
});
|
|
924
|
+
return expression ?? celLiteralForValueTree(knownBase, context, path);
|
|
925
|
+
}
|
|
926
|
+
function celDeepMergeRuntimeOverlay(base, overlayExpression, context, path) {
|
|
927
|
+
const entries = Object.entries(base).flatMap(([key, baseValue]) => {
|
|
928
|
+
if (baseValue === undefined)
|
|
929
|
+
return [];
|
|
930
|
+
const overlayValue = mapKeyAccess(overlayExpression, key);
|
|
931
|
+
const baseExpression = celLiteralForValueTree(baseValue, context, childPath(path, key));
|
|
932
|
+
const mergedValue = isKnownMergeObject(baseValue)
|
|
933
|
+
? celDeepMergeRuntimeOverlay(baseValue, overlayValue, context, childPath(path, key))
|
|
934
|
+
: overlayValue;
|
|
935
|
+
return [
|
|
936
|
+
`${JSON.stringify(key)}: ${mapHasKey(overlayExpression, key)} ? ${mergedValue} : ${baseExpression}`,
|
|
937
|
+
];
|
|
938
|
+
});
|
|
939
|
+
return `(${overlayExpression}).merge({${entries.join(', ')}})`;
|
|
940
|
+
}
|
|
941
|
+
function celDeepMergeKnownOverlay(baseExpression, overlay, context, path) {
|
|
942
|
+
const entries = Object.entries(overlay).flatMap(([key, overlayValue]) => {
|
|
943
|
+
if (overlayValue === undefined)
|
|
944
|
+
return [];
|
|
945
|
+
const overlayExpression = celLiteralForValueTree(overlayValue, context, childPath(path, key));
|
|
946
|
+
const baseValue = mapKeyAccess(baseExpression, key);
|
|
947
|
+
const mergedValue = isKnownMergeObject(overlayValue)
|
|
948
|
+
? `${mapHasKey(baseExpression, key)} ? ${celDeepMergeKnownOverlay(baseValue, overlayValue, context, childPath(path, key))} : ${overlayExpression}`
|
|
949
|
+
: overlayExpression;
|
|
950
|
+
return [`${JSON.stringify(key)}: ${mergedValue}`];
|
|
951
|
+
});
|
|
952
|
+
return `(${baseExpression}).merge({${entries.join(', ')}})`;
|
|
953
|
+
}
|
|
954
|
+
function celValuesMergeExpression(base, overlays, context, path) {
|
|
955
|
+
const flattened = flattenValuesMergeExpression(base, overlays);
|
|
956
|
+
base = flattened.base;
|
|
957
|
+
const normalizedOverlays = flattened.overlays;
|
|
958
|
+
if (needsRuntimeMapMerge(base) ||
|
|
959
|
+
normalizedOverlays.some((overlay) => needsRuntimeMapMerge(overlay))) {
|
|
960
|
+
return celRuntimeMapMergeExpression(base, normalizedOverlays, context, path || 'spec.values');
|
|
961
|
+
}
|
|
962
|
+
let merged = base;
|
|
963
|
+
normalizedOverlays.forEach((overlay, index) => {
|
|
964
|
+
merged = mergeValueTrees(merged, overlay, path || `spec.values.overlay[${index}]`);
|
|
965
|
+
});
|
|
966
|
+
return celLiteralForValueTree(merged, context, path);
|
|
967
|
+
}
|
|
968
|
+
function valuesMergeNeedsRuntimeMapMerge(base, overlays) {
|
|
969
|
+
const flattened = flattenValuesMergeExpression(base, overlays);
|
|
970
|
+
return (needsRuntimeMapMerge(flattened.base) ||
|
|
971
|
+
flattened.overlays.some((overlay) => needsRuntimeMapMerge(overlay)));
|
|
972
|
+
}
|
|
973
|
+
function flattenValuesMergeExpression(base, overlays) {
|
|
974
|
+
let flattenedBase = base;
|
|
975
|
+
const flattenedOverlays = Array.isArray(overlays) ? [...overlays] : [overlays];
|
|
976
|
+
while (isValuesMergeExpression(flattenedBase)) {
|
|
977
|
+
const nestedOverlays = Array.isArray(flattenedBase.overlays)
|
|
978
|
+
? flattenedBase.overlays
|
|
979
|
+
: [flattenedBase.overlays];
|
|
980
|
+
flattenedOverlays.unshift(...nestedOverlays);
|
|
981
|
+
flattenedBase = flattenedBase.base;
|
|
982
|
+
}
|
|
983
|
+
return { base: flattenedBase, overlays: flattenedOverlays };
|
|
984
|
+
}
|
|
985
|
+
function collectKnownMergeKeys(value, keys = new Set()) {
|
|
986
|
+
if (isValuesMergeExpression(value)) {
|
|
987
|
+
collectKnownMergeKeys(value.base, keys);
|
|
988
|
+
const normalizedOverlays = Array.isArray(value.overlays) ? value.overlays : [value.overlays];
|
|
989
|
+
normalizedOverlays.forEach((overlay) => collectKnownMergeKeys(overlay, keys));
|
|
990
|
+
return keys;
|
|
991
|
+
}
|
|
992
|
+
if (isKnownMergeObject(value)) {
|
|
993
|
+
Object.keys(value).forEach((key) => keys.add(key));
|
|
994
|
+
}
|
|
995
|
+
return keys;
|
|
996
|
+
}
|
|
997
|
+
function celValuesMergeTemplateObject(base, overlays, context, path) {
|
|
998
|
+
const flattened = flattenValuesMergeExpression(base, overlays);
|
|
999
|
+
base = flattened.base;
|
|
1000
|
+
const normalizedOverlays = flattened.overlays;
|
|
1001
|
+
const knownKeys = collectKnownMergeKeys(base);
|
|
1002
|
+
normalizedOverlays.forEach((overlay) => collectKnownMergeKeys(overlay, knownKeys));
|
|
1003
|
+
const orderedKeys = Array.from(knownKeys);
|
|
1004
|
+
if (orderedKeys.length === 0)
|
|
1005
|
+
return undefined;
|
|
1006
|
+
return Object.fromEntries(orderedKeys.map((key) => {
|
|
1007
|
+
const value = celValuesMergeTemplateValue(base, normalizedOverlays, key, context, path);
|
|
1008
|
+
return [key, value];
|
|
1009
|
+
}));
|
|
1010
|
+
}
|
|
1011
|
+
function celValuesMergeTemplateValue(base, overlays, key, context, path) {
|
|
1012
|
+
let knownValue = isKnownMergeObject(base) ? base[key] : undefined;
|
|
1013
|
+
for (const [index, overlay] of overlays.entries()) {
|
|
1014
|
+
if (overlay === undefined)
|
|
1015
|
+
continue;
|
|
1016
|
+
const overlayPath = path || `spec.values.overlay[${index}]`;
|
|
1017
|
+
if (isKnownMergeObject(overlay)) {
|
|
1018
|
+
if (!Object.hasOwn(overlay, key))
|
|
1019
|
+
continue;
|
|
1020
|
+
knownValue = mergeValueTrees(knownValue, overlay[key], childPath(overlayPath, key));
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
1023
|
+
return celValuesMergeRuntimeTemplateValue(knownValue, celMapMergeOperand(overlay, context, overlayPath), key, context, childPath(path, key));
|
|
1024
|
+
}
|
|
1025
|
+
return processResourceReferences(knownValue, context, childPath(path, key));
|
|
1026
|
+
}
|
|
1027
|
+
function celValuesMergeRuntimeTemplateValue(fallbackValue, overlayExpression, key, context, path) {
|
|
1028
|
+
if (isValuesMergeExpression(fallbackValue)) {
|
|
1029
|
+
const templateObject = celValuesMergeTemplateObject(fallbackValue.base, fallbackValue.overlays, context, path);
|
|
1030
|
+
if (templateObject) {
|
|
1031
|
+
return celValuesMergeRuntimeTemplateValue(templateObject, overlayExpression, key, context, path);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
if (isKnownMergeObject(fallbackValue)) {
|
|
1035
|
+
const childOverlay = `(${mapHasKey(overlayExpression, key)} ? ${mapKeyAccess(overlayExpression, key)} : {})`;
|
|
1036
|
+
return Object.fromEntries(Object.entries(fallbackValue).flatMap(([childKey, childValue]) => {
|
|
1037
|
+
if (childValue === undefined)
|
|
1038
|
+
return [];
|
|
1039
|
+
return [
|
|
1040
|
+
[
|
|
1041
|
+
childKey,
|
|
1042
|
+
celValuesMergeRuntimeTemplateValue(childValue, childOverlay, childKey, context, childPath(path, childKey)),
|
|
1043
|
+
],
|
|
1044
|
+
];
|
|
1045
|
+
}));
|
|
1046
|
+
}
|
|
1047
|
+
if (Array.isArray(fallbackValue)) {
|
|
1048
|
+
return processResourceReferences(fallbackValue, context, path);
|
|
1049
|
+
}
|
|
1050
|
+
const fallbackExpression = celLiteralForValueTree(fallbackValue, context, path);
|
|
1051
|
+
const expression = `${mapHasKey(overlayExpression, key)} ? ${mapKeyAccess(overlayExpression, key)} : (${fallbackExpression})`;
|
|
1052
|
+
return finalizeCelForKro(expression, context?.nestedStatusCel, context);
|
|
1053
|
+
}
|
|
1054
|
+
export function processResourceReferences(obj, context, path = '') {
|
|
1055
|
+
const inValueTree = valueTreePath(path);
|
|
1056
|
+
if (isValuesMergeExpression(obj)) {
|
|
1057
|
+
if (path === 'spec.values' && !valuesMergeNeedsRuntimeMapMerge(obj.base, obj.overlays)) {
|
|
1058
|
+
const templateObject = celValuesMergeTemplateObject(obj.base, obj.overlays, context, path);
|
|
1059
|
+
if (templateObject)
|
|
1060
|
+
return templateObject;
|
|
1061
|
+
}
|
|
1062
|
+
return finalizeValueTreeCelForKro(celValuesMergeExpression(obj.base, obj.overlays, context, path), context);
|
|
1063
|
+
}
|
|
660
1064
|
if (isKubernetesRef(obj)) {
|
|
661
1065
|
return generateCelExpression(obj, context);
|
|
662
1066
|
}
|
|
663
1067
|
if (isCelExpression(obj)) {
|
|
664
1068
|
if (obj.__isTemplate) {
|
|
665
|
-
return obj.expression.replace(/\$\{([^}]+)\}/g, (_match, innerExpr) => finalizeCelForKro(innerExpr, context?.nestedStatusCel, context));
|
|
1069
|
+
return obj.expression.replace(/\$\{([^}]+)\}/g, (_match, innerExpr) => finalizeCelForKro(normalizeSchemaSpecShorthand(innerExpr), context?.nestedStatusCel, context));
|
|
666
1070
|
}
|
|
667
1071
|
// Bare CelExpression — may be a single schema.spec.X reference (possibly
|
|
668
1072
|
// wrapped in `string(...)`). Delegate any single schema ref to
|
|
@@ -679,6 +1083,12 @@ export function processResourceReferences(obj, context) {
|
|
|
679
1083
|
}
|
|
680
1084
|
return finalizeCelForKro(expr, context?.nestedStatusCel, context);
|
|
681
1085
|
}
|
|
1086
|
+
if (inValueTree && typeof obj === 'function') {
|
|
1087
|
+
throw valueTreeSerializationError(path, 'Functions are direct-mode only.');
|
|
1088
|
+
}
|
|
1089
|
+
if (inValueTree && typeof obj === 'symbol') {
|
|
1090
|
+
throw valueTreeSerializationError(path, 'Symbols are runtime-only values.');
|
|
1091
|
+
}
|
|
682
1092
|
// Strings containing __KUBERNETES_REF__ markers from template literals.
|
|
683
1093
|
// First resolve any nested-composition references — markers produced by
|
|
684
1094
|
// `createKubernetesRefProxy` use a virtual baseId as the resourceId,
|
|
@@ -695,15 +1105,24 @@ export function processResourceReferences(obj, context) {
|
|
|
695
1105
|
return resolved;
|
|
696
1106
|
}
|
|
697
1107
|
if (Array.isArray(obj)) {
|
|
698
|
-
return obj.
|
|
1108
|
+
return obj.flatMap((item, index) => {
|
|
1109
|
+
if (inValueTree && item === undefined)
|
|
1110
|
+
return [];
|
|
1111
|
+
return [processResourceReferences(item, context, arrayChildPath(path, index))];
|
|
1112
|
+
});
|
|
699
1113
|
}
|
|
700
1114
|
if (obj && typeof obj === 'object') {
|
|
1115
|
+
if (inValueTree && !isPlainSerializableObject(obj)) {
|
|
1116
|
+
throw valueTreeSerializationError(path, `${obj.constructor?.name ?? 'This value'} is not a plain serializable object.`);
|
|
1117
|
+
}
|
|
701
1118
|
const result = {};
|
|
702
1119
|
for (const [key, value] of Object.entries(obj)) {
|
|
703
|
-
// Exclude hidden resourceId
|
|
704
|
-
if (key === '__resourceId'
|
|
1120
|
+
// Exclude hidden resourceId metadata; nested `id` fields can be valid resource config.
|
|
1121
|
+
if (key === '__resourceId')
|
|
1122
|
+
continue;
|
|
1123
|
+
if (inValueTree && value === undefined)
|
|
705
1124
|
continue;
|
|
706
|
-
result[key] = processResourceReferences(value, context);
|
|
1125
|
+
result[key] = processResourceReferences(value, context, childPath(path, key));
|
|
707
1126
|
}
|
|
708
1127
|
// Preserve resource metadata (resourceId, readinessEvaluator, etc.) via WeakMap
|
|
709
1128
|
// Also migrates legacy non-enumerable properties from source
|
|
@@ -755,20 +1174,16 @@ export function serializeStatusMappingsToCel(statusMappings, nestedStatusCel, re
|
|
|
755
1174
|
: normalized;
|
|
756
1175
|
}
|
|
757
1176
|
/**
|
|
758
|
-
* Rewrite
|
|
759
|
-
* the result is valid KRO status CEL. KRO does not accept `schema.spec.*`
|
|
760
|
-
* in status CEL, so we apply two rewrites:
|
|
761
|
-
*
|
|
762
|
-
* - `schema.spec.X.Y.orValue(Z)` → `Z` (substitute the default)
|
|
763
|
-
* - `schema.spec.X.Y` → `X.spec.Y` iff `X` is a known resource ID
|
|
764
|
-
* (routes the reference to the deployed resource's spec field)
|
|
1177
|
+
* Rewrite resolved schema refs for KRO status CEL.
|
|
765
1178
|
*
|
|
766
|
-
*
|
|
767
|
-
*
|
|
1179
|
+
* KRO validates CR spec references as `schema.spec.*` in status mappings.
|
|
1180
|
+
* The only schema-to-resource rewrite we keep is the explicit legacy shape
|
|
1181
|
+
* `schema.spec.<resourceId>.<field>`, where `<resourceId>` is a real graph
|
|
1182
|
+
* resource. Plain CR spec fields must remain `schema.spec.*`.
|
|
768
1183
|
*/
|
|
769
1184
|
function rewriteSchemaRefsForKroStatus(expr) {
|
|
770
1185
|
let out = expr.replace(/__schema__\.spec\.[a-zA-Z0-9_.]+\.orValue\(([^)]+)\)/g, '$1');
|
|
771
|
-
out = out.replace(/__schema__\.spec\.([a-zA-Z0-9_.]+)/g, 'spec.$1');
|
|
1186
|
+
out = out.replace(/__schema__\.spec\.([a-zA-Z0-9_.]+)/g, 'schema.spec.$1');
|
|
772
1187
|
out = out.replace(/schema\.spec\.[a-zA-Z0-9_.]+\.orValue\(([^)]+)\)/g, '$1');
|
|
773
1188
|
out = out.replace(/schema\.spec\.([a-zA-Z0-9]+)\.([a-zA-Z0-9.]+)/g, (_match, firstSegment, rest) => {
|
|
774
1189
|
if (resourceIds?.has(firstSegment)) {
|
|
@@ -776,7 +1191,6 @@ export function serializeStatusMappingsToCel(statusMappings, nestedStatusCel, re
|
|
|
776
1191
|
}
|
|
777
1192
|
return _match;
|
|
778
1193
|
});
|
|
779
|
-
out = out.replace(/schema\.spec\.([a-zA-Z_$][\w$]*)/g, 'spec.$1');
|
|
780
1194
|
return out;
|
|
781
1195
|
}
|
|
782
1196
|
/**
|
|
@@ -785,18 +1199,20 @@ export function serializeStatusMappingsToCel(statusMappings, nestedStatusCel, re
|
|
|
785
1199
|
* Centralized here so the two branches don't drift on what "produce
|
|
786
1200
|
* KRO status CEL from a resolved expression" means.
|
|
787
1201
|
*/
|
|
788
|
-
function statusFieldFromExpression(expr) {
|
|
789
|
-
const resolved = normalizeLocalResourceExpr(resolveNestedCompositionRefs(normalizeLocalResourceExpr(expr), nestedStatusCel, resourceIds));
|
|
1202
|
+
function statusFieldFromExpression(expr, rewriteSchemaRefs = true) {
|
|
1203
|
+
const resolved = normalizeCelArrayIndexPaths(normalizeLocalResourceExpr(resolveNestedCompositionRefs(normalizeLocalResourceExpr(expr), nestedStatusCel, resourceIds)));
|
|
790
1204
|
if (resolved.includes('__KUBERNETES_REF_')) {
|
|
791
1205
|
// Marker-laden — use mixed-template form.
|
|
792
|
-
|
|
1206
|
+
const converted = convertKubernetesRefMarkersTocel(resolved);
|
|
1207
|
+
return rewriteSchemaRefs ? rewriteSchemaRefsForKroStatus(converted) : converted;
|
|
793
1208
|
}
|
|
794
1209
|
if (resolved.includes('${')) {
|
|
795
1210
|
// Already a KRO mixed-template value. Do not wrap it again as
|
|
796
1211
|
// `${http://${...}}`, which is invalid CEL/YAML for status fields.
|
|
797
|
-
return rewriteSchemaRefsForKroStatus(resolved);
|
|
1212
|
+
return rewriteSchemaRefs ? rewriteSchemaRefsForKroStatus(resolved) : resolved;
|
|
798
1213
|
}
|
|
799
|
-
|
|
1214
|
+
const expression = rewriteSchemaRefs ? rewriteSchemaRefsForKroStatus(resolved) : resolved;
|
|
1215
|
+
return `\${${expression}}`;
|
|
800
1216
|
}
|
|
801
1217
|
function serializeValue(value) {
|
|
802
1218
|
if (isKubernetesRef(value)) {
|
|
@@ -819,7 +1235,7 @@ export function serializeStatusMappingsToCel(statusMappings, nestedStatusCel, re
|
|
|
819
1235
|
}
|
|
820
1236
|
if (isCelExpression(value)) {
|
|
821
1237
|
if (value.__isTemplate) {
|
|
822
|
-
return value.expression.replace(/\$\{([^}]+)\}/g, (_match, innerExpr) => statusFieldFromExpression(innerExpr));
|
|
1238
|
+
return value.expression.replace(/\$\{([^}]+)\}/g, (_match, innerExpr) => statusFieldFromExpression(normalizeSchemaSpecShorthand(innerExpr), false));
|
|
823
1239
|
}
|
|
824
1240
|
return statusFieldFromExpression(value.expression);
|
|
825
1241
|
}
|