@firestartr/cli 2.11.0-snapshot-feature-cr-git-sha-tags-1 → 2.11.0-snapshot-patch-claims-3

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/build/index.js CHANGED
@@ -128395,7 +128395,7 @@ function trasformLeg(leg) {
128395
128395
 
128396
128396
 
128397
128397
  function getFeatureRenderedPathForEntity(entity, featureName, basePath = '/tmp') {
128398
- const entityFolderName = `${entity.metadata.name}`.toLowerCase();
128398
+ const entityFolderName = `${entity.metadata?.name ?? entity.name}`.toLowerCase();
128399
128399
  return external_path_default().join(basePath, entityFolderName, featureName);
128400
128400
  }
128401
128401
  function getFeatureRenderedConfigForComponent(entity, featureName, basePath = '/tmp/features') {
@@ -164488,771 +164488,6 @@ const base_secrets_schema = 'firestartr.dev://common/SecretsClaim';
164488
164488
  SecretsClaimSchema: base_secrets_schema,
164489
164489
  });
164490
164490
 
164491
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/lazy_loader.ts
164492
-
164493
-
164494
-
164495
-
164496
-
164497
-
164498
-
164499
-
164500
-
164501
-
164502
-
164503
-
164504
-
164505
-
164506
-
164507
-
164508
- // We need to ensure the grep command is a gnu version
164509
- let IS_GNU_GREP = false;
164510
- let GREP_ON_CHECK = false;
164511
- // this loop is for avoid race conditions
164512
- // once a check is running no more checks
164513
- async function waitForCheck() {
164514
- const f_wait = () => new Promise((timeOut) => setTimeout(timeOut, 10));
164515
- do {
164516
- await f_wait();
164517
- } while (GREP_ON_CHECK);
164518
- }
164519
- function checkGrep() {
164520
- return new Promise((ok, ko) => {
164521
- // idempotency: already checked
164522
- if (IS_GNU_GREP)
164523
- return ok();
164524
- // a check is already running no new checks
164525
- if (GREP_ON_CHECK)
164526
- return waitForCheck();
164527
- logger.info('Checking the grep command');
164528
- GREP_ON_CHECK = true;
164529
- const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['--version']);
164530
- handler.on('close', (code) => {
164531
- GREP_ON_CHECK = false;
164532
- if (code === 0) {
164533
- logger.debug('Grep is a gnu grep');
164534
- IS_GNU_GREP = true;
164535
- ok();
164536
- }
164537
- else {
164538
- ko('Lazy evaluation needs a gnu-grep command');
164539
- }
164540
- });
164541
- });
164542
- }
164543
- async function loadClaim(claimRef, org, defaults = loadClaimDefaults(), patchClaim = loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, existingRefs = {}, postValidations = new Map()) {
164544
- await checkGrep();
164545
- let result = existingRefs;
164546
- logger.info(`Load reference ${claimRef}`);
164547
- initVirtualClaims(org);
164548
- logger.info(`Load reference (parts) ${claimRef.split(/-/)[0]} ${claimRef.replace(/^[^-]+-/, '')}`);
164549
- // cargas datos con grep
164550
- try {
164551
- const claimData = await lazyGetClaim(claimRef.split(/-/)[0], claimRef.replace(/^[^-]+-/, ''), org, cwd);
164552
- const claim = patchClaim(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(claimData), defaults);
164553
- let variants = [];
164554
- if (claim.kind === 'TFWorkspaceClaim' &&
164555
- claim.providers?.terraform?.variants) {
164556
- variants = claim.providers.terraform.variants;
164557
- delete claim.providers.terraform.variants;
164558
- }
164559
- logger.silly(`Patched claim is:
164560
- ---
164561
- ${catalog_common/* default.io.toYaml */.Z.io.toYaml(claim)}`);
164562
- try {
164563
- validateClaim(claim, base[`${claim.kind}Schema`]);
164564
- await optionalValidation(claim);
164565
- }
164566
- catch (error) {
164567
- let errorMsg = '';
164568
- if (Array.isArray(error)) {
164569
- for (const data of error) {
164570
- errorMsg = `${errorMsg}
164571
- - ${data.instancePath}: ${data.message}`;
164572
- }
164573
- }
164574
- else {
164575
- errorMsg = error instanceof Error ? error.message : String(error);
164576
- }
164577
- throw new Error(`Error when validating claim ${claimRef}: ${errorMsg}`);
164578
- }
164579
- result[claimRef] = {};
164580
- result[claimRef]['claim'] = claim;
164581
- result[claimRef]['claimPath'] = VisitedClaims[claimRef];
164582
- if (VisitedClaims[claimRef] === 'virtual') {
164583
- result = await setVirtualClaimAdditionalData(result, claim, claimRef);
164584
- }
164585
- else {
164586
- result = await setNonVirtualClaimAdditionalData(result, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
164587
- }
164588
- const claimKind = claim.kind;
164589
- const references = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(claim));
164590
- for (const ref of references) {
164591
- if (!result[ref]) {
164592
- const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
164593
- result = lodash_default().merge(result, resolvedReferences);
164594
- }
164595
- }
164596
- if (variants.length > 0) {
164597
- const parentClaimPath = VisitedClaims[claimRef];
164598
- for (const variant of variants) {
164599
- if (!variant.name || !variant.overrides) {
164600
- throw new Error(`Variant in claim ${claimRef} is missing required field 'name' or 'overrides'`);
164601
- }
164602
- const prohibitedOverrideFields = ['source', 'module', 'name'];
164603
- for (const field of prohibitedOverrideFields) {
164604
- if (field in variant.overrides) {
164605
- throw new Error(`Variant '${variant.name}' in claim ${claimRef} cannot override '${field}'`);
164606
- }
164607
- }
164608
- const composedName = claim.providers.terraform.name + '-' + variant.name;
164609
- const variantClaim = lodash_default().cloneDeep(claim);
164610
- variantClaim._parentClaimName = claim.name;
164611
- variantClaim.name = composedName;
164612
- variantClaim.providers.terraform = lodash_default().merge({}, claim.providers.terraform, variant.overrides);
164613
- variantClaim.providers.terraform.name = composedName;
164614
- if (!variantClaim.annotations) {
164615
- variantClaim.annotations = {};
164616
- }
164617
- variantClaim.annotations['firestartr.dev/variant-of'] =
164618
- claim.providers.terraform.name;
164619
- const variantRef = `TFWorkspaceClaim-${composedName}`;
164620
- if (result[variantRef]) {
164621
- throw new Error(`Variant name '${variant.name}' (composed: '${composedName}') from claim '${claimRef}' conflicts with existing claim '${variantRef}'`);
164622
- }
164623
- logger.info(`Creating synthetic variant claim ${variantRef}`);
164624
- result[variantRef] = {};
164625
- result[variantRef]['claim'] = variantClaim;
164626
- result[variantRef]['claimPath'] = parentClaimPath;
164627
- result = await setNonVirtualClaimAdditionalData(result, variantClaim, variantRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
164628
- const variantReferences = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(variantClaim));
164629
- for (const ref of variantReferences) {
164630
- if (!result[ref]) {
164631
- const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
164632
- result = lodash_default().merge(result, resolvedReferences);
164633
- }
164634
- }
164635
- }
164636
- }
164637
- }
164638
- catch (err) {
164639
- throw `Lazy Loading: ${err}`;
164640
- }
164641
- return [result, postValidations];
164642
- }
164643
- let LoadedClaims = {};
164644
- let VisitedClaims = {};
164645
- let DuplicatedClaims = {};
164646
- function resetLazyLoader() {
164647
- LoadedClaims = {};
164648
- VisitedClaims = {};
164649
- DuplicatedClaims = {};
164650
- }
164651
- async function lazyGetClaim(kind, name, org, cwd) {
164652
- const indice = `${kind}-${name}`;
164653
- logger.info(`Lazy loading ${kind}-${name} with index ${indice}`);
164654
- if (indice in LoadedClaims)
164655
- return LoadedClaims[indice];
164656
- if (isVirtualClaim(kind, name)) {
164657
- await loadVirtualClaim(kind, name, org);
164658
- }
164659
- else {
164660
- await getClaimsByName(name, cwd);
164661
- }
164662
- if (indice in LoadedClaims) {
164663
- return LoadedClaims[indice];
164664
- }
164665
- else {
164666
- throw new Error(`Error: ${kind}-${name} not found`);
164667
- }
164668
- }
164669
- async function getClaimsByName(name, cwd = '.') {
164670
- return new Promise((ok, ko) => {
164671
- const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['-r', '-l', '--include=*', '-E', `name: "?${name}"?`, '.'], {
164672
- cwd: cwd,
164673
- });
164674
- logger.info(`Running ${['grep', '-r', '-l', '--include', '-E', 'name: "' + name + '"?', '.'].join(' ')}`);
164675
- const entradas = [];
164676
- const chunks = [];
164677
- handler.stdout.on('data', (data) => {
164678
- chunks.push(data.toString('utf-8'));
164679
- });
164680
- handler.on('close', () => {
164681
- const dataFinal = chunks.join('');
164682
- const fileNameList = dataFinal.split(/\n/).filter((line) => line !== '');
164683
- for (const fileName of fileNameList) {
164684
- // only yaml files
164685
- if (!fileName.match(/\.yml$|\.yaml$/)) {
164686
- continue;
164687
- }
164688
- // only entries not already visited
164689
- if (Object.values(VisitedClaims).includes(fileName)) {
164690
- continue;
164691
- }
164692
- entradas.push(external_path_.join(cwd, fileName));
164693
- }
164694
- return Promise.all(entradas.map((entrada) => {
164695
- return loadRawClaim(entrada);
164696
- }))
164697
- .then(() => {
164698
- ok();
164699
- })
164700
- .catch((err) => {
164701
- ko(err);
164702
- });
164703
- });
164704
- });
164705
- }
164706
- async function loadRawClaim(entry) {
164707
- return new Promise((ok, ko) => {
164708
- external_fs_.readFile(entry, 'utf-8', (error, data) => {
164709
- if (error)
164710
- return ko(`Reading ${entry}: ${error}`);
164711
- const claim = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
164712
- if (!('kind' in claim && 'name' in claim)) {
164713
- logger.warn(`Invalid claim file ${entry}`);
164714
- }
164715
- else {
164716
- const claimKey = `${claim.kind}-${claim.name}`;
164717
- if (Object.keys(DuplicatedClaims).includes(claimKey) &&
164718
- DuplicatedClaims[claimKey] !== entry) {
164719
- return ko(`Duplicated claim ${claimKey} found files: ${DuplicatedClaims[claimKey]} and ${entry}`);
164720
- }
164721
- LoadedClaims[claimKey] = data;
164722
- VisitedClaims[claimKey] = entry;
164723
- DuplicatedClaims[claimKey] = entry;
164724
- ok();
164725
- }
164726
- });
164727
- });
164728
- }
164729
- async function setVirtualClaimAdditionalData(renderedData, claim, claimRef) {
164730
- const virtualClaim = getVirtualClaim(claim.kind, claim.name);
164731
- renderedData[claimRef]['initializers'] = [
164732
- new UUIDInitializer(claim),
164733
- new InitializerClaimRef(),
164734
- new InitializerDefault(virtualClaim.getDefaultConfig()),
164735
- ];
164736
- renderedData[claimRef]['globals'] = [];
164737
- renderedData[claimRef]['overrides'] = [];
164738
- renderedData[claimRef]['normalizers'] = [new NameNormalizer()];
164739
- return renderedData;
164740
- }
164741
- async function setNonVirtualClaimAdditionalData(renderedData, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers) {
164742
- renderedData[claimRef]['initializers'] = await loadInitializers(claim, renderedData[claimRef]['claimPath']);
164743
- renderedData[claimRef]['globals'] = await loadGlobals(claim);
164744
- renderedData[claimRef]['overrides'] = loadOverrides(claim);
164745
- renderedData[claimRef]['normalizers'] = await loadNormalizers(claim, renderedData[claimRef]['claimPath']);
164746
- return renderedData;
164747
- }
164748
- async function loadVirtualClaim(kind, name, org) {
164749
- const virtualClaim = getVirtualClaim(kind, name);
164750
- const expandedClaim = await virtualClaim.expand({ org });
164751
- logger.info(`Loading virtual claim ${kind} ${name} ${org}`);
164752
- LoadedClaims[`${kind}-${name}`] = catalog_common/* default.io.toYaml */.Z.io.toYaml(expandedClaim);
164753
- VisitedClaims[`${kind}-${name}`] = 'virtual';
164754
- }
164755
-
164756
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/claimsDefaulter.ts
164757
-
164758
-
164759
-
164760
- /*
164761
- * Default blocks are objects that cannot be merged but applied as a whole. Meaning:
164762
- * - If the object is defined at the claim-level it is maintained *AS IT IS* (no merge)
164763
- * - If the block is missing at the claim-level, the default block is applied
164764
- */
164765
- const defaultBlocksPaths = ['/providers/terraform/sync'];
164766
- function applyBlockAwareDefaults(claim, defaultClaim) {
164767
- // we always work with a clone of the original
164768
- const claimClone = JSON.parse(JSON.stringify(claim));
164769
- // if the block is claim-level defined we store it here
164770
- const defaultBlocks = new Map();
164771
- // we remove the blocks entirely of the claim
164772
- // to achieve an add copy
164773
- for (const defaultBlockPath of defaultBlocksPaths) {
164774
- if (claimsDefaulter_hasDeepPath(claimClone, defaultBlockPath) !== true)
164775
- continue;
164776
- const originalValue = fast_json_patch_default().getValueByPointer(claimClone, defaultBlockPath);
164777
- if (originalValue) {
164778
- logger.silly(`${claim.kind}/${claim.name}: has an original ${defaultBlockPath}: preserving`);
164779
- defaultBlocks.set(defaultBlockPath, originalValue);
164780
- fast_json_patch_default().applyPatch(claimClone, [{ op: 'remove', path: defaultBlockPath }]);
164781
- }
164782
- }
164783
- // if the patch has the same path of a block
164784
- // and the block was defined at claim-level
164785
- // we interchange the value with the original value
164786
- // otherwise we use the default
164787
- const jsonPatchOps = fast_json_patch_default().compare(claimClone, defaultClaim)
164788
- .filter((jp) => jp.op === 'add')
164789
- .map((jp) => {
164790
- if (defaultBlocks.has(jp.path)) {
164791
- return {
164792
- ...jp,
164793
- value: defaultBlocks.get(jp.path),
164794
- };
164795
- }
164796
- else {
164797
- return jp;
164798
- }
164799
- });
164800
- return fast_json_patch_default().applyPatch(claimClone, jsonPatchOps).newDocument;
164801
- }
164802
- function claimsDefaulter_hasDeepPath(data, deepPath) {
164803
- return lodash_default().has(data, deepPath.replace(/\//g, '.').replace(/^\./, ''));
164804
- }
164805
-
164806
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/loader.ts
164807
-
164808
-
164809
-
164810
-
164811
-
164812
-
164813
-
164814
-
164815
-
164816
-
164817
-
164818
-
164819
-
164820
-
164821
-
164822
- const isYamlFile = new RegExp(/\.(yaml|yml)$/);
164823
- /**
164824
- *
164825
- * @returns Virtual Claims
164826
- */
164827
- const virtualClaims = [new FirestartrAllClaim()];
164828
- /*
164829
- * Loads all applicable globals for a given claim and stores them into a list.
164830
- *
164831
- * Input:
164832
- * - claim: our custom claim object, with which we check if the global is applicable
164833
- *
164834
- * Returns:
164835
- * - A GlobalSection array promise, containing all the globals loaded for this claim
164836
- *
164837
- */
164838
- async function loadGlobals(claim) {
164839
- const result = [];
164840
- await crawl(getPath('globals'), (entry) => {
164841
- return isYamlFile.test(entry);
164842
- }, async (entry, data) => {
164843
- const globalData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
164844
- const ext = external_path_.extname(entry);
164845
- const name = external_path_.basename(entry, ext);
164846
- if (name in SECTIONS_BY_FILE_NAME) {
164847
- const section = new SECTIONS_BY_FILE_NAME[name](globalData);
164848
- if (!(await section.validate(SCHEMAS_BY_SECTION_NAME[name] || {}))) {
164849
- throw new Error(`Invalid ${name} global file`);
164850
- }
164851
- if (globalData.kind === claim.kind) {
164852
- result.push(section);
164853
- }
164854
- else {
164855
- logger.debug(`Skipping global ${name} because it is not applicable to ${claim.kind}`);
164856
- }
164857
- }
164858
- });
164859
- logger.info(`Loaded globals ${result}`);
164860
- return result;
164861
- }
164862
- /*
164863
- * If the input claim is a GithubRepositoryClaim, it loads all it's applicable
164864
- * overrides and stores them into a list. Otherwise, it returns an empty list.
164865
- *
164866
- * Input:
164867
- * - claim: our custom claim object, which should be a GithubRepositoryClaim
164868
- *
164869
- * Returns:
164870
- * - An Overrider array, with a GithubRepositoryOverrider if the claim
164871
- * was a GithubRepositoryClaim, or empty if it was not.
164872
- *
164873
- */
164874
- function loadOverrides(claim) {
164875
- const overrides = [];
164876
- if (claim.kind === 'ComponentClaim' && claim.providers.github.overrides) {
164877
- overrides.push(new GithubRepositoryOverrider());
164878
- }
164879
- return overrides;
164880
- }
164881
- /**
164882
- * Load all normalizers and stores them into a list.
164883
- * @param claim
164884
- * @returns
164885
- */
164886
- async function loadNormalizers(_claim, path) {
164887
- const result = [];
164888
- for (const normalizer of NORMALIZERS) {
164889
- result.push(new normalizer({ path: path }));
164890
- }
164891
- return result;
164892
- }
164893
- /*
164894
- * Loads all applicable initializers for a given claim and stores them into a list.
164895
- *
164896
- * Input:
164897
- * - claim: our custom claim object, with which we check if the initializer is applicable
164898
- *
164899
- * Returns:
164900
- * - An Initializer array promise, containing all the initializers loaded for this claim
164901
- *
164902
- */
164903
- let loadedInitializers = false;
164904
- async function loadInitializers(claim, claimPath, initializersPath = getPath('initializers')) {
164905
- const result = [];
164906
- await crawl(initializersPath, (entry) => {
164907
- return isYamlFile.test(entry);
164908
- }, async (entry, data) => {
164909
- const initializerData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
164910
- const ext = external_path_.extname(entry);
164911
- const name = external_path_.basename(entry, ext);
164912
- if (name in INITIALIZERS_BY_FILE_NAME) {
164913
- const initializerBfN = new INITIALIZERS_BY_FILE_NAME[name](initializerData);
164914
- if (!(await initializerBfN.validate(SCHEMAS_BY_INITIALIZER_NAME[name] || {}))) {
164915
- throw new Error(`Invalid ${name} initializer file`);
164916
- }
164917
- if (initializerData.kind === claim.kind) {
164918
- result.push(initializerBfN);
164919
- }
164920
- else {
164921
- logger.debug(`Skipping initializer ${name} because it is not applicable to ${claim.kind}`);
164922
- }
164923
- }
164924
- });
164925
- for (const initializer of INITIALIZERS) {
164926
- if (initializer.applicableKinds.includes(claim.kind)) {
164927
- result.push(new initializer({ path: claimPath }));
164928
- }
164929
- }
164930
- logger.info(`Loaded initializers ${JSON.stringify(result)}`);
164931
- loadedInitializers = result;
164932
- return loadedInitializers;
164933
- }
164934
- function loadClaimDefaults() {
164935
- try {
164936
- return catalog_common/* default.io.fromYaml */.Z.io.fromYaml(external_fs_.readFileSync(`${getPath('claimsDefaults')}/claims_defaults.yaml`, 'utf-8'));
164937
- }
164938
- catch (error) {
164939
- const errMsg = `Could not read claims defaults file in path ${getPath('claimsDefaults')}/claims_defaults.yaml: ${error}`;
164940
- logger.error(errMsg);
164941
- throw new Error(errMsg);
164942
- }
164943
- }
164944
- /*
164945
- * Using fast-json-patch, compares a claim to its kind's defaults, then patches it.
164946
- *
164947
- * Input:
164948
- * - claim: our custom claim object, which we want to patch
164949
- * - defaultsClaims: an object, containing the defaults for each claim kind,
164950
- * with the same structure that we use for the actual claims
164951
- *
164952
- * Returns:
164953
- * - The patched claim
164954
- *
164955
- */
164956
- function loader_patchClaim(claim, defaultsClaims) {
164957
- if (defaultsClaims[claim.kind]) {
164958
- claim = applyBlockAwareDefaults(claim, defaultsClaims[claim.kind]);
164959
- }
164960
- return claim;
164961
- }
164962
- async function loadCRsWithFullSet(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
164963
- const result = {};
164964
- const fullResult = {};
164965
- await crawlWithExclusions(crsPath, (entry) => {
164966
- return isYamlFile.test(entry);
164967
- }, async (entry, data) => {
164968
- const yamlData = stripKnownRelations(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data));
164969
- const metadata = yamlData?.metadata;
164970
- if (!(yamlData &&
164971
- typeof yamlData === 'object' &&
164972
- 'kind' in yamlData &&
164973
- metadata &&
164974
- typeof metadata === 'object' &&
164975
- 'name' in metadata)) {
164976
- logger.warn(`Invalid CR file ${entry}`);
164977
- }
164978
- else {
164979
- const crKey = `${yamlData.kind}-${metadata.name}`;
164980
- if (fullResult[crKey]) {
164981
- logger.error(`Duplicate CR ${crKey}`);
164982
- }
164983
- // Keep the complete CR set for validations that need repository-wide context.
164984
- fullResult[crKey] = yamlData;
164985
- // Keep the filtered CR set for previous-CR lookup during claim rendering.
164986
- if (allowedClaimReferences.length === 0 ||
164987
- allowedClaimReferences.includes(yamlData.metadata?.annotations?.[catalog_common/* default.generic.getFirestartrAnnotation */.Z.generic.getFirestartrAnnotation('claim-ref')])) {
164988
- result[crKey] = yamlData;
164989
- }
164990
- }
164991
- }, excludedPaths.concat(getAdditionalPaths()));
164992
- return { crs: result, fullCrs: fullResult };
164993
- }
164994
- async function loadCRs(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
164995
- const { crs } = await loadCRsWithFullSet(crsPath, excludedPaths, allowedClaimReferences);
164996
- return crs;
164997
- }
164998
- /*
164999
- * Description: Get the renamed using claimRef annotation
165000
- * Input: claims: any, crs: any
165001
- * Output: renames: IRenameResult[]
165002
- */
165003
- async function loadRenames(claims, crs) {
165004
- /*
165005
- * If checkRenames is false, we don't need to check for renames
165006
- */
165007
- const renamesEnabled = getRenamesEnabled();
165008
- if (!renamesEnabled)
165009
- return [];
165010
- const result = [];
165011
- /**
165012
- * For each CR, look for a claim that does not match the name with CR's firestartr.dev/claim-ref annotation
165013
- */
165014
- for (const cr of Object.values(crs)) {
165015
- const anyCr = cr;
165016
- if (!anyCr?.metadata?.annotations)
165017
- continue;
165018
- // if the annotation is a claimRef
165019
- const annotationName = common.generic.getFirestartrAnnotation('claim-ref');
165020
- const annotations = anyCr?.metadata?.annotations || {};
165021
- if (annotationName in annotations) {
165022
- const claimRef = anyCr.metadata.annotations[annotationName];
165023
- // split the annotation into kind and name
165024
- const [kind, name] = claimRef.split('/');
165025
- // if the claim exists, check if the name is different for the given provider
165026
- const referencedClaim = claims[`${kind}-${name}`].claim;
165027
- const oldName = anyCr.metadata.name;
165028
- const newName = referencedClaim.providers[getConfiguredProvider()].name;
165029
- if (oldName !== newName) {
165030
- result.push({
165031
- kind: referencedClaim.kind,
165032
- claimPath: claims[`${kind}-${name}`].claimPath,
165033
- claimName: referencedClaim.name,
165034
- oldName,
165035
- newName,
165036
- });
165037
- }
165038
- }
165039
- }
165040
- return result;
165041
- }
165042
- async function loadClaimsList(claimRefList, claimsPath = getPath('claims')) {
165043
- const data = {
165044
- renderClaims: {},
165045
- crs: {},
165046
- fullCrs: {},
165047
- };
165048
- const defaults = loadClaimDefaults();
165049
- for await (const claimRef of claimRefList) {
165050
- const [renderedClaimData] = await loadClaim(claimRef, getOrg(), defaults, loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, claimsPath);
165051
- data.renderClaims = lodash_default().merge(data.renderClaims, renderedClaimData);
165052
- }
165053
- const crClaimReferences = [];
165054
- for (const ref of Object.keys(data.renderClaims)) {
165055
- // Replaces only the first instance of '-'
165056
- crClaimReferences.push(ref.replace('-', '/'));
165057
- }
165058
- const loadedCrs = await loadCRsWithFullSet(getPath('crs'), [getPath('globals'), getPath('initializers')], crClaimReferences);
165059
- data['crs'] = loadedCrs.crs;
165060
- data['fullCrs'] = loadedCrs.fullCrs;
165061
- return data;
165062
- }
165063
- function getOrg() {
165064
- const groupOrg = catalog_common/* default.environment.getFromEnvironmentWithDefault */.Z.environment.getFromEnvironmentWithDefault(catalog_common/* default.types.envVars.org */.Z.types.envVars.org, '');
165065
- return groupOrg;
165066
- }
165067
-
165068
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/references.ts
165069
- function validateTfStateKeyUniqueness(crs) {
165070
- const tfStateKeys = {};
165071
- for (const crKey of Object.keys(crs)) {
165072
- const cr = crs[crKey];
165073
- if (!isTerraformWorkspace(cr))
165074
- continue;
165075
- const tfKey = cr.spec.firestartr.tfStateKey;
165076
- if (tfStateKeys[tfKey]) {
165077
- throw new Error(`❗❗ ❌ RENDER ERROR ❌ ❗❗
165078
-
165079
- The tfStateKey ${tfKey} is not unique,
165080
-
165081
- It is being used by the following CRs:
165082
-
165083
- - ${tfStateKeys[tfKey].kind} ${tfStateKeys[tfKey].metadata.name}
165084
-
165085
- - ${cr.kind} ${cr.metadata.name}
165086
-
165087
- Check the following Claims to ensure that the tfStateKey is unique:
165088
-
165089
- - ${tfStateKeys[tfKey].metadata.annotations['firestartr.dev/claim-ref']}
165090
-
165091
- - ${cr.metadata.annotations['firestartr.dev/claim-ref']}
165092
- `);
165093
- }
165094
- tfStateKeys[cr.spec.firestartr.tfStateKey] = cr;
165095
- }
165096
- }
165097
- function references_isCatalogEntity(cr) {
165098
- return [
165099
- 'Component',
165100
- 'Domain',
165101
- 'System',
165102
- 'User',
165103
- 'Group',
165104
- 'Resource',
165105
- ].includes(cr.kind);
165106
- }
165107
- function isTerraformWorkspace(cr) {
165108
- return cr.kind === 'FirestartrTerraformWorkspace';
165109
- }
165110
-
165111
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/crSize.ts
165112
-
165113
- const K8S_OBJECT_SIZE_LIMIT = 1572864; // 1.5 MiB in bytes, etcd recommended limit: https://etcd.io/docs/latest/dev-guide/limit/
165114
- function validateCrSizes(crs) {
165115
- for (const key of Object.keys(crs)) {
165116
- const cr = crs[key];
165117
- const serialized = catalog_common/* default.io.toYaml */.Z.io.toYaml(cr);
165118
- const size = Buffer.byteLength(serialized, 'utf8');
165119
- if (size > K8S_OBJECT_SIZE_LIMIT) {
165120
- throw new Error(`CR "${cr.kind}-${cr.metadata.name}" exceeds the Kubernetes object size limit by ${size - K8S_OBJECT_SIZE_LIMIT} bytes. Maximum allowed is ${K8S_OBJECT_SIZE_LIMIT} bytes (1.5MiB).`);
165121
- }
165122
- }
165123
- }
165124
-
165125
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/permissions.ts
165126
-
165127
- function validatePermissionsUniqueness(crs) {
165128
- for (const crkey in crs) {
165129
- const cr = crs[crkey];
165130
- if (cr.kind === 'FirestartrGithubRepository') {
165131
- const permissions = cr.spec.permissions;
165132
- logger.debug(`Validating Permissions Uniqueness of ${crkey}`);
165133
- if (permissions) {
165134
- const rolesByIdentifier = new Map();
165135
- for (const perm of permissions) {
165136
- const identifier = perm.collaborator
165137
- ? perm.collaborator
165138
- : `ref:${perm.ref.kind}:${perm.ref.name}`;
165139
- const role = perm.role;
165140
- const prevRole = rolesByIdentifier.get(identifier);
165141
- if (prevRole === undefined) {
165142
- rolesByIdentifier.set(identifier, role);
165143
- }
165144
- else if (prevRole !== role) {
165145
- throw new Error(`Conflicting permission role in FirestartrGithubRepository ${crkey}: ${identifier} has roles "${prevRole}" and "${role}".\nFull CR: ${JSON.stringify(cr, null, 2)}`);
165146
- }
165147
- }
165148
- }
165149
- }
165150
- }
165151
- }
165152
-
165153
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/backstageAnnotations.ts
165154
- const backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX = 'firestartr.backstage.dev/';
165155
- const CATALOG_KINDS = new Set([
165156
- 'User',
165157
- 'Group',
165158
- 'Component',
165159
- 'System',
165160
- 'Domain',
165161
- 'API',
165162
- 'Resource',
165163
- 'CatalogKubernetesResource',
165164
- 'CatalogTerraformWorkspace',
165165
- 'CatalogArgoDeploy',
165166
- 'CatalogOrgWebhook',
165167
- 'CatalogSecrets',
165168
- ]);
165169
- function validateNoBackstageAnnotationsInK8sCrs(crs) {
165170
- for (const crKey in crs) {
165171
- const cr = crs[crKey];
165172
- if (CATALOG_KINDS.has(cr.kind))
165173
- continue;
165174
- if (cr.metadata?.annotations) {
165175
- const backstageKeys = Object.keys(cr.metadata.annotations).filter((k) => k.startsWith(backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX));
165176
- if (backstageKeys.length > 0) {
165177
- throw new Error(`K8s CR ${crKey} contains Backstage-only annotations (${backstageKeys.join(', ')}). ` +
165178
- `Annotations with prefix ${backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX} must only appear on Backstage catalog entities, not on Kubernetes CRs.`);
165179
- }
165180
- }
165181
- }
165182
- }
165183
-
165184
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/refsSorter/refsSorter.ts
165185
-
165186
- /**
165187
- * @param renderClaims renderClaims to sort
165188
- * @param kinds kinds to get from renderClaims
165189
- * @returns renderClaims with only the kinds specified
165190
- */
165191
- function sortRenderClaimsByKind(renderClaims, kinds) {
165192
- let result = {};
165193
- for (const kind of kinds) {
165194
- for (const key in renderClaims) {
165195
- if (renderClaims[key].claim.kind === kind) {
165196
- result[key] = renderClaims[key];
165197
- }
165198
- }
165199
- result = sortSelfKindRefs(result, kind);
165200
- }
165201
- return result;
165202
- }
165203
- function sortSelfKindRefs(renderClaims, kind) {
165204
- if (!hasSelfRefs(kind)) {
165205
- return renderClaims;
165206
- }
165207
- const newRenderClaims = {};
165208
- const data = extractRefs(renderClaims, kind);
165209
- const ordered = [];
165210
- const visiting = [];
165211
- const visited = [];
165212
- for (const key of Object.keys(data)) {
165213
- if (!visited.find((obj) => obj.name === key)) {
165214
- sortRefs(data, data[key], ordered, visiting, visited);
165215
- }
165216
- }
165217
- for (const cr of ordered) {
165218
- newRenderClaims[cr.name] = renderClaims[cr.name];
165219
- }
165220
- return newRenderClaims;
165221
- }
165222
- function sortRefs(data, claim, ordered = [], visiting = [], visited = []) {
165223
- if (visiting.find((obj) => obj.name === claim.name)) {
165224
- throw new Error(`Circular dependency detected for '${claim.name}'`);
165225
- }
165226
- if (!visited.find((obj) => obj.name === claim.name)) {
165227
- visiting.push(claim);
165228
- if (claim.refs) {
165229
- for (const ref of claim.refs) {
165230
- const referencedObject = data[ref];
165231
- if (!referencedObject) {
165232
- throw new Error(`Reference '${ref}' does not exist for '${claim.name}'`);
165233
- }
165234
- sortRefs(data, referencedObject, ordered, visiting, visited);
165235
- }
165236
- }
165237
- visited.push(visiting.pop());
165238
- ordered.push(claim);
165239
- }
165240
- }
165241
- function hasSelfRefs(kind) {
165242
- return ['TFWorkspaceClaim', 'GroupClaim'].includes(kind);
165243
- }
165244
-
165245
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/renderer/claims-sorter.ts
165246
-
165247
- function sortKinds(orderedKinds, claims) {
165248
- const result = [];
165249
- for (const kind of orderedKinds) {
165250
- const kindRenderClaims = sortRenderClaimsByKind(claims, [kind]);
165251
- result.push(kindRenderClaims);
165252
- }
165253
- return result;
165254
- }
165255
-
165256
164491
  ;// CONCATENATED MODULE: ../features_renderer/src/common.ts
165257
164492
 
165258
164493
  function isDirectory(path) {
@@ -165292,6 +164527,16 @@ var ajv_default = /*#__PURE__*/__nccwpck_require__.n(dist_ajv);
165292
164527
  $ref: '#/definitions/File',
165293
164528
  },
165294
164529
  },
164530
+ claimPatches: {
164531
+ type: 'array',
164532
+ items: {
164533
+ $ref: '#/definitions/Patch',
164534
+ },
164535
+ },
164536
+ // Legacy field kept for backward compatibility. Real feature packages
164537
+ // (prefapp/features) ship `patches` in the provider-keyed object
164538
+ // shape. The renderer ignores it for claim-level behaviour; claim-level
164539
+ // patches must come from `claimPatches`.
165295
164540
  patches: {
165296
164541
  type: 'object',
165297
164542
  patternProperties: {
@@ -165323,7 +164568,7 @@ var ajv_default = /*#__PURE__*/__nccwpck_require__.n(dist_ajv);
165323
164568
  },
165324
164569
  },
165325
164570
  },
165326
- required: ['args', 'feature_name', 'files', 'patches'],
164571
+ required: ['args', 'feature_name', 'files'],
165327
164572
  title: 'root',
165328
164573
  },
165329
164574
  Args: {
@@ -165403,7 +164648,7 @@ var ajv_default = /*#__PURE__*/__nccwpck_require__.n(dist_ajv);
165403
164648
  type: 'string',
165404
164649
  },
165405
164650
  value: {
165406
- type: 'string',
164651
+ type: ['string', 'object', 'array', 'number', 'boolean', 'null'],
165407
164652
  },
165408
164653
  },
165409
164654
  required: ['name', 'op', 'path'],
@@ -165428,10 +164673,10 @@ function validate(featurePath) {
165428
164673
  }
165429
164674
  function __validateDirStructure(featurePath) {
165430
164675
  if (!isDirectory(featurePath)) {
165431
- throw `Feature: ${featurePath} not a directory`;
164676
+ throw new Error(`Feature: ${featurePath} not a directory`);
165432
164677
  }
165433
164678
  if (!isFile(external_path_.join(featurePath, 'config.yaml'))) {
165434
- throw `Feature: ${featurePath}/config.yaml not found or not a file`;
164679
+ throw new Error(`Feature: ${featurePath}/config.yaml not found or not a file`);
165435
164680
  }
165436
164681
  }
165437
164682
  function __validateFeatureConfig(featurePath) {
@@ -165441,10 +164686,16 @@ function __validateFeatureConfig(featurePath) {
165441
164686
  return configData;
165442
164687
  }
165443
164688
  catch (error) {
165444
- throw `Feature: loading config.yaml: ${error}`;
164689
+ throw new Error(`Feature: loading config.yaml: ${error}`);
165445
164690
  }
165446
164691
  }
165447
164692
  function __validateFeatureConfigData(configData) {
164693
+ // Detect old provider-keyed claimPatches shape for migration hint.
164694
+ if (configData.claimPatches &&
164695
+ !Array.isArray(configData.claimPatches) &&
164696
+ typeof configData.claimPatches === 'object') {
164697
+ throw new Error(`Feature: config.yaml invalid claimPatches: expected flat array of claimPatches (claim-relative pointers like /annotations/backstage.io~1techdocs-ref), but got provider-keyed object ${JSON.stringify(Object.keys(configData.claimPatches))}. Migrate: claimPatches: { catalog: [...] } → claimPatches: [{ op, path: /annotations/backstage.io~1techdocs-ref, value }]. $ref paths also migrate: [spec, org] → [providers, github, org], [metadata, annotations, firestartr.dev/external-name] → [providers, github, name]`);
164698
+ }
165448
164699
  const ajv = new (ajv_default())({
165449
164700
  allowUnionTypes: true,
165450
164701
  });
@@ -165455,7 +164706,7 @@ function __validateFeatureConfigData(configData) {
165455
164706
  const validate = ajv.compile(activeSchema);
165456
164707
  const valid = validate(configData);
165457
164708
  if (!valid) {
165458
- throw `Feature: config.yaml invalid ${JSON.stringify(validate.errors)}`;
164709
+ throw new Error(`Feature: config.yaml invalid ${JSON.stringify(validate.errors)}`);
165459
164710
  }
165460
164711
  }
165461
164712
  function stripAnchoringConstraints(schemaDef) {
@@ -165466,12 +164717,26 @@ function stripAnchoringConstraints(schemaDef) {
165466
164717
  return clone;
165467
164718
  }
165468
164719
  function __validateJsonPatches(configData) {
165469
- const keys = Object.keys(configData.patches);
165470
- for (const key of keys) {
165471
- if (configData.patches[key]) {
165472
- if (fast_json_patch_default().validate(configData.patches[key])) {
165473
- const errors = JSON.stringify(fast_json_patch_default().validate(configData.patches[key]).message);
165474
- throw `Feature: config.yaml -> patches: invalid patches ${errors}`;
164720
+ const patches = configData.claimPatches;
164721
+ if (!Array.isArray(patches))
164722
+ return;
164723
+ const err = fast_json_patch_default().validate(patches);
164724
+ if (err) {
164725
+ throw new Error(`Feature: config.yaml -> claimPatches: invalid claimPatches ${JSON.stringify(err.message ?? err)}`);
164726
+ }
164727
+ // Legacy provider-keyed `patches` field is accepted for backward
164728
+ // compatibility. Its values are per-provider arrays of the same Patch
164729
+ // shape, so validate each of them too.
164730
+ const legacyPatches = configData.patches;
164731
+ if (legacyPatches && typeof legacyPatches === 'object') {
164732
+ for (const key of Object.keys(legacyPatches)) {
164733
+ const arr = legacyPatches[key];
164734
+ if (!Array.isArray(arr)) {
164735
+ throw new Error(`Feature: config.yaml -> patches.${key}: expected an array of patches, got ${typeof arr}`);
164736
+ }
164737
+ const legacyErr = fast_json_patch_default().validate(arr);
164738
+ if (legacyErr) {
164739
+ throw new Error(`Feature: config.yaml -> patches.${key}: invalid patch ${JSON.stringify(legacyErr.message ?? legacyErr)}`);
165475
164740
  }
165476
164741
  }
165477
164742
  }
@@ -166259,19 +165524,12 @@ function addTraceability(context, src, content) {
166259
165524
  }
166260
165525
  function addTraceabilityStamp(context, content) {
166261
165526
  const traceability = context.traceability || {};
166262
- const stampLines = [
166263
- '---',
166264
- `# FEATURE_NAME: ${traceability.name}`,
166265
- `# FEATURE_VERSION: ${traceability.version}`,
166266
- `# FEATURE_URL: ${traceability.url}`,
166267
- ];
166268
- if (traceability.sha) {
166269
- stampLines.push(`# FEATURE_GIT_SHA: "${traceability.sha}"`);
166270
- }
166271
- if (Array.isArray(traceability.tags) && traceability.tags.length > 0) {
166272
- stampLines.push(`# FEATURE_GIT_TAGS: ${JSON.stringify(traceability.tags)}`);
166273
- }
166274
- const stamp = `${stampLines.join('\n')}\n\n`;
165527
+ const stamp = `---
165528
+ # FEATURE_NAME: ${traceability.name}
165529
+ # FEATURE_VERSION: ${traceability.version}
165530
+ # FEATURE_URL: ${traceability.url}
165531
+
165532
+ `;
166275
165533
  const output = [];
166276
165534
  let replaced = false;
166277
165535
  for (const line of content.split(/\n/)) {
@@ -166318,10 +165576,12 @@ const renderTestsSchema = {
166318
165576
  items: {
166319
165577
  type: 'object',
166320
165578
  additionalProperties: false,
166321
- required: ['name', 'cr'],
165579
+ required: ['name'],
165580
+ anyOf: [{ required: ['cr'] }, { required: ['claim'] }],
166322
165581
  properties: {
166323
165582
  name: { type: 'string', minLength: 1 },
166324
165583
  cr: { type: 'string', minLength: 1 },
165584
+ claim: { type: 'string', minLength: 1 },
166325
165585
  args: { type: 'object' },
166326
165586
  },
166327
165587
  },
@@ -166376,6 +165636,9 @@ function resolveCrPath(featurePath, crRelPath) {
166376
165636
  }
166377
165637
  return resolved;
166378
165638
  }
165639
+ function resolveClaimPath(featurePath, claimRelPath) {
165640
+ return resolveCrPath(featurePath, claimRelPath);
165641
+ }
166379
165642
  function listYamlFiles(dir) {
166380
165643
  if (!fs.existsSync(dir))
166381
165644
  return [];
@@ -166439,8 +165702,7 @@ function buildExpectedOutput(config, renderDir) {
166439
165702
  }));
166440
165703
  return {
166441
165704
  files,
166442
- patches: config.patches || [],
166443
- traceability: {},
165705
+ claimPatches: config.claimPatches || [],
166444
165706
  };
166445
165707
  }
166446
165708
  /* ---------- Context-style API for a render temp dir ---------- */
@@ -166494,6 +165756,7 @@ async function createRenderContext(prefix = 'feature-render-') {
166494
165756
  buildExpectedOutput,
166495
165757
  loadAndValidateRenderTests,
166496
165758
  resolveCrPath,
165759
+ resolveClaimPath,
166497
165760
  });
166498
165761
 
166499
165762
  ;// CONCATENATED MODULE: ../features_renderer/src/featureArgsValidation.ts
@@ -166586,7 +165849,7 @@ function render(featurePath, featureRenderPath, entity, firestartrConfig = {}, f
166586
165849
  resolveInside(featureRenderPath, dest, 'file dest', 'render target directory');
166587
165850
  }
166588
165851
  }
166589
- const output = { files: [] };
165852
+ const output = { files: [], claimPatches: [] };
166590
165853
  context.config.files.forEach((file) => {
166591
165854
  const { src, dest } = file;
166592
165855
  // For now let's keep upgradable flag for backward compatibility
@@ -166607,30 +165870,124 @@ function render(featurePath, featureRenderPath, entity, firestartrConfig = {}, f
166607
165870
  userManaged: userManaged,
166608
165871
  targetBranch,
166609
165872
  });
166610
- output.patches = context.config.patches;
165873
+ // claimPatches are applied to the claim during claim stitching. Present
165874
+ // in the output even when empty. The legacy `patches` field is deliberately
165875
+ // not copied: it is accepted in config.yaml for backward compatibility but
165876
+ // ignored for claim-level behaviour.
165877
+ output.claimPatches = context.config.claimPatches || [];
166611
165878
  // Not used anymore
166612
165879
  // output.unpatches = context.config.unpatches
166613
165880
  });
166614
- output.traceability = context.traceability;
166615
165881
  external_fs_default().writeFileSync(external_path_default().join(featureRenderPath, 'config.json'), JSON.stringify(context.config));
166616
165882
  // write the output file
166617
165883
  const configFile = external_path_default().join(featureRenderPath, 'output.json');
166618
165884
  external_fs_default().writeFileSync(configFile, JSON.stringify(output));
166619
165885
  return output;
166620
165886
  }
166621
- function buildContext(entity, args, firestartrConfig, featureArgs) {
165887
+ function getClaimPatches(featurePath, entity, firestartrConfig = {}, featureArgs = {}) {
165888
+ const configData = validate(featurePath);
165889
+ validateFeatureArgs(featurePath, featureArgs);
165890
+ const context = buildContext(entity, configData.args, firestartrConfig, featureArgs);
165891
+ const rendered = JSON.parse(renderContent(JSON.stringify(configData), context));
165892
+ return rendered.claimPatches ?? [];
165893
+ }
165894
+ function buildContext(stitchedClaim, args, firestartrConfig, featureArgs) {
166622
165895
  const context = {};
165896
+ const resolveRef = (doc, ref) => {
165897
+ const v = lodash_default().get(doc, ref);
165898
+ if (v !== undefined)
165899
+ return v;
165900
+ // Fallback for transition: try CR-shaped alternatives when stitchedClaim is CR-shaped
165901
+ // and vice versa. Covers the ADR 0006 table plus legacy feature_a provisioner paths.
165902
+ const altTable = {
165903
+ 'providers,github,org': [
165904
+ ['spec', 'org'],
165905
+ ['spec', 'provisioner', 'org'],
165906
+ ],
165907
+ 'providers,github,branchStrategy,defaultBranch': [
165908
+ ['spec', 'repo', 'defaultBranch'],
165909
+ ['spec', 'provisioner', 'repo', 'defaultBranch'],
165910
+ ],
165911
+ 'providers,github,name': [
165912
+ ['metadata', 'annotations', 'firestartr.dev/external-name'],
165913
+ ['metadata', 'name'],
165914
+ ],
165915
+ 'providers,github,technology,stack': [
165916
+ ['spec', 'firestartr', 'technology', 'stack'],
165917
+ ['spec', 'provisioner', 'technology', 'stack'],
165918
+ ],
165919
+ name: [['metadata', 'name']],
165920
+ 'annotations,firestartr.dev/dest-annotation': [
165921
+ ['metadata', 'annotations', 'firestartr.dev/dest-annotation'],
165922
+ ],
165923
+ };
165924
+ const key = ref.join(',');
165925
+ const alts = altTable[key];
165926
+ if (alts) {
165927
+ for (const alt of alts) {
165928
+ const av = lodash_default().get(doc, alt);
165929
+ if (av !== undefined)
165930
+ return av;
165931
+ }
165932
+ }
165933
+ // Reverse fallback: if ref is CR-shaped, try claim-shaped
165934
+ const reverseTable = {
165935
+ 'spec,org': ['providers', 'github', 'org'],
165936
+ 'spec,provisioner,technology,stack': [
165937
+ 'providers',
165938
+ 'github',
165939
+ 'technology',
165940
+ 'stack',
165941
+ ],
165942
+ 'spec,firestartr,technology,stack': [
165943
+ 'providers',
165944
+ 'github',
165945
+ 'technology',
165946
+ 'stack',
165947
+ ],
165948
+ 'metadata,name': ['name'],
165949
+ 'metadata,annotations,firestartr.dev/dest-annotation': [
165950
+ 'annotations',
165951
+ 'firestartr.dev/dest-annotation',
165952
+ ],
165953
+ 'spec,provisioner,org': ['providers', 'github', 'org'],
165954
+ 'spec,provisioner,repo,defaultBranch': [
165955
+ 'providers',
165956
+ 'github',
165957
+ 'branchStrategy',
165958
+ 'defaultBranch',
165959
+ ],
165960
+ 'spec,repo,defaultBranch': [
165961
+ 'providers',
165962
+ 'github',
165963
+ 'branchStrategy',
165964
+ 'defaultBranch',
165965
+ ],
165966
+ 'metadata,annotations,firestartr.dev/external-name': [
165967
+ 'providers',
165968
+ 'github',
165969
+ 'name',
165970
+ ],
165971
+ };
165972
+ const rev = reverseTable[ref.join(',')];
165973
+ if (rev) {
165974
+ const rv = lodash_default().get(doc, rev);
165975
+ if (rv !== undefined)
165976
+ return rv;
165977
+ }
165978
+ return v;
165979
+ };
166623
165980
  Object.entries(args).forEach(([key, arg]) => {
166624
165981
  if ('$default' in arg) {
166625
165982
  if (Array.isArray(arg['$default'])) {
166626
- context[key] = lodash_default().get(entity, arg['$default']);
165983
+ context[key] = resolveRef(stitchedClaim, arg['$default']);
166627
165984
  }
166628
165985
  else {
166629
165986
  context[key] = arg['$default'];
166630
165987
  }
166631
165988
  }
166632
165989
  if ('$ref' in arg) {
166633
- const refValue = lodash_default().get(entity, arg['$ref']);
165990
+ const refValue = resolveRef(stitchedClaim, arg['$ref']);
166634
165991
  context[key] = refValue;
166635
165992
  }
166636
165993
  else if ('$lit' in arg) {
@@ -166760,6 +166117,7 @@ function updateFileContent(featureRenderPath, filePath, content) {
166760
166117
 
166761
166118
 
166762
166119
 
166120
+
166763
166121
  /* harmony default export */ const features_renderer = ({
166764
166122
  validate: validate,
166765
166123
  render: render,
@@ -166769,6 +166127,7 @@ function updateFileContent(featureRenderPath, filePath, content) {
166769
166127
  renderContent: renderContent,
166770
166128
  expandFiles: expandFiles,
166771
166129
  validateFeatureArgs: validateFeatureArgs,
166130
+ getClaimPatches: getClaimPatches,
166772
166131
  });
166773
166132
 
166774
166133
  ;// CONCATENATED MODULE: ../features_preparer/src/logger.ts
@@ -166793,6 +166152,13 @@ function renderFeature(featureName, version, owner, repo, featureOwner, renderPa
166793
166152
  function renderFeatureFromPath(extractPath, renderedPath, featureOwner, featureArgs = {}) {
166794
166153
  return features_renderer.render(extractPath, renderedPath, featureOwner, {}, featureArgs);
166795
166154
  }
166155
+ function getFeatureClaimPatches(featureName, version, owner, repo, featureOwner, featureArgs = {}) {
166156
+ const extractPath = external_path_default().join(catalog_common/* default.features.tarballs.getFeaturesExtractPath */.Z.features.tarballs.getFeaturesExtractPath(featureName, version, owner, repo), 'packages', featureName);
166157
+ return getClaimPatches(extractPath, featureOwner, {}, featureArgs);
166158
+ }
166159
+ function getFeatureClaimPatchesFromPath(extractPath, featureOwner, featureArgs = {}) {
166160
+ return getClaimPatches(extractPath, featureOwner, {}, featureArgs);
166161
+ }
166796
166162
 
166797
166163
  // EXTERNAL MODULE: ../../node_modules/adm-zip/adm-zip.js
166798
166164
  var adm_zip = __nccwpck_require__(7993);
@@ -166843,7 +166209,6 @@ featureArgs = {}, repo = 'features', owner = 'prefapp') {
166843
166209
  async function processFeature(featureName, reference, featureOwner, // -> cr
166844
166210
  featureArgs = {}, repo = 'features', owner = 'prefapp') {
166845
166211
  await downloadFeatureZip(repo, featureName, reference, owner);
166846
- const commitInfo = await resolveFeatureCommit(owner, repo, reference);
166847
166212
  featureArgs['traceability'] = {
166848
166213
  version: reference,
166849
166214
  owner,
@@ -166851,43 +166216,28 @@ featureArgs = {}, repo = 'features', owner = 'prefapp') {
166851
166216
  name: featureName,
166852
166217
  ref: reference,
166853
166218
  url: `https://github.com/${owner}/${repo}/tree/${reference}/packages/${featureName}`,
166854
- sha: commitInfo.sha,
166855
- tags: commitInfo.tags,
166856
166219
  };
166857
166220
  return renderFeature(featureName, reference, owner, repo, featureOwner, '/tmp', featureArgs);
166858
166221
  }
166859
- async function resolveFeatureCommit(owner, repo, reference) {
166860
- try {
166861
- const octokit = await github["default"].getOctokitForOrg(owner, true);
166862
- // Get the resolved commit SHA
166863
- const { data: commit } = await octokit.rest.repos.getCommit({
166864
- owner,
166865
- repo,
166866
- ref: reference,
166867
- });
166868
- const sha = commit.sha;
166869
- // Get all tags pointing to this commit. `paginate` is all-or-nothing:
166870
- // if listing fails, we still return the resolved SHA with an empty tag set.
166871
- let tags = [];
166872
- try {
166873
- const allTags = await octokit.paginate(octokit.rest.repos.listTags, {
166874
- owner,
166875
- repo,
166876
- per_page: 100,
166877
- });
166878
- tags = allTags
166879
- .filter((t) => t.commit?.sha === sha)
166880
- .map((t) => t.name);
166881
- }
166882
- catch (tagError) {
166883
- features_preparer_src_logger.warn(`[features_preparer] Failed to list tags for ${owner}/${repo}@${reference}: ${tagError instanceof Error ? tagError.message : String(tagError)}`);
166884
- }
166885
- return { sha, tags };
166886
- }
166887
- catch (error) {
166888
- features_preparer_src_logger.warn(`[features_preparer] Failed to resolve commit for ${owner}/${repo}@${reference}: ${error instanceof Error ? error.message : String(error)}`);
166889
- return { sha: '', tags: [] };
166890
- }
166222
+ async function installer_getFeatureClaimPatches(featureName, version, featureOwner, featureArgs = {}, repo = 'features', owner = 'prefapp') {
166223
+ const reference = `${featureName}-v${version}`;
166224
+ return processFeatureClaimPatches(featureName, reference, featureOwner, featureArgs, repo, owner);
166225
+ }
166226
+ async function getFeatureClaimPatchesFromRef(featureName, featureRef, featureOwner, featureArgs = {}, repo = 'features', owner = 'prefapp') {
166227
+ const reference = `${featureRef}`;
166228
+ return processFeatureClaimPatches(featureName, reference, featureOwner, featureArgs, repo, owner);
166229
+ }
166230
+ async function processFeatureClaimPatches(featureName, reference, featureOwner, featureArgs = {}, repo = 'features', owner = 'prefapp') {
166231
+ await downloadFeatureZip(repo, featureName, reference, owner);
166232
+ featureArgs['traceability'] = {
166233
+ version: reference,
166234
+ owner,
166235
+ repo,
166236
+ name: featureName,
166237
+ ref: reference,
166238
+ url: `https://github.com/${owner}/${repo}/tree/${reference}/packages/${featureName}`,
166239
+ };
166240
+ return getFeatureClaimPatches(featureName, reference, owner, repo, featureOwner, featureArgs);
166891
166241
  }
166892
166242
  async function prepareFeature(featureName, version, repo = 'features', owner = 'prefapp') {
166893
166243
  await downloadFeatureZip(repo, featureName, version, owner);
@@ -166943,6 +166293,9 @@ async function downloadFeatureZip(repo, featureName, reference, owner = 'prefapp
166943
166293
  /* harmony default export */ const src = ({
166944
166294
  renderFeature: renderFeature,
166945
166295
  renderFeatureFromPath: renderFeatureFromPath,
166296
+ getFeatureClaimPatches: installer_getFeatureClaimPatches,
166297
+ getFeatureClaimPatchesFromRef: getFeatureClaimPatchesFromRef,
166298
+ getFeatureClaimPatchesFromPath: getFeatureClaimPatchesFromPath,
166946
166299
  getFeatureConfigFromRef: getFeatureConfigFromRef,
166947
166300
  getFeatureConfig: getFeatureConfig,
166948
166301
  prepareFeature: prepareFeature,
@@ -166959,6 +166312,1138 @@ async function downloadFeatureZip(repo, featureName, reference, owner = 'prefapp
166959
166312
  getFeatureConfigFromRef: src.getFeatureConfigFromRef,
166960
166313
  });
166961
166314
 
166315
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/stitching/protectedPaths.ts
166316
+ // Platform-curated protected claim paths — features may not write to these.
166317
+ // Stored as JSON Pointers (RFC 6901). Supports wildcard `*` for a single segment.
166318
+ // Example: /providers/STAR/org matches /providers/github/org where STAR is *.
166319
+ const PROTECTED_CLAIM_PATHS = [
166320
+ '/kind',
166321
+ '/name',
166322
+ '/providers/*/org',
166323
+ '/providers/github/org',
166324
+ '/providers/github/name',
166325
+ '/metadata/name',
166326
+ '/metadata/namespace',
166327
+ ];
166328
+ function isProtectedPath(patchPath) {
166329
+ const normalizedPatch = normalizePointer(patchPath);
166330
+ for (const protectedPath of PROTECTED_CLAIM_PATHS) {
166331
+ if (matchesProtected(protectedPath, normalizedPatch)) {
166332
+ return true;
166333
+ }
166334
+ }
166335
+ return false;
166336
+ }
166337
+ function normalizePointer(pointer) {
166338
+ if (!pointer.startsWith('/')) {
166339
+ return '/' + pointer;
166340
+ }
166341
+ return pointer;
166342
+ }
166343
+ function matchesProtected(protectedPointer, patchPointer) {
166344
+ const protectedSegments = protectedPointer
166345
+ .split('/')
166346
+ .slice(1)
166347
+ .map(unescapePointer);
166348
+ const patchSegments = patchPointer.split('/').slice(1).map(unescapePointer);
166349
+ // Treat ancestor/descendant as protected: a patch to a parent (e.g. /metadata)
166350
+ // would replace the protected child (/metadata/name), and a patch to a child
166351
+ // (e.g. /metadata/name/extra) is also under the protected tree. Match when
166352
+ // the shorter prefix equals the longer prefix (with wildcard support).
166353
+ const minLen = Math.min(protectedSegments.length, patchSegments.length);
166354
+ for (let i = 0; i < minLen; i++) {
166355
+ if (protectedSegments[i] === '*')
166356
+ continue;
166357
+ if (protectedSegments[i] !== patchSegments[i])
166358
+ return false;
166359
+ }
166360
+ return true;
166361
+ }
166362
+ function unescapePointer(segment) {
166363
+ return segment.replace(/~1/g, '/').replace(/~0/g, '~');
166364
+ }
166365
+
166366
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/stitching/errors.ts
166367
+ class StitchingError extends Error {
166368
+ constructor(violations) {
166369
+ const lines = violations.map((v) => ` - ${v.message}`).join('\n');
166370
+ super(`Claim stitching failed with ${violations.length} violation(s):\n${lines}`);
166371
+ this.name = 'StitchingError';
166372
+ this.violations = violations;
166373
+ }
166374
+ }
166375
+ function formatStitchingError(error) {
166376
+ return error.message;
166377
+ }
166378
+
166379
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/stitching/stitching.ts
166380
+
166381
+
166382
+
166383
+
166384
+ let MOCK_FEATURES_FN;
166385
+ function MOCK_STITCHING_FEATURES(mock) {
166386
+ MOCK_FEATURES_FN = mock;
166387
+ }
166388
+ function pointerExists(doc, pointer) {
166389
+ const segments = pointer
166390
+ .split('/')
166391
+ .slice(1)
166392
+ .map((s) => s.replace(/~1/g, '/').replace(/~0/g, '~'));
166393
+ let cur = doc;
166394
+ for (const seg of segments) {
166395
+ if (cur === null || cur === undefined || typeof cur !== 'object')
166396
+ return false;
166397
+ if (!Object.prototype.hasOwnProperty.call(cur, seg))
166398
+ return false;
166399
+ cur = cur[seg];
166400
+ }
166401
+ return true;
166402
+ }
166403
+ function isAppendPointer(pointer) {
166404
+ return pointer.endsWith('/-');
166405
+ }
166406
+ function isRecord(value) {
166407
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
166408
+ }
166409
+ // Deduplication identity for a feature patch. Ordinary writes collide on the
166410
+ // normalized pointer. Array appends (`add ... /-`) collide on the pointer plus
166411
+ // the appended element's `name` when present, for example so several features can each add
166412
+ // a distinct label to /providers/github/labels/- while the same label name from
166413
+ // two features is silently deduplicated (or errors if values differ).
166414
+ function writeIdentity(patch) {
166415
+ const path = normalizePointer(patch.path);
166416
+ if (patch.op === 'add' &&
166417
+ isAppendPointer(path) &&
166418
+ isRecord(patch.value) &&
166419
+ typeof patch.value.name === 'string') {
166420
+ return `${path}#${patch.value.name}`;
166421
+ }
166422
+ return path;
166423
+ }
166424
+ // Check whether an array at `appendPath`'s parent already contains an element
166425
+ // whose `name` matches `value.name`. Used for the "claim wins" gate: when a
166426
+ // feature tries to append an array element that already exists in the claim,
166427
+ // the patch is silently dropped.
166428
+ function arrayElementExists(doc, appendPath, value) {
166429
+ if (!isRecord(value) || typeof value.name !== 'string')
166430
+ return false;
166431
+ const parentPath = appendPath.slice(0, -2); // strip trailing /-
166432
+ const segments = parentPath
166433
+ .split('/')
166434
+ .slice(1)
166435
+ .map((s) => s.replace(/~1/g, '/').replace(/~0/g, '~'));
166436
+ let cur = doc;
166437
+ for (const seg of segments) {
166438
+ if (cur === null || cur === undefined || typeof cur !== 'object')
166439
+ return false;
166440
+ if (!Object.prototype.hasOwnProperty.call(cur, seg))
166441
+ return false;
166442
+ cur = cur[seg];
166443
+ }
166444
+ if (!Array.isArray(cur))
166445
+ return false;
166446
+ return cur.some((el) => isRecord(el) && el.name === value.name);
166447
+ }
166448
+ // fast-json-patch does not create intermediate containers for `add`, and
166449
+ // appending to an array requires the array to already exist. Ensure every
166450
+ // ancestor segment exists before applying an `add`: missing containers are
166451
+ // created as objects, except the immediate parent of a `/-` append, which is
166452
+ // created as an array. Existing (user/default-declared) values are never
166453
+ // overwritten.
166454
+ function ensureAddParents(doc, patch) {
166455
+ const segments = patch.path.split('/').slice(1);
166456
+ let prefix = '';
166457
+ for (let i = 0; i < segments.length - 1; i++) {
166458
+ prefix += '/' + segments[i];
166459
+ if (pointerExists(doc, prefix))
166460
+ continue;
166461
+ const isArrayParent = isAppendPointer(patch.path) && i === segments.length - 2;
166462
+ const value = isArrayParent ? [] : {};
166463
+ try {
166464
+ doc = fast_json_patch.applyPatch(doc, [{ op: 'add', path: prefix, value }], false, false).newDocument;
166465
+ }
166466
+ catch {
166467
+ // Concurrent structural conflict — let the actual patch surface the error.
166468
+ }
166469
+ }
166470
+ return doc;
166471
+ }
166472
+ async function stitchClaim(claimAfterDefaults, preCollectedEnvelopes, originalClaim) {
166473
+ const envelopes = preCollectedEnvelopes ?? (await collectFeaturePatches(claimAfterDefaults));
166474
+ if (envelopes.length === 0) {
166475
+ return claimAfterDefaults;
166476
+ }
166477
+ // Normalize all patches once so every gate and the apply phase use the same pointers.
166478
+ const normalizedEnvelopes = envelopes.map((env) => ({
166479
+ feature: env.feature,
166480
+ patches: env.patches.map((p) => ({
166481
+ ...p,
166482
+ path: normalizePointer(p.path),
166483
+ _raw: p.path,
166484
+ })),
166485
+ }));
166486
+ // ── Phase 1: silently drop patches that should not be applied ──────────
166487
+ // • Array append whose element already exists in the claim → claim wins
166488
+ // • Duplicate identity with identical value across features → silent dedup
166489
+ const dropped = new Set(); // "envelopeIdx:patchIdx"
166490
+ const seenIdentities = new Map();
166491
+ for (let ei = 0; ei < normalizedEnvelopes.length; ei++) {
166492
+ const envelope = normalizedEnvelopes[ei];
166493
+ for (let pi = 0; pi < envelope.patches.length; pi++) {
166494
+ const patch = envelope.patches[pi];
166495
+ const key = `${ei}:${pi}`;
166496
+ // Array claim-wins: element already in claim → drop
166497
+ if (patch.op === 'add' && isAppendPointer(patch.path)) {
166498
+ if (arrayElementExists(claimAfterDefaults, patch.path, patch.value)) {
166499
+ dropped.add(key);
166500
+ continue;
166501
+ }
166502
+ }
166503
+ // Silent dedup: same identity + same value → drop
166504
+ const identity = writeIdentity(patch);
166505
+ const existing = seenIdentities.get(identity);
166506
+ if (existing) {
166507
+ if (JSON.stringify(existing.value) === JSON.stringify(patch.value)) {
166508
+ dropped.add(key);
166509
+ continue;
166510
+ }
166511
+ // Different value — kept here, caught as a violation in Phase 2.
166512
+ }
166513
+ else {
166514
+ seenIdentities.set(identity, {
166515
+ feature: envelope.feature,
166516
+ value: patch.value,
166517
+ });
166518
+ }
166519
+ }
166520
+ }
166521
+ // ── Phase 2: collect violations on non-dropped patches ─────────────────
166522
+ const violations = [];
166523
+ const seenForViolations = new Map(); // identity → feature
166524
+ for (let ei = 0; ei < normalizedEnvelopes.length; ei++) {
166525
+ const envelope = normalizedEnvelopes[ei];
166526
+ for (let pi = 0; pi < envelope.patches.length; pi++) {
166527
+ if (dropped.has(`${ei}:${pi}`))
166528
+ continue;
166529
+ const patch = envelope.patches[pi];
166530
+ const path = patch.path;
166531
+ const raw = patch._raw ?? path;
166532
+ // Gate 1: protected path
166533
+ if (isProtectedPath(path)) {
166534
+ violations.push({
166535
+ feature: envelope.feature,
166536
+ op: patch.op,
166537
+ path,
166538
+ reason: 'protected-path',
166539
+ message: `Feature '${envelope.feature}' patch ${patch.op} ${raw} (normalized ${path}) targets protected claim path`,
166540
+ });
166541
+ }
166542
+ // Gate 2: conflicting value from another feature
166543
+ const identity = writeIdentity(patch);
166544
+ if (seenForViolations.has(identity)) {
166545
+ violations.push({
166546
+ feature: envelope.feature,
166547
+ op: patch.op,
166548
+ path,
166549
+ reason: 'duplicate-path',
166550
+ conflictingFeature: seenForViolations.get(identity),
166551
+ message: `Feature '${envelope.feature}' and '${seenForViolations.get(identity)}' both write to ${path} (deduplication)`,
166552
+ });
166553
+ }
166554
+ else {
166555
+ seenForViolations.set(identity, envelope.feature);
166556
+ }
166557
+ // Gate 3: replace / remove to an absent path
166558
+ if (['replace', 'remove'].includes(patch.op)) {
166559
+ if (!pointerExists(claimAfterDefaults, path)) {
166560
+ violations.push({
166561
+ feature: envelope.feature,
166562
+ op: patch.op,
166563
+ path,
166564
+ reason: 'overwrite-protected',
166565
+ message: `Feature '${envelope.feature}' patch ${patch.op} ${raw} (normalized ${path}) targets absent path (replace/remove requires existing value)`,
166566
+ });
166567
+ }
166568
+ }
166569
+ // Gate 4: non-append ops must not overwrite a user-declared field
166570
+ // (overwrite protection). The user's own claim wins over any feature.
166571
+ // Only the patch's target field itself is protected: adding a sibling
166572
+ // field under a user-declared parent (e.g. a new field in
166573
+ // /providers/github) is allowed. Array appends (`add ... /-`) are
166574
+ // exempt: the appended element is deduplicated against the claim in
166575
+ // Phase 1, preserving original values, while distinct elements from
166576
+ // multiple features may be added.
166577
+ if (originalClaim && !isAppendPointer(path)) {
166578
+ if (pointerExists(originalClaim, path)) {
166579
+ violations.push({
166580
+ feature: envelope.feature,
166581
+ op: patch.op,
166582
+ path,
166583
+ reason: 'user-declared-path',
166584
+ message: `Feature '${envelope.feature}' patch ${patch.op} ${raw} (normalized ${path}) targets a field declared by the user (overwrite protection)`,
166585
+ });
166586
+ }
166587
+ }
166588
+ }
166589
+ }
166590
+ if (violations.length > 0) {
166591
+ throw new StitchingError(violations);
166592
+ }
166593
+ // ── Phase 3: apply remaining patches ───────────────────────────────────
166594
+ let stitched = JSON.parse(JSON.stringify(claimAfterDefaults));
166595
+ for (let ei = 0; ei < normalizedEnvelopes.length; ei++) {
166596
+ const envelope = normalizedEnvelopes[ei];
166597
+ const patchesToApply = envelope.patches.filter((_, pi) => !dropped.has(`${ei}:${pi}`));
166598
+ if (patchesToApply.length === 0)
166599
+ continue;
166600
+ for (const patch of patchesToApply) {
166601
+ if (patch.op === 'add') {
166602
+ stitched = ensureAddParents(stitched, patch);
166603
+ }
166604
+ }
166605
+ const result = fast_json_patch.applyPatch(stitched, patchesToApply, false, false);
166606
+ stitched = result.newDocument;
166607
+ }
166608
+ return stitched;
166609
+ }
166610
+ async function collectFeaturePatches(claim) {
166611
+ if (MOCK_FEATURES_FN) {
166612
+ return MOCK_FEATURES_FN(claim);
166613
+ }
166614
+ const typedClaim = claim;
166615
+ const features = typedClaim.providers?.github?.features ?? [];
166616
+ if (features.length === 0)
166617
+ return [];
166618
+ // In unit tests without GitHub credentials, avoid real download — tests that care
166619
+ // about stitching should set MOCK_STITCHING_FEATURES explicitly.
166620
+ if (process.env.JEST_WORKER_ID && !process.env.GITHUB_TOKEN) {
166621
+ return [];
166622
+ }
166623
+ const envelopes = [];
166624
+ for (const feature of features) {
166625
+ const featureName = feature.name;
166626
+ const versionOrRef = feature.ref ?? feature.version ?? '';
166627
+ const repo = feature.repo
166628
+ ? (feature.repo.split('/')[1] ?? 'features')
166629
+ : 'features';
166630
+ const owner = feature.repo
166631
+ ? (feature.repo.split('/')[0] ?? 'prefapp')
166632
+ : 'prefapp';
166633
+ const args = feature.args ?? {};
166634
+ try {
166635
+ let patchesOrRendered;
166636
+ const preparerAny = features_preparer;
166637
+ if (feature.ref) {
166638
+ if (typeof preparerAny.getFeatureClaimPatchesFromRef === 'function') {
166639
+ patchesOrRendered = await preparerAny.getFeatureClaimPatchesFromRef(featureName, versionOrRef, claim, args, repo, owner);
166640
+ }
166641
+ else {
166642
+ patchesOrRendered = await preparerAny.getFeatureConfigFromRef(featureName, versionOrRef, claim, args, repo, owner);
166643
+ }
166644
+ }
166645
+ else {
166646
+ if (typeof preparerAny.getFeatureClaimPatches === 'function') {
166647
+ patchesOrRendered = await preparerAny.getFeatureClaimPatches(featureName, versionOrRef, claim, args, repo, owner);
166648
+ }
166649
+ else {
166650
+ patchesOrRendered = await preparerAny.getFeatureConfig(featureName, versionOrRef, claim, args, repo, owner);
166651
+ }
166652
+ }
166653
+ const patches = Array.isArray(patchesOrRendered)
166654
+ ? patchesOrRendered
166655
+ : extractClaimPatches(patchesOrRendered);
166656
+ envelopes.push({ feature: featureName, patches });
166657
+ }
166658
+ catch (err) {
166659
+ throw new Error(`Failed to collect claimPatches for feature '${featureName}': ${err instanceof Error ? err.message : String(err)}`);
166660
+ }
166661
+ }
166662
+ return envelopes;
166663
+ }
166664
+ function extractClaimPatches(rendered) {
166665
+ const patches = rendered.claimPatches;
166666
+ if (!patches)
166667
+ return [];
166668
+ if (Array.isArray(patches))
166669
+ return patches;
166670
+ throw new Error(`Feature claimPatches must be a flat array (claim-relative pointers like /annotations/...), got ${typeof patches}: ${JSON.stringify(patches).slice(0, 200)}`);
166671
+ }
166672
+
166673
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/lazy_loader.ts
166674
+
166675
+
166676
+
166677
+
166678
+
166679
+
166680
+
166681
+
166682
+
166683
+
166684
+
166685
+
166686
+
166687
+
166688
+
166689
+
166690
+
166691
+ // We need to ensure the grep command is a gnu version
166692
+ let IS_GNU_GREP = false;
166693
+ let GREP_ON_CHECK = false;
166694
+ // this loop is for avoid race conditions
166695
+ // once a check is running no more checks
166696
+ async function waitForCheck() {
166697
+ const f_wait = () => new Promise((timeOut) => setTimeout(timeOut, 10));
166698
+ do {
166699
+ await f_wait();
166700
+ } while (GREP_ON_CHECK);
166701
+ }
166702
+ function checkGrep() {
166703
+ return new Promise((ok, ko) => {
166704
+ // idempotency: already checked
166705
+ if (IS_GNU_GREP)
166706
+ return ok();
166707
+ // a check is already running no new checks
166708
+ if (GREP_ON_CHECK)
166709
+ return waitForCheck();
166710
+ logger.info('Checking the grep command');
166711
+ GREP_ON_CHECK = true;
166712
+ const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['--version']);
166713
+ handler.on('close', (code) => {
166714
+ GREP_ON_CHECK = false;
166715
+ if (code === 0) {
166716
+ logger.debug('Grep is a gnu grep');
166717
+ IS_GNU_GREP = true;
166718
+ ok();
166719
+ }
166720
+ else {
166721
+ ko('Lazy evaluation needs a gnu-grep command');
166722
+ }
166723
+ });
166724
+ });
166725
+ }
166726
+ async function loadClaim(claimRef, org, defaults = loadClaimDefaults(), patchClaim = loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, existingRefs = {}, postValidations = new Map()) {
166727
+ await checkGrep();
166728
+ let result = existingRefs;
166729
+ logger.info(`Load reference ${claimRef}`);
166730
+ initVirtualClaims(org);
166731
+ logger.info(`Load reference (parts) ${claimRef.split(/-/)[0]} ${claimRef.replace(/^[^-]+-/, '')}`);
166732
+ // cargas datos con grep
166733
+ try {
166734
+ const claimData = await lazyGetClaim(claimRef.split(/-/)[0], claimRef.replace(/^[^-]+-/, ''), org, cwd);
166735
+ const rawClaim = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(claimData);
166736
+ let claim = patchClaim(rawClaim, defaults);
166737
+ // Claim stitching: feature claim-patches with gates before AJV
166738
+ try {
166739
+ claim = await stitchClaim(claim, undefined, rawClaim);
166740
+ }
166741
+ catch (e) {
166742
+ throw new Error(`Error when stitching claim ${claimRef}: ${e instanceof Error ? e.message : String(e)}`);
166743
+ }
166744
+ let variants = [];
166745
+ if (claim.kind === 'TFWorkspaceClaim' &&
166746
+ claim.providers?.terraform?.variants) {
166747
+ variants = claim.providers.terraform.variants;
166748
+ delete claim.providers.terraform.variants;
166749
+ }
166750
+ logger.silly(`Patched claim is:
166751
+ ---
166752
+ ${catalog_common/* default.io.toYaml */.Z.io.toYaml(claim)}`);
166753
+ try {
166754
+ validateClaim(claim, base[`${claim.kind}Schema`]);
166755
+ await optionalValidation(claim);
166756
+ }
166757
+ catch (error) {
166758
+ let errorMsg = '';
166759
+ if (Array.isArray(error)) {
166760
+ for (const data of error) {
166761
+ errorMsg = `${errorMsg}
166762
+ - ${data.instancePath}: ${data.message}`;
166763
+ }
166764
+ }
166765
+ else {
166766
+ errorMsg = error instanceof Error ? error.message : String(error);
166767
+ }
166768
+ throw new Error(`Error when validating claim ${claimRef}: ${errorMsg}`);
166769
+ }
166770
+ result[claimRef] = {};
166771
+ result[claimRef]['claim'] = claim;
166772
+ result[claimRef]['claimPath'] = VisitedClaims[claimRef];
166773
+ if (VisitedClaims[claimRef] === 'virtual') {
166774
+ result = await setVirtualClaimAdditionalData(result, claim, claimRef);
166775
+ }
166776
+ else {
166777
+ result = await setNonVirtualClaimAdditionalData(result, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
166778
+ }
166779
+ const claimKind = claim.kind;
166780
+ const references = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(claim));
166781
+ for (const ref of references) {
166782
+ if (!result[ref]) {
166783
+ const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
166784
+ result = lodash_default().merge(result, resolvedReferences);
166785
+ }
166786
+ }
166787
+ if (variants.length > 0) {
166788
+ const parentClaimPath = VisitedClaims[claimRef];
166789
+ for (const variant of variants) {
166790
+ if (!variant.name || !variant.overrides) {
166791
+ throw new Error(`Variant in claim ${claimRef} is missing required field 'name' or 'overrides'`);
166792
+ }
166793
+ const prohibitedOverrideFields = ['source', 'module', 'name'];
166794
+ for (const field of prohibitedOverrideFields) {
166795
+ if (field in variant.overrides) {
166796
+ throw new Error(`Variant '${variant.name}' in claim ${claimRef} cannot override '${field}'`);
166797
+ }
166798
+ }
166799
+ const composedName = claim.providers.terraform.name + '-' + variant.name;
166800
+ const variantClaim = lodash_default().cloneDeep(claim);
166801
+ variantClaim._parentClaimName = claim.name;
166802
+ variantClaim.name = composedName;
166803
+ variantClaim.providers.terraform = lodash_default().merge({}, claim.providers.terraform, variant.overrides);
166804
+ variantClaim.providers.terraform.name = composedName;
166805
+ if (!variantClaim.annotations) {
166806
+ variantClaim.annotations = {};
166807
+ }
166808
+ variantClaim.annotations['firestartr.dev/variant-of'] =
166809
+ claim.providers.terraform.name;
166810
+ const variantRef = `TFWorkspaceClaim-${composedName}`;
166811
+ if (result[variantRef]) {
166812
+ throw new Error(`Variant name '${variant.name}' (composed: '${composedName}') from claim '${claimRef}' conflicts with existing claim '${variantRef}'`);
166813
+ }
166814
+ logger.info(`Creating synthetic variant claim ${variantRef}`);
166815
+ result[variantRef] = {};
166816
+ result[variantRef]['claim'] = variantClaim;
166817
+ result[variantRef]['claimPath'] = parentClaimPath;
166818
+ result = await setNonVirtualClaimAdditionalData(result, variantClaim, variantRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
166819
+ const variantReferences = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(variantClaim));
166820
+ for (const ref of variantReferences) {
166821
+ if (!result[ref]) {
166822
+ const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
166823
+ result = lodash_default().merge(result, resolvedReferences);
166824
+ }
166825
+ }
166826
+ }
166827
+ }
166828
+ }
166829
+ catch (err) {
166830
+ throw `Lazy Loading: ${err}`;
166831
+ }
166832
+ return [result, postValidations];
166833
+ }
166834
+ let LoadedClaims = {};
166835
+ let VisitedClaims = {};
166836
+ let DuplicatedClaims = {};
166837
+ function resetLazyLoader() {
166838
+ LoadedClaims = {};
166839
+ VisitedClaims = {};
166840
+ DuplicatedClaims = {};
166841
+ }
166842
+ async function lazyGetClaim(kind, name, org, cwd) {
166843
+ const indice = `${kind}-${name}`;
166844
+ logger.info(`Lazy loading ${kind}-${name} with index ${indice}`);
166845
+ if (indice in LoadedClaims)
166846
+ return LoadedClaims[indice];
166847
+ if (isVirtualClaim(kind, name)) {
166848
+ await loadVirtualClaim(kind, name, org);
166849
+ }
166850
+ else {
166851
+ await getClaimsByName(name, cwd);
166852
+ }
166853
+ if (indice in LoadedClaims) {
166854
+ return LoadedClaims[indice];
166855
+ }
166856
+ else {
166857
+ throw new Error(`Error: ${kind}-${name} not found`);
166858
+ }
166859
+ }
166860
+ async function getClaimsByName(name, cwd = '.') {
166861
+ return new Promise((ok, ko) => {
166862
+ const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['-r', '-l', '--include=*', '-E', `name: "?${name}"?`, '.'], {
166863
+ cwd: cwd,
166864
+ });
166865
+ logger.info(`Running ${['grep', '-r', '-l', '--include', '-E', 'name: "' + name + '"?', '.'].join(' ')}`);
166866
+ const entradas = [];
166867
+ const chunks = [];
166868
+ handler.stdout.on('data', (data) => {
166869
+ chunks.push(data.toString('utf-8'));
166870
+ });
166871
+ handler.on('close', () => {
166872
+ const dataFinal = chunks.join('');
166873
+ const fileNameList = dataFinal.split(/\n/).filter((line) => line !== '');
166874
+ for (const fileName of fileNameList) {
166875
+ // only yaml files
166876
+ if (!fileName.match(/\.yml$|\.yaml$/)) {
166877
+ continue;
166878
+ }
166879
+ // only entries not already visited
166880
+ if (Object.values(VisitedClaims).includes(fileName)) {
166881
+ continue;
166882
+ }
166883
+ entradas.push(external_path_.join(cwd, fileName));
166884
+ }
166885
+ return Promise.all(entradas.map((entrada) => {
166886
+ return loadRawClaim(entrada);
166887
+ }))
166888
+ .then(() => {
166889
+ ok();
166890
+ })
166891
+ .catch((err) => {
166892
+ ko(err);
166893
+ });
166894
+ });
166895
+ });
166896
+ }
166897
+ async function loadRawClaim(entry) {
166898
+ return new Promise((ok, ko) => {
166899
+ external_fs_.readFile(entry, 'utf-8', (error, data) => {
166900
+ if (error)
166901
+ return ko(`Reading ${entry}: ${error}`);
166902
+ const claim = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
166903
+ if (!('kind' in claim && 'name' in claim)) {
166904
+ logger.warn(`Invalid claim file ${entry}`);
166905
+ }
166906
+ else {
166907
+ const claimKey = `${claim.kind}-${claim.name}`;
166908
+ if (Object.keys(DuplicatedClaims).includes(claimKey) &&
166909
+ DuplicatedClaims[claimKey] !== entry) {
166910
+ return ko(`Duplicated claim ${claimKey} found files: ${DuplicatedClaims[claimKey]} and ${entry}`);
166911
+ }
166912
+ LoadedClaims[claimKey] = data;
166913
+ VisitedClaims[claimKey] = entry;
166914
+ DuplicatedClaims[claimKey] = entry;
166915
+ ok();
166916
+ }
166917
+ });
166918
+ });
166919
+ }
166920
+ async function setVirtualClaimAdditionalData(renderedData, claim, claimRef) {
166921
+ const virtualClaim = getVirtualClaim(claim.kind, claim.name);
166922
+ renderedData[claimRef]['initializers'] = [
166923
+ new UUIDInitializer(claim),
166924
+ new InitializerClaimRef(),
166925
+ new InitializerDefault(virtualClaim.getDefaultConfig()),
166926
+ ];
166927
+ renderedData[claimRef]['globals'] = [];
166928
+ renderedData[claimRef]['overrides'] = [];
166929
+ renderedData[claimRef]['normalizers'] = [new NameNormalizer()];
166930
+ return renderedData;
166931
+ }
166932
+ async function setNonVirtualClaimAdditionalData(renderedData, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers) {
166933
+ renderedData[claimRef]['initializers'] = await loadInitializers(claim, renderedData[claimRef]['claimPath']);
166934
+ renderedData[claimRef]['globals'] = await loadGlobals(claim);
166935
+ renderedData[claimRef]['overrides'] = loadOverrides(claim);
166936
+ renderedData[claimRef]['normalizers'] = await loadNormalizers(claim, renderedData[claimRef]['claimPath']);
166937
+ return renderedData;
166938
+ }
166939
+ async function loadVirtualClaim(kind, name, org) {
166940
+ const virtualClaim = getVirtualClaim(kind, name);
166941
+ const expandedClaim = await virtualClaim.expand({ org });
166942
+ logger.info(`Loading virtual claim ${kind} ${name} ${org}`);
166943
+ LoadedClaims[`${kind}-${name}`] = catalog_common/* default.io.toYaml */.Z.io.toYaml(expandedClaim);
166944
+ VisitedClaims[`${kind}-${name}`] = 'virtual';
166945
+ }
166946
+
166947
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/claimsDefaulter.ts
166948
+
166949
+
166950
+
166951
+ /*
166952
+ * Default blocks are objects that cannot be merged but applied as a whole. Meaning:
166953
+ * - If the object is defined at the claim-level it is maintained *AS IT IS* (no merge)
166954
+ * - If the block is missing at the claim-level, the default block is applied
166955
+ */
166956
+ const defaultBlocksPaths = ['/providers/terraform/sync'];
166957
+ function applyBlockAwareDefaults(claim, defaultClaim) {
166958
+ // we always work with a clone of the original
166959
+ const claimClone = JSON.parse(JSON.stringify(claim));
166960
+ // if the block is claim-level defined we store it here
166961
+ const defaultBlocks = new Map();
166962
+ // we remove the blocks entirely of the claim
166963
+ // to achieve an add copy
166964
+ for (const defaultBlockPath of defaultBlocksPaths) {
166965
+ if (claimsDefaulter_hasDeepPath(claimClone, defaultBlockPath) !== true)
166966
+ continue;
166967
+ const originalValue = fast_json_patch_default().getValueByPointer(claimClone, defaultBlockPath);
166968
+ if (originalValue) {
166969
+ logger.silly(`${claim.kind}/${claim.name}: has an original ${defaultBlockPath}: preserving`);
166970
+ defaultBlocks.set(defaultBlockPath, originalValue);
166971
+ fast_json_patch_default().applyPatch(claimClone, [{ op: 'remove', path: defaultBlockPath }]);
166972
+ }
166973
+ }
166974
+ // if the patch has the same path of a block
166975
+ // and the block was defined at claim-level
166976
+ // we interchange the value with the original value
166977
+ // otherwise we use the default
166978
+ const jsonPatchOps = fast_json_patch_default().compare(claimClone, defaultClaim)
166979
+ .filter((jp) => jp.op === 'add')
166980
+ .map((jp) => {
166981
+ if (defaultBlocks.has(jp.path)) {
166982
+ return {
166983
+ ...jp,
166984
+ value: defaultBlocks.get(jp.path),
166985
+ };
166986
+ }
166987
+ else {
166988
+ return jp;
166989
+ }
166990
+ });
166991
+ return fast_json_patch_default().applyPatch(claimClone, jsonPatchOps).newDocument;
166992
+ }
166993
+ function claimsDefaulter_hasDeepPath(data, deepPath) {
166994
+ return lodash_default().has(data, deepPath.replace(/\//g, '.').replace(/^\./, ''));
166995
+ }
166996
+
166997
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/loader.ts
166998
+
166999
+
167000
+
167001
+
167002
+
167003
+
167004
+
167005
+
167006
+
167007
+
167008
+
167009
+
167010
+
167011
+
167012
+
167013
+ const isYamlFile = new RegExp(/\.(yaml|yml)$/);
167014
+ /**
167015
+ *
167016
+ * @returns Virtual Claims
167017
+ */
167018
+ const virtualClaims = [new FirestartrAllClaim()];
167019
+ /*
167020
+ * Loads all applicable globals for a given claim and stores them into a list.
167021
+ *
167022
+ * Input:
167023
+ * - claim: our custom claim object, with which we check if the global is applicable
167024
+ *
167025
+ * Returns:
167026
+ * - A GlobalSection array promise, containing all the globals loaded for this claim
167027
+ *
167028
+ */
167029
+ async function loadGlobals(claim) {
167030
+ const result = [];
167031
+ await crawl(getPath('globals'), (entry) => {
167032
+ return isYamlFile.test(entry);
167033
+ }, async (entry, data) => {
167034
+ const globalData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
167035
+ const ext = external_path_.extname(entry);
167036
+ const name = external_path_.basename(entry, ext);
167037
+ if (name in SECTIONS_BY_FILE_NAME) {
167038
+ const section = new SECTIONS_BY_FILE_NAME[name](globalData);
167039
+ if (!(await section.validate(SCHEMAS_BY_SECTION_NAME[name] || {}))) {
167040
+ throw new Error(`Invalid ${name} global file`);
167041
+ }
167042
+ if (globalData.kind === claim.kind) {
167043
+ result.push(section);
167044
+ }
167045
+ else {
167046
+ logger.debug(`Skipping global ${name} because it is not applicable to ${claim.kind}`);
167047
+ }
167048
+ }
167049
+ });
167050
+ logger.info(`Loaded globals ${result}`);
167051
+ return result;
167052
+ }
167053
+ /*
167054
+ * If the input claim is a GithubRepositoryClaim, it loads all it's applicable
167055
+ * overrides and stores them into a list. Otherwise, it returns an empty list.
167056
+ *
167057
+ * Input:
167058
+ * - claim: our custom claim object, which should be a GithubRepositoryClaim
167059
+ *
167060
+ * Returns:
167061
+ * - An Overrider array, with a GithubRepositoryOverrider if the claim
167062
+ * was a GithubRepositoryClaim, or empty if it was not.
167063
+ *
167064
+ */
167065
+ function loadOverrides(claim) {
167066
+ const overrides = [];
167067
+ if (claim.kind === 'ComponentClaim' && claim.providers.github.overrides) {
167068
+ overrides.push(new GithubRepositoryOverrider());
167069
+ }
167070
+ return overrides;
167071
+ }
167072
+ /**
167073
+ * Load all normalizers and stores them into a list.
167074
+ * @param claim
167075
+ * @returns
167076
+ */
167077
+ async function loadNormalizers(_claim, path) {
167078
+ const result = [];
167079
+ for (const normalizer of NORMALIZERS) {
167080
+ result.push(new normalizer({ path: path }));
167081
+ }
167082
+ return result;
167083
+ }
167084
+ /*
167085
+ * Loads all applicable initializers for a given claim and stores them into a list.
167086
+ *
167087
+ * Input:
167088
+ * - claim: our custom claim object, with which we check if the initializer is applicable
167089
+ *
167090
+ * Returns:
167091
+ * - An Initializer array promise, containing all the initializers loaded for this claim
167092
+ *
167093
+ */
167094
+ let loadedInitializers = false;
167095
+ async function loadInitializers(claim, claimPath, initializersPath = getPath('initializers')) {
167096
+ const result = [];
167097
+ await crawl(initializersPath, (entry) => {
167098
+ return isYamlFile.test(entry);
167099
+ }, async (entry, data) => {
167100
+ const initializerData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
167101
+ const ext = external_path_.extname(entry);
167102
+ const name = external_path_.basename(entry, ext);
167103
+ if (name in INITIALIZERS_BY_FILE_NAME) {
167104
+ const initializerBfN = new INITIALIZERS_BY_FILE_NAME[name](initializerData);
167105
+ if (!(await initializerBfN.validate(SCHEMAS_BY_INITIALIZER_NAME[name] || {}))) {
167106
+ throw new Error(`Invalid ${name} initializer file`);
167107
+ }
167108
+ if (initializerData.kind === claim.kind) {
167109
+ result.push(initializerBfN);
167110
+ }
167111
+ else {
167112
+ logger.debug(`Skipping initializer ${name} because it is not applicable to ${claim.kind}`);
167113
+ }
167114
+ }
167115
+ });
167116
+ for (const initializer of INITIALIZERS) {
167117
+ if (initializer.applicableKinds.includes(claim.kind)) {
167118
+ result.push(new initializer({ path: claimPath }));
167119
+ }
167120
+ }
167121
+ logger.info(`Loaded initializers ${JSON.stringify(result)}`);
167122
+ loadedInitializers = result;
167123
+ return loadedInitializers;
167124
+ }
167125
+ function loadClaimDefaults() {
167126
+ try {
167127
+ return catalog_common/* default.io.fromYaml */.Z.io.fromYaml(external_fs_.readFileSync(`${getPath('claimsDefaults')}/claims_defaults.yaml`, 'utf-8'));
167128
+ }
167129
+ catch (error) {
167130
+ const errMsg = `Could not read claims defaults file in path ${getPath('claimsDefaults')}/claims_defaults.yaml: ${error}`;
167131
+ logger.error(errMsg);
167132
+ throw new Error(errMsg);
167133
+ }
167134
+ }
167135
+ /*
167136
+ * Using fast-json-patch, compares a claim to its kind's defaults, then patches it.
167137
+ *
167138
+ * Input:
167139
+ * - claim: our custom claim object, which we want to patch
167140
+ * - defaultsClaims: an object, containing the defaults for each claim kind,
167141
+ * with the same structure that we use for the actual claims
167142
+ *
167143
+ * Returns:
167144
+ * - The patched claim
167145
+ *
167146
+ */
167147
+ function loader_patchClaim(claim, defaultsClaims) {
167148
+ if (defaultsClaims[claim.kind]) {
167149
+ claim = applyBlockAwareDefaults(claim, defaultsClaims[claim.kind]);
167150
+ }
167151
+ return claim;
167152
+ }
167153
+ async function loadCRsWithFullSet(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
167154
+ const result = {};
167155
+ const fullResult = {};
167156
+ await crawlWithExclusions(crsPath, (entry) => {
167157
+ return isYamlFile.test(entry);
167158
+ }, async (entry, data) => {
167159
+ const yamlData = stripKnownRelations(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data));
167160
+ const metadata = yamlData?.metadata;
167161
+ if (!(yamlData &&
167162
+ typeof yamlData === 'object' &&
167163
+ 'kind' in yamlData &&
167164
+ metadata &&
167165
+ typeof metadata === 'object' &&
167166
+ 'name' in metadata)) {
167167
+ logger.warn(`Invalid CR file ${entry}`);
167168
+ }
167169
+ else {
167170
+ const crKey = `${yamlData.kind}-${metadata.name}`;
167171
+ if (fullResult[crKey]) {
167172
+ logger.error(`Duplicate CR ${crKey}`);
167173
+ }
167174
+ // Keep the complete CR set for validations that need repository-wide context.
167175
+ fullResult[crKey] = yamlData;
167176
+ // Keep the filtered CR set for previous-CR lookup during claim rendering.
167177
+ if (allowedClaimReferences.length === 0 ||
167178
+ allowedClaimReferences.includes(yamlData.metadata?.annotations?.[catalog_common/* default.generic.getFirestartrAnnotation */.Z.generic.getFirestartrAnnotation('claim-ref')])) {
167179
+ result[crKey] = yamlData;
167180
+ }
167181
+ }
167182
+ }, excludedPaths.concat(getAdditionalPaths()));
167183
+ return { crs: result, fullCrs: fullResult };
167184
+ }
167185
+ async function loadCRs(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
167186
+ const { crs } = await loadCRsWithFullSet(crsPath, excludedPaths, allowedClaimReferences);
167187
+ return crs;
167188
+ }
167189
+ /*
167190
+ * Description: Get the renamed using claimRef annotation
167191
+ * Input: claims: any, crs: any
167192
+ * Output: renames: IRenameResult[]
167193
+ */
167194
+ async function loadRenames(claims, crs) {
167195
+ /*
167196
+ * If checkRenames is false, we don't need to check for renames
167197
+ */
167198
+ const renamesEnabled = getRenamesEnabled();
167199
+ if (!renamesEnabled)
167200
+ return [];
167201
+ const result = [];
167202
+ /**
167203
+ * For each CR, look for a claim that does not match the name with CR's firestartr.dev/claim-ref annotation
167204
+ */
167205
+ for (const cr of Object.values(crs)) {
167206
+ const anyCr = cr;
167207
+ if (!anyCr?.metadata?.annotations)
167208
+ continue;
167209
+ // if the annotation is a claimRef
167210
+ const annotationName = common.generic.getFirestartrAnnotation('claim-ref');
167211
+ const annotations = anyCr?.metadata?.annotations || {};
167212
+ if (annotationName in annotations) {
167213
+ const claimRef = anyCr.metadata.annotations[annotationName];
167214
+ // split the annotation into kind and name
167215
+ const [kind, name] = claimRef.split('/');
167216
+ // if the claim exists, check if the name is different for the given provider
167217
+ const referencedClaim = claims[`${kind}-${name}`].claim;
167218
+ const oldName = anyCr.metadata.name;
167219
+ const newName = referencedClaim.providers[getConfiguredProvider()].name;
167220
+ if (oldName !== newName) {
167221
+ result.push({
167222
+ kind: referencedClaim.kind,
167223
+ claimPath: claims[`${kind}-${name}`].claimPath,
167224
+ claimName: referencedClaim.name,
167225
+ oldName,
167226
+ newName,
167227
+ });
167228
+ }
167229
+ }
167230
+ }
167231
+ return result;
167232
+ }
167233
+ async function loadClaimsList(claimRefList, claimsPath = getPath('claims')) {
167234
+ const data = {
167235
+ renderClaims: {},
167236
+ crs: {},
167237
+ fullCrs: {},
167238
+ };
167239
+ const defaults = loadClaimDefaults();
167240
+ for await (const claimRef of claimRefList) {
167241
+ const [renderedClaimData] = await loadClaim(claimRef, getOrg(), defaults, loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, claimsPath);
167242
+ data.renderClaims = lodash_default().merge(data.renderClaims, renderedClaimData);
167243
+ }
167244
+ const crClaimReferences = [];
167245
+ for (const ref of Object.keys(data.renderClaims)) {
167246
+ // Replaces only the first instance of '-'
167247
+ crClaimReferences.push(ref.replace('-', '/'));
167248
+ }
167249
+ const loadedCrs = await loadCRsWithFullSet(getPath('crs'), [getPath('globals'), getPath('initializers')], crClaimReferences);
167250
+ data['crs'] = loadedCrs.crs;
167251
+ data['fullCrs'] = loadedCrs.fullCrs;
167252
+ return data;
167253
+ }
167254
+ function getOrg() {
167255
+ const groupOrg = catalog_common/* default.environment.getFromEnvironmentWithDefault */.Z.environment.getFromEnvironmentWithDefault(catalog_common/* default.types.envVars.org */.Z.types.envVars.org, '');
167256
+ return groupOrg;
167257
+ }
167258
+
167259
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/references.ts
167260
+ function validateTfStateKeyUniqueness(crs) {
167261
+ const tfStateKeys = {};
167262
+ for (const crKey of Object.keys(crs)) {
167263
+ const cr = crs[crKey];
167264
+ if (!isTerraformWorkspace(cr))
167265
+ continue;
167266
+ const tfKey = cr.spec.firestartr.tfStateKey;
167267
+ if (tfStateKeys[tfKey]) {
167268
+ throw new Error(`❗❗ ❌ RENDER ERROR ❌ ❗❗
167269
+
167270
+ The tfStateKey ${tfKey} is not unique,
167271
+
167272
+ It is being used by the following CRs:
167273
+
167274
+ - ${tfStateKeys[tfKey].kind} ${tfStateKeys[tfKey].metadata.name}
167275
+
167276
+ - ${cr.kind} ${cr.metadata.name}
167277
+
167278
+ Check the following Claims to ensure that the tfStateKey is unique:
167279
+
167280
+ - ${tfStateKeys[tfKey].metadata.annotations['firestartr.dev/claim-ref']}
167281
+
167282
+ - ${cr.metadata.annotations['firestartr.dev/claim-ref']}
167283
+ `);
167284
+ }
167285
+ tfStateKeys[cr.spec.firestartr.tfStateKey] = cr;
167286
+ }
167287
+ }
167288
+ function references_isCatalogEntity(cr) {
167289
+ return [
167290
+ 'Component',
167291
+ 'Domain',
167292
+ 'System',
167293
+ 'User',
167294
+ 'Group',
167295
+ 'Resource',
167296
+ ].includes(cr.kind);
167297
+ }
167298
+ function isTerraformWorkspace(cr) {
167299
+ return cr.kind === 'FirestartrTerraformWorkspace';
167300
+ }
167301
+
167302
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/crSize.ts
167303
+
167304
+ const K8S_OBJECT_SIZE_LIMIT = 1572864; // 1.5 MiB in bytes, etcd recommended limit: https://etcd.io/docs/latest/dev-guide/limit/
167305
+ function validateCrSizes(crs) {
167306
+ for (const key of Object.keys(crs)) {
167307
+ const cr = crs[key];
167308
+ const serialized = catalog_common/* default.io.toYaml */.Z.io.toYaml(cr);
167309
+ const size = Buffer.byteLength(serialized, 'utf8');
167310
+ if (size > K8S_OBJECT_SIZE_LIMIT) {
167311
+ throw new Error(`CR "${cr.kind}-${cr.metadata.name}" exceeds the Kubernetes object size limit by ${size - K8S_OBJECT_SIZE_LIMIT} bytes. Maximum allowed is ${K8S_OBJECT_SIZE_LIMIT} bytes (1.5MiB).`);
167312
+ }
167313
+ }
167314
+ }
167315
+
167316
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/permissions.ts
167317
+
167318
+ function validatePermissionsUniqueness(crs) {
167319
+ for (const crkey in crs) {
167320
+ const cr = crs[crkey];
167321
+ if (cr.kind === 'FirestartrGithubRepository') {
167322
+ const permissions = cr.spec.permissions;
167323
+ logger.debug(`Validating Permissions Uniqueness of ${crkey}`);
167324
+ if (permissions) {
167325
+ const rolesByIdentifier = new Map();
167326
+ for (const perm of permissions) {
167327
+ const identifier = perm.collaborator
167328
+ ? perm.collaborator
167329
+ : `ref:${perm.ref.kind}:${perm.ref.name}`;
167330
+ const role = perm.role;
167331
+ const prevRole = rolesByIdentifier.get(identifier);
167332
+ if (prevRole === undefined) {
167333
+ rolesByIdentifier.set(identifier, role);
167334
+ }
167335
+ else if (prevRole !== role) {
167336
+ throw new Error(`Conflicting permission role in FirestartrGithubRepository ${crkey}: ${identifier} has roles "${prevRole}" and "${role}".\nFull CR: ${JSON.stringify(cr, null, 2)}`);
167337
+ }
167338
+ }
167339
+ }
167340
+ }
167341
+ }
167342
+ }
167343
+
167344
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/backstageAnnotations.ts
167345
+ const backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX = 'firestartr.backstage.dev/';
167346
+ const CATALOG_KINDS = new Set([
167347
+ 'User',
167348
+ 'Group',
167349
+ 'Component',
167350
+ 'System',
167351
+ 'Domain',
167352
+ 'API',
167353
+ 'Resource',
167354
+ 'CatalogKubernetesResource',
167355
+ 'CatalogTerraformWorkspace',
167356
+ 'CatalogArgoDeploy',
167357
+ 'CatalogOrgWebhook',
167358
+ 'CatalogSecrets',
167359
+ ]);
167360
+ function validateNoBackstageAnnotationsInK8sCrs(crs) {
167361
+ for (const crKey in crs) {
167362
+ const cr = crs[crKey];
167363
+ if (CATALOG_KINDS.has(cr.kind))
167364
+ continue;
167365
+ if (cr.metadata?.annotations) {
167366
+ const backstageKeys = Object.keys(cr.metadata.annotations).filter((k) => k.startsWith(backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX));
167367
+ if (backstageKeys.length > 0) {
167368
+ throw new Error(`K8s CR ${crKey} contains Backstage-only annotations (${backstageKeys.join(', ')}). ` +
167369
+ `Annotations with prefix ${backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX} must only appear on Backstage catalog entities, not on Kubernetes CRs.`);
167370
+ }
167371
+ }
167372
+ }
167373
+ }
167374
+
167375
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/refsSorter/refsSorter.ts
167376
+
167377
+ /**
167378
+ * @param renderClaims renderClaims to sort
167379
+ * @param kinds kinds to get from renderClaims
167380
+ * @returns renderClaims with only the kinds specified
167381
+ */
167382
+ function sortRenderClaimsByKind(renderClaims, kinds) {
167383
+ let result = {};
167384
+ for (const kind of kinds) {
167385
+ for (const key in renderClaims) {
167386
+ if (renderClaims[key].claim.kind === kind) {
167387
+ result[key] = renderClaims[key];
167388
+ }
167389
+ }
167390
+ result = sortSelfKindRefs(result, kind);
167391
+ }
167392
+ return result;
167393
+ }
167394
+ function sortSelfKindRefs(renderClaims, kind) {
167395
+ if (!hasSelfRefs(kind)) {
167396
+ return renderClaims;
167397
+ }
167398
+ const newRenderClaims = {};
167399
+ const data = extractRefs(renderClaims, kind);
167400
+ const ordered = [];
167401
+ const visiting = [];
167402
+ const visited = [];
167403
+ for (const key of Object.keys(data)) {
167404
+ if (!visited.find((obj) => obj.name === key)) {
167405
+ sortRefs(data, data[key], ordered, visiting, visited);
167406
+ }
167407
+ }
167408
+ for (const cr of ordered) {
167409
+ newRenderClaims[cr.name] = renderClaims[cr.name];
167410
+ }
167411
+ return newRenderClaims;
167412
+ }
167413
+ function sortRefs(data, claim, ordered = [], visiting = [], visited = []) {
167414
+ if (visiting.find((obj) => obj.name === claim.name)) {
167415
+ throw new Error(`Circular dependency detected for '${claim.name}'`);
167416
+ }
167417
+ if (!visited.find((obj) => obj.name === claim.name)) {
167418
+ visiting.push(claim);
167419
+ if (claim.refs) {
167420
+ for (const ref of claim.refs) {
167421
+ const referencedObject = data[ref];
167422
+ if (!referencedObject) {
167423
+ throw new Error(`Reference '${ref}' does not exist for '${claim.name}'`);
167424
+ }
167425
+ sortRefs(data, referencedObject, ordered, visiting, visited);
167426
+ }
167427
+ }
167428
+ visited.push(visiting.pop());
167429
+ ordered.push(claim);
167430
+ }
167431
+ }
167432
+ function hasSelfRefs(kind) {
167433
+ return ['TFWorkspaceClaim', 'GroupClaim'].includes(kind);
167434
+ }
167435
+
167436
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/renderer/claims-sorter.ts
167437
+
167438
+ function sortKinds(orderedKinds, claims) {
167439
+ const result = [];
167440
+ for (const kind of orderedKinds) {
167441
+ const kindRenderClaims = sortRenderClaimsByKind(claims, [kind]);
167442
+ result.push(kindRenderClaims);
167443
+ }
167444
+ return result;
167445
+ }
167446
+
166962
167447
  ;// CONCATENATED MODULE: ../cdk8s_renderer/src/overriders/utils.ts
166963
167448
 
166964
167449
 
@@ -166974,20 +167459,11 @@ async function utils_renderFeature(featureName, featureVersion, featureOutputObj
166974
167459
  files.push({ path, userManaged, content, targetBranch });
166975
167460
  }
166976
167461
  const featName = catalog_common/* default.generic.normalizeName */.Z.generic.normalizeName(`${featureName}-${cr.spec.firestartr.tfStateKey}`);
166977
- const traceability = featureOutputObject.traceability || {};
166978
167462
  return {
166979
167463
  kind: 'GithubRepositoryFeatureClaim',
166980
167464
  version: '1.0.0',
166981
167465
  name: featName,
166982
- feature: {
166983
- name: featureName,
166984
- version: featureVersion,
166985
- ref: traceability.ref,
166986
- repo: traceability.repo,
166987
- sha: traceability.sha,
166988
- tags: traceability.tags,
166989
- url: traceability.url,
166990
- },
167466
+ feature: { name: featureName, version: featureVersion },
166991
167467
  firestartr: { tfStateKey: cr.spec.firestartr.tfStateKey },
166992
167468
  files,
166993
167469
  claimRepoName,
@@ -167009,14 +167485,12 @@ async function utils_renderFeature(featureName, featureVersion, featureOutputObj
167009
167485
 
167010
167486
 
167011
167487
 
167012
-
167013
-
167014
167488
  // this is the only way currently to make
167015
167489
  // work the system and inject our code
167016
167490
  // without having to download a feature
167017
- let MOCK_FEATURES_FN;
167491
+ let featureOverride_MOCK_FEATURES_FN;
167018
167492
  function MOCK_FEATURES(mock) {
167019
- MOCK_FEATURES_FN = mock;
167493
+ featureOverride_MOCK_FEATURES_FN = mock;
167020
167494
  if (mock) {
167021
167495
  log.warn('⚠️ Warning features system downloader has been mocked, it should only happen in unit testing');
167022
167496
  }
@@ -167047,8 +167521,8 @@ class FeaturesOverrider extends OverriderPatches {
167047
167521
  let owner = 'prefapp';
167048
167522
  let versionOrRef = this.featureVersion;
167049
167523
  let renderer = 'getFeatureConfig';
167050
- if (MOCK_FEATURES_FN) {
167051
- return MOCK_FEATURES_FN();
167524
+ if (featureOverride_MOCK_FEATURES_FN) {
167525
+ return featureOverride_MOCK_FEATURES_FN();
167052
167526
  }
167053
167527
  if (this.featureRepo) {
167054
167528
  [owner, repo] = this.featureRepo.split(/\//);
@@ -167066,8 +167540,8 @@ class FeaturesOverrider extends OverriderPatches {
167066
167540
  const featureName = this.featureName;
167067
167541
  const featureVersion = this.featureVersion || this.featureRef;
167068
167542
  const featureArgs = this.featureArgs;
167069
- const fSetUpAndRunRenderer = function (cr) {
167070
- return this.setUpAndRunRenderer(cr);
167543
+ const fSetUpAndRunRenderer = function (target) {
167544
+ return this.setUpAndRunRenderer(target);
167071
167545
  }.bind(this);
167072
167546
  let renderedFeatureConfig = null;
167073
167547
  return [
@@ -167076,7 +167550,7 @@ class FeaturesOverrider extends OverriderPatches {
167076
167550
  return true;
167077
167551
  },
167078
167552
  async apply(cr) {
167079
- renderedFeatureConfig = await fSetUpAndRunRenderer(cr);
167553
+ renderedFeatureConfig = await fSetUpAndRunRenderer(claim);
167080
167554
  return cr;
167081
167555
  },
167082
167556
  identify() {
@@ -167089,28 +167563,6 @@ class FeaturesOverrider extends OverriderPatches {
167089
167563
  return utils_renderFeature(featureName, featureVersion, renderedFeatureConfig, cr, claim.name);
167090
167564
  },
167091
167565
  },
167092
- {
167093
- isPostPatch: true,
167094
- validate() {
167095
- return true;
167096
- },
167097
- async apply(cr) {
167098
- if (!renderedFeatureConfig) {
167099
- logger.warn(`Feature ${featureName}: renderedFeatureConfig is null, skipping post-patch`);
167100
- return cr;
167101
- }
167102
- const provider = helperCTX(this).provider;
167103
- const jsonPatches = renderedFeatureConfig.patches?.[provider] || [];
167104
- fast_json_patch_default().applyPatch(cr, jsonPatches);
167105
- return cr;
167106
- },
167107
- identify() {
167108
- return `feature-json-patch/${featureName}`;
167109
- },
167110
- applicable() {
167111
- return { applicableProviders: ['catalog', 'github'] };
167112
- },
167113
- },
167114
167566
  ];
167115
167567
  }
167116
167568
  }
@@ -170854,29 +171306,13 @@ class FeatureRepoChart extends BaseGithubChart {
170854
171306
  template() {
170855
171307
  const claim = this.get('claim');
170856
171308
  const repositoryTarget = this.resolveRepositoryTarget();
170857
- const annotations = {
170858
- 'firestartr.dev/external-name': claim.name,
170859
- 'firestartr.dev/feature-name': claim.feature.name,
170860
- };
170861
- if (claim.feature.sha) {
170862
- annotations['firestartr.dev/feature-git-sha'] = claim.feature.sha;
170863
- }
170864
- if (claim.feature.tags?.length) {
170865
- annotations['firestartr.dev/feature-git-tags'] = JSON.stringify(claim.feature.tags);
170866
- }
170867
- if (claim.feature.url) {
170868
- annotations['firestartr.dev/feature-url'] = claim.feature.url;
170869
- }
170870
- if (claim.feature.ref) {
170871
- annotations['firestartr.dev/feature-ref'] = claim.feature.ref;
170872
- }
170873
- if (claim.feature.repo) {
170874
- annotations['firestartr.dev/feature-repo'] = claim.feature.repo;
170875
- }
170876
171309
  const cr = {
170877
171310
  metadata: {
170878
171311
  name: claim.name,
170879
- annotations,
171312
+ annotations: {
171313
+ 'firestartr.dev/external-name': claim.name,
171314
+ 'firestartr.dev/feature-name': claim.feature.name,
171315
+ },
170880
171316
  },
170881
171317
  spec: {
170882
171318
  context: claim.context,
@@ -170896,7 +171332,7 @@ class FeatureRepoChart extends BaseGithubChart {
170896
171332
  },
170897
171333
  },
170898
171334
  };
170899
- const repoAnnotations = this.getAnnotationsFromRepo(this.get('repoCr'), [
171335
+ const annotations = this.getAnnotationsFromRepo(this.get('repoCr'), [
170900
171336
  'claim-ref',
170901
171337
  'revision',
170902
171338
  'sync-enabled',
@@ -170904,7 +171340,7 @@ class FeatureRepoChart extends BaseGithubChart {
170904
171340
  ]);
170905
171341
  cr.metadata.annotations = {
170906
171342
  ...cr.metadata.annotations,
170907
- ...repoAnnotations,
171343
+ ...annotations,
170908
171344
  };
170909
171345
  // We do this to remove undefined variables from the template
170910
171346
  const crTemplate = JSON.parse(JSON.stringify(cr, null, 2));
@@ -363578,7 +364014,7 @@ function helperCreateCheckRunName(cmd, item) {
363578
364014
 
363579
364015
  ;// CONCATENATED MODULE: ../operator/src/utils/index.ts
363580
364016
  const secretRegex = /\$\{\{ secrets\.(.*?) \}\}/g;
363581
- function isRecord(value) {
364017
+ function utils_isRecord(value) {
363582
364018
  return typeof value === 'object' && value !== null;
363583
364019
  }
363584
364020
  function getFirstNonEmptyString(...values) {
@@ -363616,7 +364052,7 @@ function replaceInlineSecrets(inline, secrets) {
363616
364052
  return result;
363617
364053
  }
363618
364054
  function extractErrorDetails(error) {
363619
- const exitCode = isRecord(error) && typeof error.exitCode === 'number'
364055
+ const exitCode = utils_isRecord(error) && typeof error.exitCode === 'number'
363620
364056
  ? error.exitCode
363621
364057
  : undefined;
363622
364058
  if (typeof error === 'string') {
@@ -363627,7 +364063,7 @@ function extractErrorDetails(error) {
363627
364063
  const output = getFirstNonEmptyString(errorWithOutput.output, error.message);
363628
364064
  return { output: output ?? String(error), exitCode };
363629
364065
  }
363630
- if (isRecord(error)) {
364066
+ if (utils_isRecord(error)) {
363631
364067
  const output = getFirstNonEmptyString(error.output, error.message, error.error);
363632
364068
  if (output !== undefined) {
363633
364069
  return { output, exitCode };
@@ -713963,7 +714399,7 @@ const crsStatusSubcommand = {
713963
714399
  };
713964
714400
 
713965
714401
  ;// CONCATENATED MODULE: ./package.json
713966
- const package_namespaceObject = JSON.parse('{"i8":"2.11.0-snapshot-feature-cr-git-sha-tags-1"}');
714402
+ const package_namespaceObject = JSON.parse('{"i8":"2.11.0-snapshot-patch-claims-3"}');
713967
714403
  ;// CONCATENATED MODULE: ../../package.json
713968
714404
  const package_namespaceObject_1 = {"i8":"2.10.0"};
713969
714405
  // EXTERNAL MODULE: ../../node_modules/semver/index.js