@firestartr/cli 2.11.0-snapshot-patch-claims → 2.11.0-snapshot-mco-03

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 ?? entity.name}`.toLowerCase();
128398
+ const entityFolderName = `${entity.metadata.name}`.toLowerCase();
128399
128399
  return external_path_default().join(basePath, entityFolderName, featureName);
128400
128400
  }
128401
128401
  function getFeatureRenderedConfigForComponent(entity, featureName, basePath = '/tmp/features') {
@@ -164363,6 +164363,771 @@ const base_secrets_schema = 'firestartr.dev://common/SecretsClaim';
164363
164363
  SecretsClaimSchema: base_secrets_schema,
164364
164364
  });
164365
164365
 
164366
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/lazy_loader.ts
164367
+
164368
+
164369
+
164370
+
164371
+
164372
+
164373
+
164374
+
164375
+
164376
+
164377
+
164378
+
164379
+
164380
+
164381
+
164382
+
164383
+ // We need to ensure the grep command is a gnu version
164384
+ let IS_GNU_GREP = false;
164385
+ let GREP_ON_CHECK = false;
164386
+ // this loop is for avoid race conditions
164387
+ // once a check is running no more checks
164388
+ async function waitForCheck() {
164389
+ const f_wait = () => new Promise((timeOut) => setTimeout(timeOut, 10));
164390
+ do {
164391
+ await f_wait();
164392
+ } while (GREP_ON_CHECK);
164393
+ }
164394
+ function checkGrep() {
164395
+ return new Promise((ok, ko) => {
164396
+ // idempotency: already checked
164397
+ if (IS_GNU_GREP)
164398
+ return ok();
164399
+ // a check is already running no new checks
164400
+ if (GREP_ON_CHECK)
164401
+ return waitForCheck();
164402
+ logger.info('Checking the grep command');
164403
+ GREP_ON_CHECK = true;
164404
+ const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['--version']);
164405
+ handler.on('close', (code) => {
164406
+ GREP_ON_CHECK = false;
164407
+ if (code === 0) {
164408
+ logger.debug('Grep is a gnu grep');
164409
+ IS_GNU_GREP = true;
164410
+ ok();
164411
+ }
164412
+ else {
164413
+ ko('Lazy evaluation needs a gnu-grep command');
164414
+ }
164415
+ });
164416
+ });
164417
+ }
164418
+ async function loadClaim(claimRef, org, defaults = loadClaimDefaults(), patchClaim = loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, existingRefs = {}, postValidations = new Map()) {
164419
+ await checkGrep();
164420
+ let result = existingRefs;
164421
+ logger.info(`Load reference ${claimRef}`);
164422
+ initVirtualClaims(org);
164423
+ logger.info(`Load reference (parts) ${claimRef.split(/-/)[0]} ${claimRef.replace(/^[^-]+-/, '')}`);
164424
+ // cargas datos con grep
164425
+ try {
164426
+ const claimData = await lazyGetClaim(claimRef.split(/-/)[0], claimRef.replace(/^[^-]+-/, ''), org, cwd);
164427
+ const claim = patchClaim(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(claimData), defaults);
164428
+ let variants = [];
164429
+ if (claim.kind === 'TFWorkspaceClaim' &&
164430
+ claim.providers?.terraform?.variants) {
164431
+ variants = claim.providers.terraform.variants;
164432
+ delete claim.providers.terraform.variants;
164433
+ }
164434
+ logger.silly(`Patched claim is:
164435
+ ---
164436
+ ${catalog_common/* default.io.toYaml */.Z.io.toYaml(claim)}`);
164437
+ try {
164438
+ validateClaim(claim, base[`${claim.kind}Schema`]);
164439
+ await optionalValidation(claim);
164440
+ }
164441
+ catch (error) {
164442
+ let errorMsg = '';
164443
+ if (Array.isArray(error)) {
164444
+ for (const data of error) {
164445
+ errorMsg = `${errorMsg}
164446
+ - ${data.instancePath}: ${data.message}`;
164447
+ }
164448
+ }
164449
+ else {
164450
+ errorMsg = error instanceof Error ? error.message : String(error);
164451
+ }
164452
+ throw new Error(`Error when validating claim ${claimRef}: ${errorMsg}`);
164453
+ }
164454
+ result[claimRef] = {};
164455
+ result[claimRef]['claim'] = claim;
164456
+ result[claimRef]['claimPath'] = VisitedClaims[claimRef];
164457
+ if (VisitedClaims[claimRef] === 'virtual') {
164458
+ result = await setVirtualClaimAdditionalData(result, claim, claimRef);
164459
+ }
164460
+ else {
164461
+ result = await setNonVirtualClaimAdditionalData(result, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
164462
+ }
164463
+ const claimKind = claim.kind;
164464
+ const references = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(claim));
164465
+ for (const ref of references) {
164466
+ if (!result[ref]) {
164467
+ const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
164468
+ result = lodash_default().merge(result, resolvedReferences);
164469
+ }
164470
+ }
164471
+ if (variants.length > 0) {
164472
+ const parentClaimPath = VisitedClaims[claimRef];
164473
+ for (const variant of variants) {
164474
+ if (!variant.name || !variant.overrides) {
164475
+ throw new Error(`Variant in claim ${claimRef} is missing required field 'name' or 'overrides'`);
164476
+ }
164477
+ const prohibitedOverrideFields = ['source', 'module', 'name'];
164478
+ for (const field of prohibitedOverrideFields) {
164479
+ if (field in variant.overrides) {
164480
+ throw new Error(`Variant '${variant.name}' in claim ${claimRef} cannot override '${field}'`);
164481
+ }
164482
+ }
164483
+ const composedName = claim.providers.terraform.name + '-' + variant.name;
164484
+ const variantClaim = lodash_default().cloneDeep(claim);
164485
+ variantClaim._parentClaimName = claim.name;
164486
+ variantClaim.name = composedName;
164487
+ variantClaim.providers.terraform = lodash_default().merge({}, claim.providers.terraform, variant.overrides);
164488
+ variantClaim.providers.terraform.name = composedName;
164489
+ if (!variantClaim.annotations) {
164490
+ variantClaim.annotations = {};
164491
+ }
164492
+ variantClaim.annotations['firestartr.dev/variant-of'] =
164493
+ claim.providers.terraform.name;
164494
+ const variantRef = `TFWorkspaceClaim-${composedName}`;
164495
+ if (result[variantRef]) {
164496
+ throw new Error(`Variant name '${variant.name}' (composed: '${composedName}') from claim '${claimRef}' conflicts with existing claim '${variantRef}'`);
164497
+ }
164498
+ logger.info(`Creating synthetic variant claim ${variantRef}`);
164499
+ result[variantRef] = {};
164500
+ result[variantRef]['claim'] = variantClaim;
164501
+ result[variantRef]['claimPath'] = parentClaimPath;
164502
+ result = await setNonVirtualClaimAdditionalData(result, variantClaim, variantRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
164503
+ const variantReferences = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(variantClaim));
164504
+ for (const ref of variantReferences) {
164505
+ if (!result[ref]) {
164506
+ const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
164507
+ result = lodash_default().merge(result, resolvedReferences);
164508
+ }
164509
+ }
164510
+ }
164511
+ }
164512
+ }
164513
+ catch (err) {
164514
+ throw `Lazy Loading: ${err}`;
164515
+ }
164516
+ return [result, postValidations];
164517
+ }
164518
+ let LoadedClaims = {};
164519
+ let VisitedClaims = {};
164520
+ let DuplicatedClaims = {};
164521
+ function resetLazyLoader() {
164522
+ LoadedClaims = {};
164523
+ VisitedClaims = {};
164524
+ DuplicatedClaims = {};
164525
+ }
164526
+ async function lazyGetClaim(kind, name, org, cwd) {
164527
+ const indice = `${kind}-${name}`;
164528
+ logger.info(`Lazy loading ${kind}-${name} with index ${indice}`);
164529
+ if (indice in LoadedClaims)
164530
+ return LoadedClaims[indice];
164531
+ if (isVirtualClaim(kind, name)) {
164532
+ await loadVirtualClaim(kind, name, org);
164533
+ }
164534
+ else {
164535
+ await getClaimsByName(name, cwd);
164536
+ }
164537
+ if (indice in LoadedClaims) {
164538
+ return LoadedClaims[indice];
164539
+ }
164540
+ else {
164541
+ throw new Error(`Error: ${kind}-${name} not found`);
164542
+ }
164543
+ }
164544
+ async function getClaimsByName(name, cwd = '.') {
164545
+ return new Promise((ok, ko) => {
164546
+ const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['-r', '-l', '--include=*', '-E', `name: "?${name}"?`, '.'], {
164547
+ cwd: cwd,
164548
+ });
164549
+ logger.info(`Running ${['grep', '-r', '-l', '--include', '-E', 'name: "' + name + '"?', '.'].join(' ')}`);
164550
+ const entradas = [];
164551
+ const chunks = [];
164552
+ handler.stdout.on('data', (data) => {
164553
+ chunks.push(data.toString('utf-8'));
164554
+ });
164555
+ handler.on('close', () => {
164556
+ const dataFinal = chunks.join('');
164557
+ const fileNameList = dataFinal.split(/\n/).filter((line) => line !== '');
164558
+ for (const fileName of fileNameList) {
164559
+ // only yaml files
164560
+ if (!fileName.match(/\.yml$|\.yaml$/)) {
164561
+ continue;
164562
+ }
164563
+ // only entries not already visited
164564
+ if (Object.values(VisitedClaims).includes(fileName)) {
164565
+ continue;
164566
+ }
164567
+ entradas.push(external_path_.join(cwd, fileName));
164568
+ }
164569
+ return Promise.all(entradas.map((entrada) => {
164570
+ return loadRawClaim(entrada);
164571
+ }))
164572
+ .then(() => {
164573
+ ok();
164574
+ })
164575
+ .catch((err) => {
164576
+ ko(err);
164577
+ });
164578
+ });
164579
+ });
164580
+ }
164581
+ async function loadRawClaim(entry) {
164582
+ return new Promise((ok, ko) => {
164583
+ external_fs_.readFile(entry, 'utf-8', (error, data) => {
164584
+ if (error)
164585
+ return ko(`Reading ${entry}: ${error}`);
164586
+ const claim = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
164587
+ if (!('kind' in claim && 'name' in claim)) {
164588
+ logger.warn(`Invalid claim file ${entry}`);
164589
+ }
164590
+ else {
164591
+ const claimKey = `${claim.kind}-${claim.name}`;
164592
+ if (Object.keys(DuplicatedClaims).includes(claimKey) &&
164593
+ DuplicatedClaims[claimKey] !== entry) {
164594
+ return ko(`Duplicated claim ${claimKey} found files: ${DuplicatedClaims[claimKey]} and ${entry}`);
164595
+ }
164596
+ LoadedClaims[claimKey] = data;
164597
+ VisitedClaims[claimKey] = entry;
164598
+ DuplicatedClaims[claimKey] = entry;
164599
+ ok();
164600
+ }
164601
+ });
164602
+ });
164603
+ }
164604
+ async function setVirtualClaimAdditionalData(renderedData, claim, claimRef) {
164605
+ const virtualClaim = getVirtualClaim(claim.kind, claim.name);
164606
+ renderedData[claimRef]['initializers'] = [
164607
+ new UUIDInitializer(claim),
164608
+ new InitializerClaimRef(),
164609
+ new InitializerDefault(virtualClaim.getDefaultConfig()),
164610
+ ];
164611
+ renderedData[claimRef]['globals'] = [];
164612
+ renderedData[claimRef]['overrides'] = [];
164613
+ renderedData[claimRef]['normalizers'] = [new NameNormalizer()];
164614
+ return renderedData;
164615
+ }
164616
+ async function setNonVirtualClaimAdditionalData(renderedData, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers) {
164617
+ renderedData[claimRef]['initializers'] = await loadInitializers(claim, renderedData[claimRef]['claimPath']);
164618
+ renderedData[claimRef]['globals'] = await loadGlobals(claim);
164619
+ renderedData[claimRef]['overrides'] = loadOverrides(claim);
164620
+ renderedData[claimRef]['normalizers'] = await loadNormalizers(claim, renderedData[claimRef]['claimPath']);
164621
+ return renderedData;
164622
+ }
164623
+ async function loadVirtualClaim(kind, name, org) {
164624
+ const virtualClaim = getVirtualClaim(kind, name);
164625
+ const expandedClaim = await virtualClaim.expand({ org });
164626
+ logger.info(`Loading virtual claim ${kind} ${name} ${org}`);
164627
+ LoadedClaims[`${kind}-${name}`] = catalog_common/* default.io.toYaml */.Z.io.toYaml(expandedClaim);
164628
+ VisitedClaims[`${kind}-${name}`] = 'virtual';
164629
+ }
164630
+
164631
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/claimsDefaulter.ts
164632
+
164633
+
164634
+
164635
+ /*
164636
+ * Default blocks are objects that cannot be merged but applied as a whole. Meaning:
164637
+ * - If the object is defined at the claim-level it is maintained *AS IT IS* (no merge)
164638
+ * - If the block is missing at the claim-level, the default block is applied
164639
+ */
164640
+ const defaultBlocksPaths = ['/providers/terraform/sync'];
164641
+ function applyBlockAwareDefaults(claim, defaultClaim) {
164642
+ // we always work with a clone of the original
164643
+ const claimClone = JSON.parse(JSON.stringify(claim));
164644
+ // if the block is claim-level defined we store it here
164645
+ const defaultBlocks = new Map();
164646
+ // we remove the blocks entirely of the claim
164647
+ // to achieve an add copy
164648
+ for (const defaultBlockPath of defaultBlocksPaths) {
164649
+ if (claimsDefaulter_hasDeepPath(claimClone, defaultBlockPath) !== true)
164650
+ continue;
164651
+ const originalValue = fast_json_patch_default().getValueByPointer(claimClone, defaultBlockPath);
164652
+ if (originalValue) {
164653
+ logger.silly(`${claim.kind}/${claim.name}: has an original ${defaultBlockPath}: preserving`);
164654
+ defaultBlocks.set(defaultBlockPath, originalValue);
164655
+ fast_json_patch_default().applyPatch(claimClone, [{ op: 'remove', path: defaultBlockPath }]);
164656
+ }
164657
+ }
164658
+ // if the patch has the same path of a block
164659
+ // and the block was defined at claim-level
164660
+ // we interchange the value with the original value
164661
+ // otherwise we use the default
164662
+ const jsonPatchOps = fast_json_patch_default().compare(claimClone, defaultClaim)
164663
+ .filter((jp) => jp.op === 'add')
164664
+ .map((jp) => {
164665
+ if (defaultBlocks.has(jp.path)) {
164666
+ return {
164667
+ ...jp,
164668
+ value: defaultBlocks.get(jp.path),
164669
+ };
164670
+ }
164671
+ else {
164672
+ return jp;
164673
+ }
164674
+ });
164675
+ return fast_json_patch_default().applyPatch(claimClone, jsonPatchOps).newDocument;
164676
+ }
164677
+ function claimsDefaulter_hasDeepPath(data, deepPath) {
164678
+ return lodash_default().has(data, deepPath.replace(/\//g, '.').replace(/^\./, ''));
164679
+ }
164680
+
164681
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/loader.ts
164682
+
164683
+
164684
+
164685
+
164686
+
164687
+
164688
+
164689
+
164690
+
164691
+
164692
+
164693
+
164694
+
164695
+
164696
+
164697
+ const isYamlFile = new RegExp(/\.(yaml|yml)$/);
164698
+ /**
164699
+ *
164700
+ * @returns Virtual Claims
164701
+ */
164702
+ const virtualClaims = [new FirestartrAllClaim()];
164703
+ /*
164704
+ * Loads all applicable globals for a given claim and stores them into a list.
164705
+ *
164706
+ * Input:
164707
+ * - claim: our custom claim object, with which we check if the global is applicable
164708
+ *
164709
+ * Returns:
164710
+ * - A GlobalSection array promise, containing all the globals loaded for this claim
164711
+ *
164712
+ */
164713
+ async function loadGlobals(claim) {
164714
+ const result = [];
164715
+ await crawl(getPath('globals'), (entry) => {
164716
+ return isYamlFile.test(entry);
164717
+ }, async (entry, data) => {
164718
+ const globalData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
164719
+ const ext = external_path_.extname(entry);
164720
+ const name = external_path_.basename(entry, ext);
164721
+ if (name in SECTIONS_BY_FILE_NAME) {
164722
+ const section = new SECTIONS_BY_FILE_NAME[name](globalData);
164723
+ if (!(await section.validate(SCHEMAS_BY_SECTION_NAME[name] || {}))) {
164724
+ throw new Error(`Invalid ${name} global file`);
164725
+ }
164726
+ if (globalData.kind === claim.kind) {
164727
+ result.push(section);
164728
+ }
164729
+ else {
164730
+ logger.debug(`Skipping global ${name} because it is not applicable to ${claim.kind}`);
164731
+ }
164732
+ }
164733
+ });
164734
+ logger.info(`Loaded globals ${result}`);
164735
+ return result;
164736
+ }
164737
+ /*
164738
+ * If the input claim is a GithubRepositoryClaim, it loads all it's applicable
164739
+ * overrides and stores them into a list. Otherwise, it returns an empty list.
164740
+ *
164741
+ * Input:
164742
+ * - claim: our custom claim object, which should be a GithubRepositoryClaim
164743
+ *
164744
+ * Returns:
164745
+ * - An Overrider array, with a GithubRepositoryOverrider if the claim
164746
+ * was a GithubRepositoryClaim, or empty if it was not.
164747
+ *
164748
+ */
164749
+ function loadOverrides(claim) {
164750
+ const overrides = [];
164751
+ if (claim.kind === 'ComponentClaim' && claim.providers.github.overrides) {
164752
+ overrides.push(new GithubRepositoryOverrider());
164753
+ }
164754
+ return overrides;
164755
+ }
164756
+ /**
164757
+ * Load all normalizers and stores them into a list.
164758
+ * @param claim
164759
+ * @returns
164760
+ */
164761
+ async function loadNormalizers(_claim, path) {
164762
+ const result = [];
164763
+ for (const normalizer of NORMALIZERS) {
164764
+ result.push(new normalizer({ path: path }));
164765
+ }
164766
+ return result;
164767
+ }
164768
+ /*
164769
+ * Loads all applicable initializers for a given claim and stores them into a list.
164770
+ *
164771
+ * Input:
164772
+ * - claim: our custom claim object, with which we check if the initializer is applicable
164773
+ *
164774
+ * Returns:
164775
+ * - An Initializer array promise, containing all the initializers loaded for this claim
164776
+ *
164777
+ */
164778
+ let loadedInitializers = false;
164779
+ async function loadInitializers(claim, claimPath, initializersPath = getPath('initializers')) {
164780
+ const result = [];
164781
+ await crawl(initializersPath, (entry) => {
164782
+ return isYamlFile.test(entry);
164783
+ }, async (entry, data) => {
164784
+ const initializerData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
164785
+ const ext = external_path_.extname(entry);
164786
+ const name = external_path_.basename(entry, ext);
164787
+ if (name in INITIALIZERS_BY_FILE_NAME) {
164788
+ const initializerBfN = new INITIALIZERS_BY_FILE_NAME[name](initializerData);
164789
+ if (!(await initializerBfN.validate(SCHEMAS_BY_INITIALIZER_NAME[name] || {}))) {
164790
+ throw new Error(`Invalid ${name} initializer file`);
164791
+ }
164792
+ if (initializerData.kind === claim.kind) {
164793
+ result.push(initializerBfN);
164794
+ }
164795
+ else {
164796
+ logger.debug(`Skipping initializer ${name} because it is not applicable to ${claim.kind}`);
164797
+ }
164798
+ }
164799
+ });
164800
+ for (const initializer of INITIALIZERS) {
164801
+ if (initializer.applicableKinds.includes(claim.kind)) {
164802
+ result.push(new initializer({ path: claimPath }));
164803
+ }
164804
+ }
164805
+ logger.info(`Loaded initializers ${JSON.stringify(result)}`);
164806
+ loadedInitializers = result;
164807
+ return loadedInitializers;
164808
+ }
164809
+ function loadClaimDefaults() {
164810
+ try {
164811
+ return catalog_common/* default.io.fromYaml */.Z.io.fromYaml(external_fs_.readFileSync(`${getPath('claimsDefaults')}/claims_defaults.yaml`, 'utf-8'));
164812
+ }
164813
+ catch (error) {
164814
+ const errMsg = `Could not read claims defaults file in path ${getPath('claimsDefaults')}/claims_defaults.yaml: ${error}`;
164815
+ logger.error(errMsg);
164816
+ throw new Error(errMsg);
164817
+ }
164818
+ }
164819
+ /*
164820
+ * Using fast-json-patch, compares a claim to its kind's defaults, then patches it.
164821
+ *
164822
+ * Input:
164823
+ * - claim: our custom claim object, which we want to patch
164824
+ * - defaultsClaims: an object, containing the defaults for each claim kind,
164825
+ * with the same structure that we use for the actual claims
164826
+ *
164827
+ * Returns:
164828
+ * - The patched claim
164829
+ *
164830
+ */
164831
+ function loader_patchClaim(claim, defaultsClaims) {
164832
+ if (defaultsClaims[claim.kind]) {
164833
+ claim = applyBlockAwareDefaults(claim, defaultsClaims[claim.kind]);
164834
+ }
164835
+ return claim;
164836
+ }
164837
+ async function loadCRsWithFullSet(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
164838
+ const result = {};
164839
+ const fullResult = {};
164840
+ await crawlWithExclusions(crsPath, (entry) => {
164841
+ return isYamlFile.test(entry);
164842
+ }, async (entry, data) => {
164843
+ const yamlData = stripKnownRelations(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data));
164844
+ const metadata = yamlData?.metadata;
164845
+ if (!(yamlData &&
164846
+ typeof yamlData === 'object' &&
164847
+ 'kind' in yamlData &&
164848
+ metadata &&
164849
+ typeof metadata === 'object' &&
164850
+ 'name' in metadata)) {
164851
+ logger.warn(`Invalid CR file ${entry}`);
164852
+ }
164853
+ else {
164854
+ const crKey = `${yamlData.kind}-${metadata.name}`;
164855
+ if (fullResult[crKey]) {
164856
+ logger.error(`Duplicate CR ${crKey}`);
164857
+ }
164858
+ // Keep the complete CR set for validations that need repository-wide context.
164859
+ fullResult[crKey] = yamlData;
164860
+ // Keep the filtered CR set for previous-CR lookup during claim rendering.
164861
+ if (allowedClaimReferences.length === 0 ||
164862
+ allowedClaimReferences.includes(yamlData.metadata?.annotations?.[catalog_common/* default.generic.getFirestartrAnnotation */.Z.generic.getFirestartrAnnotation('claim-ref')])) {
164863
+ result[crKey] = yamlData;
164864
+ }
164865
+ }
164866
+ }, excludedPaths.concat(getAdditionalPaths()));
164867
+ return { crs: result, fullCrs: fullResult };
164868
+ }
164869
+ async function loadCRs(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
164870
+ const { crs } = await loadCRsWithFullSet(crsPath, excludedPaths, allowedClaimReferences);
164871
+ return crs;
164872
+ }
164873
+ /*
164874
+ * Description: Get the renamed using claimRef annotation
164875
+ * Input: claims: any, crs: any
164876
+ * Output: renames: IRenameResult[]
164877
+ */
164878
+ async function loadRenames(claims, crs) {
164879
+ /*
164880
+ * If checkRenames is false, we don't need to check for renames
164881
+ */
164882
+ const renamesEnabled = getRenamesEnabled();
164883
+ if (!renamesEnabled)
164884
+ return [];
164885
+ const result = [];
164886
+ /**
164887
+ * For each CR, look for a claim that does not match the name with CR's firestartr.dev/claim-ref annotation
164888
+ */
164889
+ for (const cr of Object.values(crs)) {
164890
+ const anyCr = cr;
164891
+ if (!anyCr?.metadata?.annotations)
164892
+ continue;
164893
+ // if the annotation is a claimRef
164894
+ const annotationName = common.generic.getFirestartrAnnotation('claim-ref');
164895
+ const annotations = anyCr?.metadata?.annotations || {};
164896
+ if (annotationName in annotations) {
164897
+ const claimRef = anyCr.metadata.annotations[annotationName];
164898
+ // split the annotation into kind and name
164899
+ const [kind, name] = claimRef.split('/');
164900
+ // if the claim exists, check if the name is different for the given provider
164901
+ const referencedClaim = claims[`${kind}-${name}`].claim;
164902
+ const oldName = anyCr.metadata.name;
164903
+ const newName = referencedClaim.providers[getConfiguredProvider()].name;
164904
+ if (oldName !== newName) {
164905
+ result.push({
164906
+ kind: referencedClaim.kind,
164907
+ claimPath: claims[`${kind}-${name}`].claimPath,
164908
+ claimName: referencedClaim.name,
164909
+ oldName,
164910
+ newName,
164911
+ });
164912
+ }
164913
+ }
164914
+ }
164915
+ return result;
164916
+ }
164917
+ async function loadClaimsList(claimRefList, claimsPath = getPath('claims')) {
164918
+ const data = {
164919
+ renderClaims: {},
164920
+ crs: {},
164921
+ fullCrs: {},
164922
+ };
164923
+ const defaults = loadClaimDefaults();
164924
+ for await (const claimRef of claimRefList) {
164925
+ const [renderedClaimData] = await loadClaim(claimRef, getOrg(), defaults, loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, claimsPath);
164926
+ data.renderClaims = lodash_default().merge(data.renderClaims, renderedClaimData);
164927
+ }
164928
+ const crClaimReferences = [];
164929
+ for (const ref of Object.keys(data.renderClaims)) {
164930
+ // Replaces only the first instance of '-'
164931
+ crClaimReferences.push(ref.replace('-', '/'));
164932
+ }
164933
+ const loadedCrs = await loadCRsWithFullSet(getPath('crs'), [getPath('globals'), getPath('initializers')], crClaimReferences);
164934
+ data['crs'] = loadedCrs.crs;
164935
+ data['fullCrs'] = loadedCrs.fullCrs;
164936
+ return data;
164937
+ }
164938
+ function getOrg() {
164939
+ const groupOrg = catalog_common/* default.environment.getFromEnvironmentWithDefault */.Z.environment.getFromEnvironmentWithDefault(catalog_common/* default.types.envVars.org */.Z.types.envVars.org, '');
164940
+ return groupOrg;
164941
+ }
164942
+
164943
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/references.ts
164944
+ function validateTfStateKeyUniqueness(crs) {
164945
+ const tfStateKeys = {};
164946
+ for (const crKey of Object.keys(crs)) {
164947
+ const cr = crs[crKey];
164948
+ if (!isTerraformWorkspace(cr))
164949
+ continue;
164950
+ const tfKey = cr.spec.firestartr.tfStateKey;
164951
+ if (tfStateKeys[tfKey]) {
164952
+ throw new Error(`❗❗ ❌ RENDER ERROR ❌ ❗❗
164953
+
164954
+ The tfStateKey ${tfKey} is not unique,
164955
+
164956
+ It is being used by the following CRs:
164957
+
164958
+ - ${tfStateKeys[tfKey].kind} ${tfStateKeys[tfKey].metadata.name}
164959
+
164960
+ - ${cr.kind} ${cr.metadata.name}
164961
+
164962
+ Check the following Claims to ensure that the tfStateKey is unique:
164963
+
164964
+ - ${tfStateKeys[tfKey].metadata.annotations['firestartr.dev/claim-ref']}
164965
+
164966
+ - ${cr.metadata.annotations['firestartr.dev/claim-ref']}
164967
+ `);
164968
+ }
164969
+ tfStateKeys[cr.spec.firestartr.tfStateKey] = cr;
164970
+ }
164971
+ }
164972
+ function references_isCatalogEntity(cr) {
164973
+ return [
164974
+ 'Component',
164975
+ 'Domain',
164976
+ 'System',
164977
+ 'User',
164978
+ 'Group',
164979
+ 'Resource',
164980
+ ].includes(cr.kind);
164981
+ }
164982
+ function isTerraformWorkspace(cr) {
164983
+ return cr.kind === 'FirestartrTerraformWorkspace';
164984
+ }
164985
+
164986
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/crSize.ts
164987
+
164988
+ const K8S_OBJECT_SIZE_LIMIT = 1572864; // 1.5 MiB in bytes, etcd recommended limit: https://etcd.io/docs/latest/dev-guide/limit/
164989
+ function validateCrSizes(crs) {
164990
+ for (const key of Object.keys(crs)) {
164991
+ const cr = crs[key];
164992
+ const serialized = catalog_common/* default.io.toYaml */.Z.io.toYaml(cr);
164993
+ const size = Buffer.byteLength(serialized, 'utf8');
164994
+ if (size > K8S_OBJECT_SIZE_LIMIT) {
164995
+ 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).`);
164996
+ }
164997
+ }
164998
+ }
164999
+
165000
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/permissions.ts
165001
+
165002
+ function validatePermissionsUniqueness(crs) {
165003
+ for (const crkey in crs) {
165004
+ const cr = crs[crkey];
165005
+ if (cr.kind === 'FirestartrGithubRepository') {
165006
+ const permissions = cr.spec.permissions;
165007
+ logger.debug(`Validating Permissions Uniqueness of ${crkey}`);
165008
+ if (permissions) {
165009
+ const rolesByIdentifier = new Map();
165010
+ for (const perm of permissions) {
165011
+ const identifier = perm.collaborator
165012
+ ? perm.collaborator
165013
+ : `ref:${perm.ref.kind}:${perm.ref.name}`;
165014
+ const role = perm.role;
165015
+ const prevRole = rolesByIdentifier.get(identifier);
165016
+ if (prevRole === undefined) {
165017
+ rolesByIdentifier.set(identifier, role);
165018
+ }
165019
+ else if (prevRole !== role) {
165020
+ throw new Error(`Conflicting permission role in FirestartrGithubRepository ${crkey}: ${identifier} has roles "${prevRole}" and "${role}".\nFull CR: ${JSON.stringify(cr, null, 2)}`);
165021
+ }
165022
+ }
165023
+ }
165024
+ }
165025
+ }
165026
+ }
165027
+
165028
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/backstageAnnotations.ts
165029
+ const backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX = 'firestartr.backstage.dev/';
165030
+ const CATALOG_KINDS = new Set([
165031
+ 'User',
165032
+ 'Group',
165033
+ 'Component',
165034
+ 'System',
165035
+ 'Domain',
165036
+ 'API',
165037
+ 'Resource',
165038
+ 'CatalogKubernetesResource',
165039
+ 'CatalogTerraformWorkspace',
165040
+ 'CatalogArgoDeploy',
165041
+ 'CatalogOrgWebhook',
165042
+ 'CatalogSecrets',
165043
+ ]);
165044
+ function validateNoBackstageAnnotationsInK8sCrs(crs) {
165045
+ for (const crKey in crs) {
165046
+ const cr = crs[crKey];
165047
+ if (CATALOG_KINDS.has(cr.kind))
165048
+ continue;
165049
+ if (cr.metadata?.annotations) {
165050
+ const backstageKeys = Object.keys(cr.metadata.annotations).filter((k) => k.startsWith(backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX));
165051
+ if (backstageKeys.length > 0) {
165052
+ throw new Error(`K8s CR ${crKey} contains Backstage-only annotations (${backstageKeys.join(', ')}). ` +
165053
+ `Annotations with prefix ${backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX} must only appear on Backstage catalog entities, not on Kubernetes CRs.`);
165054
+ }
165055
+ }
165056
+ }
165057
+ }
165058
+
165059
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/refsSorter/refsSorter.ts
165060
+
165061
+ /**
165062
+ * @param renderClaims renderClaims to sort
165063
+ * @param kinds kinds to get from renderClaims
165064
+ * @returns renderClaims with only the kinds specified
165065
+ */
165066
+ function sortRenderClaimsByKind(renderClaims, kinds) {
165067
+ let result = {};
165068
+ for (const kind of kinds) {
165069
+ for (const key in renderClaims) {
165070
+ if (renderClaims[key].claim.kind === kind) {
165071
+ result[key] = renderClaims[key];
165072
+ }
165073
+ }
165074
+ result = sortSelfKindRefs(result, kind);
165075
+ }
165076
+ return result;
165077
+ }
165078
+ function sortSelfKindRefs(renderClaims, kind) {
165079
+ if (!hasSelfRefs(kind)) {
165080
+ return renderClaims;
165081
+ }
165082
+ const newRenderClaims = {};
165083
+ const data = extractRefs(renderClaims, kind);
165084
+ const ordered = [];
165085
+ const visiting = [];
165086
+ const visited = [];
165087
+ for (const key of Object.keys(data)) {
165088
+ if (!visited.find((obj) => obj.name === key)) {
165089
+ sortRefs(data, data[key], ordered, visiting, visited);
165090
+ }
165091
+ }
165092
+ for (const cr of ordered) {
165093
+ newRenderClaims[cr.name] = renderClaims[cr.name];
165094
+ }
165095
+ return newRenderClaims;
165096
+ }
165097
+ function sortRefs(data, claim, ordered = [], visiting = [], visited = []) {
165098
+ if (visiting.find((obj) => obj.name === claim.name)) {
165099
+ throw new Error(`Circular dependency detected for '${claim.name}'`);
165100
+ }
165101
+ if (!visited.find((obj) => obj.name === claim.name)) {
165102
+ visiting.push(claim);
165103
+ if (claim.refs) {
165104
+ for (const ref of claim.refs) {
165105
+ const referencedObject = data[ref];
165106
+ if (!referencedObject) {
165107
+ throw new Error(`Reference '${ref}' does not exist for '${claim.name}'`);
165108
+ }
165109
+ sortRefs(data, referencedObject, ordered, visiting, visited);
165110
+ }
165111
+ }
165112
+ visited.push(visiting.pop());
165113
+ ordered.push(claim);
165114
+ }
165115
+ }
165116
+ function hasSelfRefs(kind) {
165117
+ return ['TFWorkspaceClaim', 'GroupClaim'].includes(kind);
165118
+ }
165119
+
165120
+ ;// CONCATENATED MODULE: ../cdk8s_renderer/src/renderer/claims-sorter.ts
165121
+
165122
+ function sortKinds(orderedKinds, claims) {
165123
+ const result = [];
165124
+ for (const kind of orderedKinds) {
165125
+ const kindRenderClaims = sortRenderClaimsByKind(claims, [kind]);
165126
+ result.push(kindRenderClaims);
165127
+ }
165128
+ return result;
165129
+ }
165130
+
164366
165131
  ;// CONCATENATED MODULE: ../features_renderer/src/common.ts
164367
165132
 
164368
165133
  function isDirectory(path) {
@@ -164403,9 +165168,14 @@ var ajv_default = /*#__PURE__*/__nccwpck_require__.n(dist_ajv);
164403
165168
  },
164404
165169
  },
164405
165170
  patches: {
164406
- type: 'array',
164407
- items: {
164408
- $ref: '#/definitions/Patch',
165171
+ type: 'object',
165172
+ patternProperties: {
165173
+ '^.+$': {
165174
+ type: 'array',
165175
+ items: {
165176
+ $ref: '#/definitions/Patch',
165177
+ },
165178
+ },
164409
165179
  },
164410
165180
  },
164411
165181
  filesTemplates: {
@@ -164508,7 +165278,7 @@ var ajv_default = /*#__PURE__*/__nccwpck_require__.n(dist_ajv);
164508
165278
  type: 'string',
164509
165279
  },
164510
165280
  value: {
164511
- type: ['string', 'object', 'array', 'number', 'boolean', 'null'],
165281
+ type: 'string',
164512
165282
  },
164513
165283
  },
164514
165284
  required: ['name', 'op', 'path'],
@@ -164550,12 +165320,6 @@ function __validateFeatureConfig(featurePath) {
164550
165320
  }
164551
165321
  }
164552
165322
  function __validateFeatureConfigData(configData) {
164553
- // Detect old provider-keyed patches shape for migration hint (PLAN #2648)
164554
- if (configData.patches &&
164555
- !Array.isArray(configData.patches) &&
164556
- typeof configData.patches === 'object') {
164557
- throw `Feature: config.yaml invalid patches: expected flat array of patches (claim-relative pointers like /annotations/backstage.io~1techdocs-ref), but got provider-keyed object ${JSON.stringify(Object.keys(configData.patches))}. Migrate: patches: { catalog: [...] } → patches: [{ op, path: /annotations/backstage.io~1techdocs-ref, value }]. See #2648. $ref paths also migrate: [spec, org] → [providers, github, org], [metadata, annotations, firestartr.dev/external-name] → [providers, github, name]`;
164558
- }
164559
165323
  const ajv = new (ajv_default())({
164560
165324
  allowUnionTypes: true,
164561
165325
  });
@@ -164577,12 +165341,14 @@ function stripAnchoringConstraints(schemaDef) {
164577
165341
  return clone;
164578
165342
  }
164579
165343
  function __validateJsonPatches(configData) {
164580
- const patches = configData.patches;
164581
- if (!Array.isArray(patches))
164582
- return;
164583
- const err = fast_json_patch_default().validate(patches);
164584
- if (err) {
164585
- throw `Feature: config.yaml -> patches: invalid patches ${JSON.stringify(err.message ?? err)}`;
165344
+ const keys = Object.keys(configData.patches);
165345
+ for (const key of keys) {
165346
+ if (configData.patches[key]) {
165347
+ if (fast_json_patch_default().validate(configData.patches[key])) {
165348
+ const errors = JSON.stringify(fast_json_patch_default().validate(configData.patches[key]).message);
165349
+ throw `Feature: config.yaml -> patches: invalid patches ${errors}`;
165350
+ }
165351
+ }
164586
165352
  }
164587
165353
  }
164588
165354
 
@@ -165420,12 +166186,10 @@ const renderTestsSchema = {
165420
166186
  items: {
165421
166187
  type: 'object',
165422
166188
  additionalProperties: false,
165423
- required: ['name'],
165424
- anyOf: [{ required: ['cr'] }, { required: ['claim'] }],
166189
+ required: ['name', 'cr'],
165425
166190
  properties: {
165426
166191
  name: { type: 'string', minLength: 1 },
165427
166192
  cr: { type: 'string', minLength: 1 },
165428
- claim: { type: 'string', minLength: 1 },
165429
166193
  args: { type: 'object' },
165430
166194
  },
165431
166195
  },
@@ -165480,9 +166244,6 @@ function resolveCrPath(featurePath, crRelPath) {
165480
166244
  }
165481
166245
  return resolved;
165482
166246
  }
165483
- function resolveClaimPath(featurePath, claimRelPath) {
165484
- return resolveCrPath(featurePath, claimRelPath);
165485
- }
165486
166247
  function listYamlFiles(dir) {
165487
166248
  if (!fs.existsSync(dir))
165488
166249
  return [];
@@ -165600,7 +166361,6 @@ async function createRenderContext(prefix = 'feature-render-') {
165600
166361
  buildExpectedOutput,
165601
166362
  loadAndValidateRenderTests,
165602
166363
  resolveCrPath,
165603
- resolveClaimPath,
165604
166364
  });
165605
166365
 
165606
166366
  ;// CONCATENATED MODULE: ../features_renderer/src/featureArgsValidation.ts
@@ -165724,97 +166484,19 @@ function render(featurePath, featureRenderPath, entity, firestartrConfig = {}, f
165724
166484
  external_fs_default().writeFileSync(configFile, JSON.stringify(output));
165725
166485
  return output;
165726
166486
  }
165727
- function getPatches(featurePath, entity, firestartrConfig = {}, featureArgs = {}) {
165728
- const configData = validate(featurePath);
165729
- validateFeatureArgs(featurePath, featureArgs);
165730
- const context = buildContext(entity, configData.args, firestartrConfig, featureArgs);
165731
- const rendered = JSON.parse(renderContent(JSON.stringify(configData), context));
165732
- return rendered.patches ?? [];
165733
- }
165734
- function buildContext(stitchedClaim, args, firestartrConfig, featureArgs) {
166487
+ function buildContext(entity, args, firestartrConfig, featureArgs) {
165735
166488
  const context = {};
165736
- const resolveRef = (doc, ref) => {
165737
- const v = lodash_default().get(doc, ref);
165738
- if (v !== undefined)
165739
- return v;
165740
- // Fallback for transition: try CR-shaped alternatives when stitchedClaim is CR-shaped
165741
- // and vice versa. Covers the ADR 0006 table plus legacy feature_a provisioner paths.
165742
- const altTable = {
165743
- 'providers,github,org': [
165744
- ['spec', 'org'],
165745
- ['spec', 'provisioner', 'org'],
165746
- ['spec', 'firestartr', 'technology', 'stack'],
165747
- ],
165748
- 'providers,github,branchStrategy,defaultBranch': [
165749
- ['spec', 'repo', 'defaultBranch'],
165750
- ['spec', 'provisioner', 'repo', 'defaultBranch'],
165751
- ],
165752
- 'providers,github,name': [
165753
- ['metadata', 'annotations', 'firestartr.dev/external-name'],
165754
- ['metadata', 'name'],
165755
- ],
165756
- name: [['metadata', 'name']],
165757
- 'annotations,firestartr.dev/dest-annotation': [
165758
- ['metadata', 'annotations', 'firestartr.dev/dest-annotation'],
165759
- ],
165760
- };
165761
- const key = ref.join(',');
165762
- const alts = altTable[key];
165763
- if (alts) {
165764
- for (const alt of alts) {
165765
- const av = lodash_default().get(doc, alt);
165766
- if (av !== undefined)
165767
- return av;
165768
- }
165769
- }
165770
- // Reverse fallback: if ref is CR-shaped, try claim-shaped
165771
- const reverseTable = {
165772
- 'spec,org': ['providers', 'github', 'org'],
165773
- 'spec,provisioner,technology,stack': ['providers', 'github', 'org'],
165774
- 'spec,firestartr,technology,stack': ['providers', 'github', 'org'],
165775
- 'metadata,name': ['name'],
165776
- 'metadata,annotations,firestartr.dev/dest-annotation': [
165777
- 'annotations',
165778
- 'firestartr.dev/dest-annotation',
165779
- ],
165780
- 'spec,provisioner,org': ['providers', 'github', 'org'],
165781
- 'spec,provisioner,repo,defaultBranch': [
165782
- 'providers',
165783
- 'github',
165784
- 'branchStrategy',
165785
- 'defaultBranch',
165786
- ],
165787
- 'spec,repo,defaultBranch': [
165788
- 'providers',
165789
- 'github',
165790
- 'branchStrategy',
165791
- 'defaultBranch',
165792
- ],
165793
- 'metadata,annotations,firestartr.dev/external-name': [
165794
- 'providers',
165795
- 'github',
165796
- 'name',
165797
- ],
165798
- };
165799
- const rev = reverseTable[ref.join(',')];
165800
- if (rev) {
165801
- const rv = lodash_default().get(doc, rev);
165802
- if (rv !== undefined)
165803
- return rv;
165804
- }
165805
- return v;
165806
- };
165807
166489
  Object.entries(args).forEach(([key, arg]) => {
165808
166490
  if ('$default' in arg) {
165809
166491
  if (Array.isArray(arg['$default'])) {
165810
- context[key] = resolveRef(stitchedClaim, arg['$default']);
166492
+ context[key] = lodash_default().get(entity, arg['$default']);
165811
166493
  }
165812
166494
  else {
165813
166495
  context[key] = arg['$default'];
165814
166496
  }
165815
166497
  }
165816
166498
  if ('$ref' in arg) {
165817
- const refValue = resolveRef(stitchedClaim, arg['$ref']);
166499
+ const refValue = lodash_default().get(entity, arg['$ref']);
165818
166500
  context[key] = refValue;
165819
166501
  }
165820
166502
  else if ('$lit' in arg) {
@@ -165944,7 +166626,6 @@ function updateFileContent(featureRenderPath, filePath, content) {
165944
166626
 
165945
166627
 
165946
166628
 
165947
-
165948
166629
  /* harmony default export */ const features_renderer = ({
165949
166630
  validate: validate,
165950
166631
  render: render,
@@ -165954,7 +166635,6 @@ function updateFileContent(featureRenderPath, filePath, content) {
165954
166635
  renderContent: renderContent,
165955
166636
  expandFiles: expandFiles,
165956
166637
  validateFeatureArgs: validateFeatureArgs,
165957
- getPatches: getPatches,
165958
166638
  });
165959
166639
 
165960
166640
  ;// CONCATENATED MODULE: ../features_preparer/src/logger.ts
@@ -165979,13 +166659,6 @@ function renderFeature(featureName, version, owner, repo, featureOwner, renderPa
165979
166659
  function renderFeatureFromPath(extractPath, renderedPath, featureOwner, featureArgs = {}) {
165980
166660
  return features_renderer.render(extractPath, renderedPath, featureOwner, {}, featureArgs);
165981
166661
  }
165982
- function getFeaturePatches(featureName, version, owner, repo, featureOwner, featureArgs = {}) {
165983
- const extractPath = external_path_default().join(catalog_common/* default.features.tarballs.getFeaturesExtractPath */.Z.features.tarballs.getFeaturesExtractPath(featureName, version, owner, repo), 'packages', featureName);
165984
- return getPatches(extractPath, featureOwner, {}, featureArgs);
165985
- }
165986
- function getFeaturePatchesFromPath(extractPath, featureOwner, featureArgs = {}) {
165987
- return getPatches(extractPath, featureOwner, {}, featureArgs);
165988
- }
165989
166662
 
165990
166663
  // EXTERNAL MODULE: ../../node_modules/adm-zip/adm-zip.js
165991
166664
  var adm_zip = __nccwpck_require__(7993);
@@ -166046,26 +166719,6 @@ featureArgs = {}, repo = 'features', owner = 'prefapp') {
166046
166719
  };
166047
166720
  return renderFeature(featureName, reference, owner, repo, featureOwner, '/tmp', featureArgs);
166048
166721
  }
166049
- async function installer_getFeaturePatches(featureName, version, featureOwner, featureArgs = {}, repo = 'features', owner = 'prefapp') {
166050
- const reference = `${featureName}-v${version}`;
166051
- return processFeaturePatches(featureName, reference, featureOwner, featureArgs, repo, owner);
166052
- }
166053
- async function getFeaturePatchesFromRef(featureName, featureRef, featureOwner, featureArgs = {}, repo = 'features', owner = 'prefapp') {
166054
- const reference = `${featureRef}`;
166055
- return processFeaturePatches(featureName, reference, featureOwner, featureArgs, repo, owner);
166056
- }
166057
- async function processFeaturePatches(featureName, reference, featureOwner, featureArgs = {}, repo = 'features', owner = 'prefapp') {
166058
- await downloadFeatureZip(repo, featureName, reference, owner);
166059
- featureArgs['traceability'] = {
166060
- version: reference,
166061
- owner,
166062
- repo,
166063
- name: featureName,
166064
- ref: reference,
166065
- url: `https://github.com/${owner}/${repo}/tree/${reference}/packages/${featureName}`,
166066
- };
166067
- return getFeaturePatches(featureName, reference, owner, repo, featureOwner, featureArgs);
166068
- }
166069
166722
  async function prepareFeature(featureName, version, repo = 'features', owner = 'prefapp') {
166070
166723
  await downloadFeatureZip(repo, featureName, version, owner);
166071
166724
  }
@@ -166120,9 +166773,6 @@ async function downloadFeatureZip(repo, featureName, reference, owner = 'prefapp
166120
166773
  /* harmony default export */ const src = ({
166121
166774
  renderFeature: renderFeature,
166122
166775
  renderFeatureFromPath: renderFeatureFromPath,
166123
- getFeaturePatches: installer_getFeaturePatches,
166124
- getFeaturePatchesFromRef: getFeaturePatchesFromRef,
166125
- getFeaturePatchesFromPath: getFeaturePatchesFromPath,
166126
166776
  getFeatureConfigFromRef: getFeatureConfigFromRef,
166127
166777
  getFeatureConfig: getFeatureConfig,
166128
166778
  prepareFeature: prepareFeature,
@@ -166139,1074 +166789,6 @@ async function downloadFeatureZip(repo, featureName, reference, owner = 'prefapp
166139
166789
  getFeatureConfigFromRef: src.getFeatureConfigFromRef,
166140
166790
  });
166141
166791
 
166142
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/stitching/protectedPaths.ts
166143
- // Platform-curated protected claim paths — features may not write to these.
166144
- // Stored as JSON Pointers (RFC 6901). Supports wildcard `*` for a single segment.
166145
- // Example: /providers/STAR/org matches /providers/github/org where STAR is *.
166146
- const PROTECTED_CLAIM_PATHS = [
166147
- '/kind',
166148
- '/name',
166149
- '/providers/*/org',
166150
- '/providers/github/org',
166151
- '/providers/github/name',
166152
- '/metadata/name',
166153
- '/metadata/namespace',
166154
- ];
166155
- function isProtectedPath(patchPath) {
166156
- const normalizedPatch = normalizePointer(patchPath);
166157
- for (const protectedPath of PROTECTED_CLAIM_PATHS) {
166158
- if (matchesProtected(protectedPath, normalizedPatch)) {
166159
- return true;
166160
- }
166161
- }
166162
- return false;
166163
- }
166164
- function normalizePointer(pointer) {
166165
- if (!pointer.startsWith('/')) {
166166
- return '/' + pointer;
166167
- }
166168
- return pointer;
166169
- }
166170
- function matchesProtected(protectedPointer, patchPointer) {
166171
- const protectedSegments = protectedPointer
166172
- .split('/')
166173
- .slice(1)
166174
- .map(unescapePointer);
166175
- const patchSegments = patchPointer.split('/').slice(1).map(unescapePointer);
166176
- // Treat ancestor/descendant as protected: a patch to a parent (e.g. /metadata)
166177
- // would replace the protected child (/metadata/name), and a patch to a child
166178
- // (e.g. /metadata/name/extra) is also under the protected tree. Match when
166179
- // the shorter prefix equals the longer prefix (with wildcard support).
166180
- const minLen = Math.min(protectedSegments.length, patchSegments.length);
166181
- for (let i = 0; i < minLen; i++) {
166182
- if (protectedSegments[i] === '*')
166183
- continue;
166184
- if (protectedSegments[i] !== patchSegments[i])
166185
- return false;
166186
- }
166187
- return true;
166188
- }
166189
- function unescapePointer(segment) {
166190
- return segment.replace(/~1/g, '/').replace(/~0/g, '~');
166191
- }
166192
-
166193
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/stitching/errors.ts
166194
- class StitchingError extends Error {
166195
- constructor(violations) {
166196
- const lines = violations.map((v) => ` - ${v.message}`).join('\n');
166197
- super(`Claim stitching failed with ${violations.length} violation(s):\n${lines}`);
166198
- this.name = 'StitchingError';
166199
- this.violations = violations;
166200
- }
166201
- }
166202
- function formatStitchingError(error) {
166203
- return error.message;
166204
- }
166205
-
166206
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/claims/stitching/stitching.ts
166207
-
166208
-
166209
-
166210
-
166211
- let MOCK_FEATURES_FN;
166212
- function MOCK_STITCHING_FEATURES(mock) {
166213
- MOCK_FEATURES_FN = mock;
166214
- }
166215
- function pointerExists(doc, pointer) {
166216
- const segments = pointer
166217
- .split('/')
166218
- .slice(1)
166219
- .map((s) => s.replace(/~1/g, '/').replace(/~0/g, '~'));
166220
- let cur = doc;
166221
- for (const seg of segments) {
166222
- if (cur === null || cur === undefined || typeof cur !== 'object')
166223
- return false;
166224
- if (!Object.prototype.hasOwnProperty.call(cur, seg))
166225
- return false;
166226
- cur = cur[seg];
166227
- }
166228
- return true;
166229
- }
166230
- function isAppendPointer(pointer) {
166231
- return pointer.endsWith('/-');
166232
- }
166233
- function isRecord(value) {
166234
- return typeof value === 'object' && value !== null && !Array.isArray(value);
166235
- }
166236
- // Deduplication identity for a feature patch. Ordinary writes collide on the
166237
- // normalized pointer. Array appends (`add ... /-`) collide on the pointer plus
166238
- // the appended element's `name` when present, so several features can each add
166239
- // a distinct label to /providers/github/labels/- while the same label name from
166240
- // two features still hard-fails.
166241
- function writeIdentity(patch) {
166242
- const path = normalizePointer(patch.path);
166243
- if (patch.op === 'add' &&
166244
- isAppendPointer(path) &&
166245
- isRecord(patch.value) &&
166246
- typeof patch.value.name === 'string') {
166247
- return `${path}#${patch.value.name}`;
166248
- }
166249
- return path;
166250
- }
166251
- // fast-json-patch does not create intermediate containers for `add`, and
166252
- // appending to an array requires the array to already exist. Ensure every
166253
- // ancestor segment exists before applying an `add`: missing containers are
166254
- // created as objects, except the immediate parent of a `/-` append, which is
166255
- // created as an array. Existing (user/default-declared) values are never
166256
- // overwritten.
166257
- function ensureAddParents(doc, patch) {
166258
- const segments = patch.path.split('/').slice(1);
166259
- let prefix = '';
166260
- for (let i = 0; i < segments.length - 1; i++) {
166261
- prefix += '/' + segments[i];
166262
- if (pointerExists(doc, prefix))
166263
- continue;
166264
- const isArrayParent = isAppendPointer(patch.path) && i === segments.length - 2;
166265
- const value = isArrayParent ? [] : {};
166266
- try {
166267
- doc = fast_json_patch.applyPatch(doc, [{ op: 'add', path: prefix, value }], false, false).newDocument;
166268
- }
166269
- catch {
166270
- // Concurrent structural conflict — let the actual patch surface the error.
166271
- }
166272
- }
166273
- return doc;
166274
- }
166275
- async function stitchClaim(claimAfterDefaults, preCollectedEnvelopes) {
166276
- const envelopes = preCollectedEnvelopes ?? (await collectFeaturePatches(claimAfterDefaults));
166277
- if (envelopes.length === 0) {
166278
- return claimAfterDefaults;
166279
- }
166280
- const violations = [];
166281
- const seenPaths = new Map();
166282
- // Collect all patches for deduplication before any gate — normalize once so all gates and apply use same pointer
166283
- const normalizedEnvelopes = envelopes.map((env) => ({
166284
- feature: env.feature,
166285
- patches: env.patches.map((p) => ({
166286
- ...p,
166287
- path: normalizePointer(p.path),
166288
- _raw: p.path,
166289
- })),
166290
- }));
166291
- for (const envelope of normalizedEnvelopes) {
166292
- for (const patch of envelope.patches) {
166293
- const path = patch.path;
166294
- const raw = patch._raw ?? path;
166295
- // Gate 1: protected path
166296
- if (isProtectedPath(path)) {
166297
- violations.push({
166298
- feature: envelope.feature,
166299
- op: patch.op,
166300
- path,
166301
- reason: 'protected-path',
166302
- message: `Feature '${envelope.feature}' patch ${patch.op} ${raw} (normalized ${path}) targets protected claim path`,
166303
- });
166304
- // continue to collect other violations for same patch (but avoid duplicate counting)
166305
- }
166306
- // Gate 3: deduplication — track before overwrite so we report both.
166307
- // Array appends (`/-`) dedup by element name (see writeIdentity); all
166308
- // other writes dedup by normalized pointer.
166309
- const identity = writeIdentity(patch);
166310
- if (seenPaths.has(identity)) {
166311
- violations.push({
166312
- feature: envelope.feature,
166313
- op: patch.op,
166314
- path,
166315
- reason: 'duplicate-path',
166316
- conflictingFeature: seenPaths.get(identity),
166317
- message: `Feature '${envelope.feature}' and '${seenPaths.get(identity)}' both write to ${path} (deduplication)`,
166318
- });
166319
- }
166320
- else {
166321
- seenPaths.set(identity, envelope.feature);
166322
- }
166323
- // Gate 2: overwrite protection (II-a strict: raw ∪ defaults counts as declared)
166324
- if (['replace', 'remove', 'add'].includes(patch.op)) {
166325
- const exists = pointerExists(claimAfterDefaults, path);
166326
- if (exists) {
166327
- // For strict II-a, any op to existing path is overwrite
166328
- // We already reported protected if applicable; still report overwrite unless it was protected
166329
- // to keep collect-all but avoid double-counting same patch as two reasons? Keep both for visibility.
166330
- if (!isProtectedPath(path)) {
166331
- violations.push({
166332
- feature: envelope.feature,
166333
- op: patch.op,
166334
- path,
166335
- reason: 'overwrite-protected',
166336
- message: `Feature '${envelope.feature}' patch ${patch.op} ${raw} (normalized ${path}) would overwrite user/default-declared value`,
166337
- });
166338
- }
166339
- }
166340
- else {
166341
- // Path absent — check intermediate parent existence? For now exact path only.
166342
- // If patch is `replace` to absent path, fast-json-patch will fail; we let that be an overwrite concern too.
166343
- if (['replace', 'remove'].includes(patch.op)) {
166344
- violations.push({
166345
- feature: envelope.feature,
166346
- op: patch.op,
166347
- path,
166348
- reason: 'overwrite-protected',
166349
- message: `Feature '${envelope.feature}' patch ${patch.op} ${raw} (normalized ${path}) targets absent path (replace/remove requires existing value)`,
166350
- });
166351
- }
166352
- }
166353
- }
166354
- }
166355
- }
166356
- if (violations.length > 0) {
166357
- throw new StitchingError(violations);
166358
- }
166359
- // No violations — apply patches sequentially (deduplication already ensures no conflicts)
166360
- let stitched = JSON.parse(JSON.stringify(claimAfterDefaults));
166361
- for (const envelope of normalizedEnvelopes) {
166362
- if (envelope.patches.length === 0)
166363
- continue;
166364
- for (const patch of envelope.patches) {
166365
- if (patch.op === 'add') {
166366
- stitched = ensureAddParents(stitched, patch);
166367
- }
166368
- }
166369
- const result = fast_json_patch.applyPatch(stitched, envelope.patches, false, false);
166370
- stitched = result.newDocument;
166371
- }
166372
- return stitched;
166373
- }
166374
- async function collectFeaturePatches(claim) {
166375
- if (MOCK_FEATURES_FN) {
166376
- return MOCK_FEATURES_FN(claim);
166377
- }
166378
- const typedClaim = claim;
166379
- const features = typedClaim.providers?.github?.features ?? [];
166380
- if (features.length === 0)
166381
- return [];
166382
- // In unit tests without GitHub credentials, avoid real download — tests that care
166383
- // about stitching should set MOCK_STITCHING_FEATURES explicitly.
166384
- if (process.env.JEST_WORKER_ID && !process.env.GITHUB_TOKEN) {
166385
- return [];
166386
- }
166387
- const envelopes = [];
166388
- for (const feature of features) {
166389
- const featureName = feature.name;
166390
- const versionOrRef = feature.ref ?? feature.version ?? '';
166391
- const repo = feature.repo
166392
- ? (feature.repo.split('/')[1] ?? 'features')
166393
- : 'features';
166394
- const owner = feature.repo
166395
- ? (feature.repo.split('/')[0] ?? 'prefapp')
166396
- : 'prefapp';
166397
- const args = feature.args ?? {};
166398
- try {
166399
- let patchesOrRendered;
166400
- const preparerAny = features_preparer;
166401
- if (feature.ref) {
166402
- if (typeof preparerAny.getFeaturePatchesFromRef === 'function') {
166403
- patchesOrRendered = await preparerAny.getFeaturePatchesFromRef(featureName, versionOrRef, claim, args, repo, owner);
166404
- }
166405
- else {
166406
- patchesOrRendered = await preparerAny.getFeatureConfigFromRef(featureName, versionOrRef, claim, args, repo, owner);
166407
- }
166408
- }
166409
- else {
166410
- if (typeof preparerAny.getFeaturePatches === 'function') {
166411
- patchesOrRendered = await preparerAny.getFeaturePatches(featureName, versionOrRef, claim, args, repo, owner);
166412
- }
166413
- else {
166414
- patchesOrRendered = await preparerAny.getFeatureConfig(featureName, versionOrRef, claim, args, repo, owner);
166415
- }
166416
- }
166417
- const patches = Array.isArray(patchesOrRendered)
166418
- ? patchesOrRendered
166419
- : extractClaimPatches(patchesOrRendered);
166420
- envelopes.push({ feature: featureName, patches });
166421
- }
166422
- catch (err) {
166423
- throw new Error(`Failed to collect patches for feature '${featureName}': ${err instanceof Error ? err.message : String(err)}`);
166424
- }
166425
- }
166426
- return envelopes;
166427
- }
166428
- function extractClaimPatches(rendered) {
166429
- const patches = rendered.patches;
166430
- if (!patches)
166431
- return [];
166432
- if (Array.isArray(patches))
166433
- return patches;
166434
- throw new Error(`Feature patches must be a flat array (claim-relative pointers like /annotations/...), got ${typeof patches}: ${JSON.stringify(patches).slice(0, 200)}. See #2648`);
166435
- }
166436
-
166437
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/lazy_loader.ts
166438
-
166439
-
166440
-
166441
-
166442
-
166443
-
166444
-
166445
-
166446
-
166447
-
166448
-
166449
-
166450
-
166451
-
166452
-
166453
-
166454
-
166455
- // We need to ensure the grep command is a gnu version
166456
- let IS_GNU_GREP = false;
166457
- let GREP_ON_CHECK = false;
166458
- // this loop is for avoid race conditions
166459
- // once a check is running no more checks
166460
- async function waitForCheck() {
166461
- const f_wait = () => new Promise((timeOut) => setTimeout(timeOut, 10));
166462
- do {
166463
- await f_wait();
166464
- } while (GREP_ON_CHECK);
166465
- }
166466
- function checkGrep() {
166467
- return new Promise((ok, ko) => {
166468
- // idempotency: already checked
166469
- if (IS_GNU_GREP)
166470
- return ok();
166471
- // a check is already running no new checks
166472
- if (GREP_ON_CHECK)
166473
- return waitForCheck();
166474
- logger.info('Checking the grep command');
166475
- GREP_ON_CHECK = true;
166476
- const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['--version']);
166477
- handler.on('close', (code) => {
166478
- GREP_ON_CHECK = false;
166479
- if (code === 0) {
166480
- logger.debug('Grep is a gnu grep');
166481
- IS_GNU_GREP = true;
166482
- ok();
166483
- }
166484
- else {
166485
- ko('Lazy evaluation needs a gnu-grep command');
166486
- }
166487
- });
166488
- });
166489
- }
166490
- async function loadClaim(claimRef, org, defaults = loadClaimDefaults(), patchClaim = loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, existingRefs = {}, postValidations = new Map()) {
166491
- await checkGrep();
166492
- let result = existingRefs;
166493
- logger.info(`Load reference ${claimRef}`);
166494
- initVirtualClaims(org);
166495
- logger.info(`Load reference (parts) ${claimRef.split(/-/)[0]} ${claimRef.replace(/^[^-]+-/, '')}`);
166496
- // cargas datos con grep
166497
- try {
166498
- const claimData = await lazyGetClaim(claimRef.split(/-/)[0], claimRef.replace(/^[^-]+-/, ''), org, cwd);
166499
- let claim = patchClaim(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(claimData), defaults);
166500
- // Claim stitching: feature claim-patches with three gates before AJV
166501
- try {
166502
- claim = await stitchClaim(claim);
166503
- }
166504
- catch (e) {
166505
- throw new Error(`Error when stitching claim ${claimRef}: ${e instanceof Error ? e.message : String(e)}`);
166506
- }
166507
- let variants = [];
166508
- if (claim.kind === 'TFWorkspaceClaim' &&
166509
- claim.providers?.terraform?.variants) {
166510
- variants = claim.providers.terraform.variants;
166511
- delete claim.providers.terraform.variants;
166512
- }
166513
- logger.silly(`Patched claim is:
166514
- ---
166515
- ${catalog_common/* default.io.toYaml */.Z.io.toYaml(claim)}`);
166516
- try {
166517
- validateClaim(claim, base[`${claim.kind}Schema`]);
166518
- await optionalValidation(claim);
166519
- }
166520
- catch (error) {
166521
- let errorMsg = '';
166522
- if (Array.isArray(error)) {
166523
- for (const data of error) {
166524
- errorMsg = `${errorMsg}
166525
- - ${data.instancePath}: ${data.message}`;
166526
- }
166527
- }
166528
- else {
166529
- errorMsg = error instanceof Error ? error.message : String(error);
166530
- }
166531
- throw new Error(`Error when validating claim ${claimRef}: ${errorMsg}`);
166532
- }
166533
- result[claimRef] = {};
166534
- result[claimRef]['claim'] = claim;
166535
- result[claimRef]['claimPath'] = VisitedClaims[claimRef];
166536
- if (VisitedClaims[claimRef] === 'virtual') {
166537
- result = await setVirtualClaimAdditionalData(result, claim, claimRef);
166538
- }
166539
- else {
166540
- result = await setNonVirtualClaimAdditionalData(result, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
166541
- }
166542
- const claimKind = claim.kind;
166543
- const references = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(claim));
166544
- for (const ref of references) {
166545
- if (!result[ref]) {
166546
- const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
166547
- result = lodash_default().merge(result, resolvedReferences);
166548
- }
166549
- }
166550
- if (variants.length > 0) {
166551
- const parentClaimPath = VisitedClaims[claimRef];
166552
- for (const variant of variants) {
166553
- if (!variant.name || !variant.overrides) {
166554
- throw new Error(`Variant in claim ${claimRef} is missing required field 'name' or 'overrides'`);
166555
- }
166556
- const prohibitedOverrideFields = ['source', 'module', 'name'];
166557
- for (const field of prohibitedOverrideFields) {
166558
- if (field in variant.overrides) {
166559
- throw new Error(`Variant '${variant.name}' in claim ${claimRef} cannot override '${field}'`);
166560
- }
166561
- }
166562
- const composedName = claim.providers.terraform.name + '-' + variant.name;
166563
- const variantClaim = lodash_default().cloneDeep(claim);
166564
- variantClaim._parentClaimName = claim.name;
166565
- variantClaim.name = composedName;
166566
- variantClaim.providers.terraform = lodash_default().merge({}, claim.providers.terraform, variant.overrides);
166567
- variantClaim.providers.terraform.name = composedName;
166568
- if (!variantClaim.annotations) {
166569
- variantClaim.annotations = {};
166570
- }
166571
- variantClaim.annotations['firestartr.dev/variant-of'] =
166572
- claim.providers.terraform.name;
166573
- const variantRef = `TFWorkspaceClaim-${composedName}`;
166574
- if (result[variantRef]) {
166575
- throw new Error(`Variant name '${variant.name}' (composed: '${composedName}') from claim '${claimRef}' conflicts with existing claim '${variantRef}'`);
166576
- }
166577
- logger.info(`Creating synthetic variant claim ${variantRef}`);
166578
- result[variantRef] = {};
166579
- result[variantRef]['claim'] = variantClaim;
166580
- result[variantRef]['claimPath'] = parentClaimPath;
166581
- result = await setNonVirtualClaimAdditionalData(result, variantClaim, variantRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers);
166582
- const variantReferences = extractAllRefs(catalog_common/* default.io.toYaml */.Z.io.toYaml(variantClaim));
166583
- for (const ref of variantReferences) {
166584
- if (!result[ref]) {
166585
- const [resolvedReferences] = await loadClaim(ref, org, defaults, patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, cwd, result, postValidations);
166586
- result = lodash_default().merge(result, resolvedReferences);
166587
- }
166588
- }
166589
- }
166590
- }
166591
- }
166592
- catch (err) {
166593
- throw `Lazy Loading: ${err}`;
166594
- }
166595
- return [result, postValidations];
166596
- }
166597
- let LoadedClaims = {};
166598
- let VisitedClaims = {};
166599
- let DuplicatedClaims = {};
166600
- function resetLazyLoader() {
166601
- LoadedClaims = {};
166602
- VisitedClaims = {};
166603
- DuplicatedClaims = {};
166604
- }
166605
- async function lazyGetClaim(kind, name, org, cwd) {
166606
- const indice = `${kind}-${name}`;
166607
- logger.info(`Lazy loading ${kind}-${name} with index ${indice}`);
166608
- if (indice in LoadedClaims)
166609
- return LoadedClaims[indice];
166610
- if (isVirtualClaim(kind, name)) {
166611
- await loadVirtualClaim(kind, name, org);
166612
- }
166613
- else {
166614
- await getClaimsByName(name, cwd);
166615
- }
166616
- if (indice in LoadedClaims) {
166617
- return LoadedClaims[indice];
166618
- }
166619
- else {
166620
- throw new Error(`Error: ${kind}-${name} not found`);
166621
- }
166622
- }
166623
- async function getClaimsByName(name, cwd = '.') {
166624
- return new Promise((ok, ko) => {
166625
- const handler = (0,external_node_child_process_namespaceObject.spawn)('grep', ['-r', '-l', '--include=*', '-E', `name: "?${name}"?`, '.'], {
166626
- cwd: cwd,
166627
- });
166628
- logger.info(`Running ${['grep', '-r', '-l', '--include', '-E', 'name: "' + name + '"?', '.'].join(' ')}`);
166629
- const entradas = [];
166630
- const chunks = [];
166631
- handler.stdout.on('data', (data) => {
166632
- chunks.push(data.toString('utf-8'));
166633
- });
166634
- handler.on('close', () => {
166635
- const dataFinal = chunks.join('');
166636
- const fileNameList = dataFinal.split(/\n/).filter((line) => line !== '');
166637
- for (const fileName of fileNameList) {
166638
- // only yaml files
166639
- if (!fileName.match(/\.yml$|\.yaml$/)) {
166640
- continue;
166641
- }
166642
- // only entries not already visited
166643
- if (Object.values(VisitedClaims).includes(fileName)) {
166644
- continue;
166645
- }
166646
- entradas.push(external_path_.join(cwd, fileName));
166647
- }
166648
- return Promise.all(entradas.map((entrada) => {
166649
- return loadRawClaim(entrada);
166650
- }))
166651
- .then(() => {
166652
- ok();
166653
- })
166654
- .catch((err) => {
166655
- ko(err);
166656
- });
166657
- });
166658
- });
166659
- }
166660
- async function loadRawClaim(entry) {
166661
- return new Promise((ok, ko) => {
166662
- external_fs_.readFile(entry, 'utf-8', (error, data) => {
166663
- if (error)
166664
- return ko(`Reading ${entry}: ${error}`);
166665
- const claim = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
166666
- if (!('kind' in claim && 'name' in claim)) {
166667
- logger.warn(`Invalid claim file ${entry}`);
166668
- }
166669
- else {
166670
- const claimKey = `${claim.kind}-${claim.name}`;
166671
- if (Object.keys(DuplicatedClaims).includes(claimKey) &&
166672
- DuplicatedClaims[claimKey] !== entry) {
166673
- return ko(`Duplicated claim ${claimKey} found files: ${DuplicatedClaims[claimKey]} and ${entry}`);
166674
- }
166675
- LoadedClaims[claimKey] = data;
166676
- VisitedClaims[claimKey] = entry;
166677
- DuplicatedClaims[claimKey] = entry;
166678
- ok();
166679
- }
166680
- });
166681
- });
166682
- }
166683
- async function setVirtualClaimAdditionalData(renderedData, claim, claimRef) {
166684
- const virtualClaim = getVirtualClaim(claim.kind, claim.name);
166685
- renderedData[claimRef]['initializers'] = [
166686
- new UUIDInitializer(claim),
166687
- new InitializerClaimRef(),
166688
- new InitializerDefault(virtualClaim.getDefaultConfig()),
166689
- ];
166690
- renderedData[claimRef]['globals'] = [];
166691
- renderedData[claimRef]['overrides'] = [];
166692
- renderedData[claimRef]['normalizers'] = [new NameNormalizer()];
166693
- return renderedData;
166694
- }
166695
- async function setNonVirtualClaimAdditionalData(renderedData, claim, claimRef, loadInitializers, loadGlobals, loadOverrides, loadNormalizers) {
166696
- renderedData[claimRef]['initializers'] = await loadInitializers(claim, renderedData[claimRef]['claimPath']);
166697
- renderedData[claimRef]['globals'] = await loadGlobals(claim);
166698
- renderedData[claimRef]['overrides'] = loadOverrides(claim);
166699
- renderedData[claimRef]['normalizers'] = await loadNormalizers(claim, renderedData[claimRef]['claimPath']);
166700
- return renderedData;
166701
- }
166702
- async function loadVirtualClaim(kind, name, org) {
166703
- const virtualClaim = getVirtualClaim(kind, name);
166704
- const expandedClaim = await virtualClaim.expand({ org });
166705
- logger.info(`Loading virtual claim ${kind} ${name} ${org}`);
166706
- LoadedClaims[`${kind}-${name}`] = catalog_common/* default.io.toYaml */.Z.io.toYaml(expandedClaim);
166707
- VisitedClaims[`${kind}-${name}`] = 'virtual';
166708
- }
166709
-
166710
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/claimsDefaulter.ts
166711
-
166712
-
166713
-
166714
- /*
166715
- * Default blocks are objects that cannot be merged but applied as a whole. Meaning:
166716
- * - If the object is defined at the claim-level it is maintained *AS IT IS* (no merge)
166717
- * - If the block is missing at the claim-level, the default block is applied
166718
- */
166719
- const defaultBlocksPaths = ['/providers/terraform/sync'];
166720
- function applyBlockAwareDefaults(claim, defaultClaim) {
166721
- // we always work with a clone of the original
166722
- const claimClone = JSON.parse(JSON.stringify(claim));
166723
- // if the block is claim-level defined we store it here
166724
- const defaultBlocks = new Map();
166725
- // we remove the blocks entirely of the claim
166726
- // to achieve an add copy
166727
- for (const defaultBlockPath of defaultBlocksPaths) {
166728
- if (claimsDefaulter_hasDeepPath(claimClone, defaultBlockPath) !== true)
166729
- continue;
166730
- const originalValue = fast_json_patch_default().getValueByPointer(claimClone, defaultBlockPath);
166731
- if (originalValue) {
166732
- logger.silly(`${claim.kind}/${claim.name}: has an original ${defaultBlockPath}: preserving`);
166733
- defaultBlocks.set(defaultBlockPath, originalValue);
166734
- fast_json_patch_default().applyPatch(claimClone, [{ op: 'remove', path: defaultBlockPath }]);
166735
- }
166736
- }
166737
- // if the patch has the same path of a block
166738
- // and the block was defined at claim-level
166739
- // we interchange the value with the original value
166740
- // otherwise we use the default
166741
- const jsonPatchOps = fast_json_patch_default().compare(claimClone, defaultClaim)
166742
- .filter((jp) => jp.op === 'add')
166743
- .map((jp) => {
166744
- if (defaultBlocks.has(jp.path)) {
166745
- return {
166746
- ...jp,
166747
- value: defaultBlocks.get(jp.path),
166748
- };
166749
- }
166750
- else {
166751
- return jp;
166752
- }
166753
- });
166754
- return fast_json_patch_default().applyPatch(claimClone, jsonPatchOps).newDocument;
166755
- }
166756
- function claimsDefaulter_hasDeepPath(data, deepPath) {
166757
- return lodash_default().has(data, deepPath.replace(/\//g, '.').replace(/^\./, ''));
166758
- }
166759
-
166760
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/loader/loader.ts
166761
-
166762
-
166763
-
166764
-
166765
-
166766
-
166767
-
166768
-
166769
-
166770
-
166771
-
166772
-
166773
-
166774
-
166775
-
166776
- const isYamlFile = new RegExp(/\.(yaml|yml)$/);
166777
- /**
166778
- *
166779
- * @returns Virtual Claims
166780
- */
166781
- const virtualClaims = [new FirestartrAllClaim()];
166782
- /*
166783
- * Loads all applicable globals for a given claim and stores them into a list.
166784
- *
166785
- * Input:
166786
- * - claim: our custom claim object, with which we check if the global is applicable
166787
- *
166788
- * Returns:
166789
- * - A GlobalSection array promise, containing all the globals loaded for this claim
166790
- *
166791
- */
166792
- async function loadGlobals(claim) {
166793
- const result = [];
166794
- await crawl(getPath('globals'), (entry) => {
166795
- return isYamlFile.test(entry);
166796
- }, async (entry, data) => {
166797
- const globalData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
166798
- const ext = external_path_.extname(entry);
166799
- const name = external_path_.basename(entry, ext);
166800
- if (name in SECTIONS_BY_FILE_NAME) {
166801
- const section = new SECTIONS_BY_FILE_NAME[name](globalData);
166802
- if (!(await section.validate(SCHEMAS_BY_SECTION_NAME[name] || {}))) {
166803
- throw new Error(`Invalid ${name} global file`);
166804
- }
166805
- if (globalData.kind === claim.kind) {
166806
- result.push(section);
166807
- }
166808
- else {
166809
- logger.debug(`Skipping global ${name} because it is not applicable to ${claim.kind}`);
166810
- }
166811
- }
166812
- });
166813
- logger.info(`Loaded globals ${result}`);
166814
- return result;
166815
- }
166816
- /*
166817
- * If the input claim is a GithubRepositoryClaim, it loads all it's applicable
166818
- * overrides and stores them into a list. Otherwise, it returns an empty list.
166819
- *
166820
- * Input:
166821
- * - claim: our custom claim object, which should be a GithubRepositoryClaim
166822
- *
166823
- * Returns:
166824
- * - An Overrider array, with a GithubRepositoryOverrider if the claim
166825
- * was a GithubRepositoryClaim, or empty if it was not.
166826
- *
166827
- */
166828
- function loadOverrides(claim) {
166829
- const overrides = [];
166830
- if (claim.kind === 'ComponentClaim' && claim.providers.github.overrides) {
166831
- overrides.push(new GithubRepositoryOverrider());
166832
- }
166833
- return overrides;
166834
- }
166835
- /**
166836
- * Load all normalizers and stores them into a list.
166837
- * @param claim
166838
- * @returns
166839
- */
166840
- async function loadNormalizers(_claim, path) {
166841
- const result = [];
166842
- for (const normalizer of NORMALIZERS) {
166843
- result.push(new normalizer({ path: path }));
166844
- }
166845
- return result;
166846
- }
166847
- /*
166848
- * Loads all applicable initializers for a given claim and stores them into a list.
166849
- *
166850
- * Input:
166851
- * - claim: our custom claim object, with which we check if the initializer is applicable
166852
- *
166853
- * Returns:
166854
- * - An Initializer array promise, containing all the initializers loaded for this claim
166855
- *
166856
- */
166857
- let loadedInitializers = false;
166858
- async function loadInitializers(claim, claimPath, initializersPath = getPath('initializers')) {
166859
- const result = [];
166860
- await crawl(initializersPath, (entry) => {
166861
- return isYamlFile.test(entry);
166862
- }, async (entry, data) => {
166863
- const initializerData = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data);
166864
- const ext = external_path_.extname(entry);
166865
- const name = external_path_.basename(entry, ext);
166866
- if (name in INITIALIZERS_BY_FILE_NAME) {
166867
- const initializerBfN = new INITIALIZERS_BY_FILE_NAME[name](initializerData);
166868
- if (!(await initializerBfN.validate(SCHEMAS_BY_INITIALIZER_NAME[name] || {}))) {
166869
- throw new Error(`Invalid ${name} initializer file`);
166870
- }
166871
- if (initializerData.kind === claim.kind) {
166872
- result.push(initializerBfN);
166873
- }
166874
- else {
166875
- logger.debug(`Skipping initializer ${name} because it is not applicable to ${claim.kind}`);
166876
- }
166877
- }
166878
- });
166879
- for (const initializer of INITIALIZERS) {
166880
- if (initializer.applicableKinds.includes(claim.kind)) {
166881
- result.push(new initializer({ path: claimPath }));
166882
- }
166883
- }
166884
- logger.info(`Loaded initializers ${JSON.stringify(result)}`);
166885
- loadedInitializers = result;
166886
- return loadedInitializers;
166887
- }
166888
- function loadClaimDefaults() {
166889
- try {
166890
- return catalog_common/* default.io.fromYaml */.Z.io.fromYaml(external_fs_.readFileSync(`${getPath('claimsDefaults')}/claims_defaults.yaml`, 'utf-8'));
166891
- }
166892
- catch (error) {
166893
- const errMsg = `Could not read claims defaults file in path ${getPath('claimsDefaults')}/claims_defaults.yaml: ${error}`;
166894
- logger.error(errMsg);
166895
- throw new Error(errMsg);
166896
- }
166897
- }
166898
- /*
166899
- * Using fast-json-patch, compares a claim to its kind's defaults, then patches it.
166900
- *
166901
- * Input:
166902
- * - claim: our custom claim object, which we want to patch
166903
- * - defaultsClaims: an object, containing the defaults for each claim kind,
166904
- * with the same structure that we use for the actual claims
166905
- *
166906
- * Returns:
166907
- * - The patched claim
166908
- *
166909
- */
166910
- function loader_patchClaim(claim, defaultsClaims) {
166911
- if (defaultsClaims[claim.kind]) {
166912
- claim = applyBlockAwareDefaults(claim, defaultsClaims[claim.kind]);
166913
- }
166914
- return claim;
166915
- }
166916
- async function loadCRsWithFullSet(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
166917
- const result = {};
166918
- const fullResult = {};
166919
- await crawlWithExclusions(crsPath, (entry) => {
166920
- return isYamlFile.test(entry);
166921
- }, async (entry, data) => {
166922
- const yamlData = stripKnownRelations(catalog_common/* default.io.fromYaml */.Z.io.fromYaml(data));
166923
- const metadata = yamlData?.metadata;
166924
- if (!(yamlData &&
166925
- typeof yamlData === 'object' &&
166926
- 'kind' in yamlData &&
166927
- metadata &&
166928
- typeof metadata === 'object' &&
166929
- 'name' in metadata)) {
166930
- logger.warn(`Invalid CR file ${entry}`);
166931
- }
166932
- else {
166933
- const crKey = `${yamlData.kind}-${metadata.name}`;
166934
- if (fullResult[crKey]) {
166935
- logger.error(`Duplicate CR ${crKey}`);
166936
- }
166937
- // Keep the complete CR set for validations that need repository-wide context.
166938
- fullResult[crKey] = yamlData;
166939
- // Keep the filtered CR set for previous-CR lookup during claim rendering.
166940
- if (allowedClaimReferences.length === 0 ||
166941
- allowedClaimReferences.includes(yamlData.metadata?.annotations?.[catalog_common/* default.generic.getFirestartrAnnotation */.Z.generic.getFirestartrAnnotation('claim-ref')])) {
166942
- result[crKey] = yamlData;
166943
- }
166944
- }
166945
- }, excludedPaths.concat(getAdditionalPaths()));
166946
- return { crs: result, fullCrs: fullResult };
166947
- }
166948
- async function loadCRs(crsPath = getPath('crs'), excludedPaths = [getPath('globals'), getPath('initializers')], allowedClaimReferences = []) {
166949
- const { crs } = await loadCRsWithFullSet(crsPath, excludedPaths, allowedClaimReferences);
166950
- return crs;
166951
- }
166952
- /*
166953
- * Description: Get the renamed using claimRef annotation
166954
- * Input: claims: any, crs: any
166955
- * Output: renames: IRenameResult[]
166956
- */
166957
- async function loadRenames(claims, crs) {
166958
- /*
166959
- * If checkRenames is false, we don't need to check for renames
166960
- */
166961
- const renamesEnabled = getRenamesEnabled();
166962
- if (!renamesEnabled)
166963
- return [];
166964
- const result = [];
166965
- /**
166966
- * For each CR, look for a claim that does not match the name with CR's firestartr.dev/claim-ref annotation
166967
- */
166968
- for (const cr of Object.values(crs)) {
166969
- const anyCr = cr;
166970
- if (!anyCr?.metadata?.annotations)
166971
- continue;
166972
- // if the annotation is a claimRef
166973
- const annotationName = common.generic.getFirestartrAnnotation('claim-ref');
166974
- const annotations = anyCr?.metadata?.annotations || {};
166975
- if (annotationName in annotations) {
166976
- const claimRef = anyCr.metadata.annotations[annotationName];
166977
- // split the annotation into kind and name
166978
- const [kind, name] = claimRef.split('/');
166979
- // if the claim exists, check if the name is different for the given provider
166980
- const referencedClaim = claims[`${kind}-${name}`].claim;
166981
- const oldName = anyCr.metadata.name;
166982
- const newName = referencedClaim.providers[getConfiguredProvider()].name;
166983
- if (oldName !== newName) {
166984
- result.push({
166985
- kind: referencedClaim.kind,
166986
- claimPath: claims[`${kind}-${name}`].claimPath,
166987
- claimName: referencedClaim.name,
166988
- oldName,
166989
- newName,
166990
- });
166991
- }
166992
- }
166993
- }
166994
- return result;
166995
- }
166996
- async function loadClaimsList(claimRefList, claimsPath = getPath('claims')) {
166997
- const data = {
166998
- renderClaims: {},
166999
- crs: {},
167000
- fullCrs: {},
167001
- };
167002
- const defaults = loadClaimDefaults();
167003
- for await (const claimRef of claimRefList) {
167004
- const [renderedClaimData] = await loadClaim(claimRef, getOrg(), defaults, loader_patchClaim, loadInitializers, loadGlobals, loadOverrides, loadNormalizers, claimsPath);
167005
- data.renderClaims = lodash_default().merge(data.renderClaims, renderedClaimData);
167006
- }
167007
- const crClaimReferences = [];
167008
- for (const ref of Object.keys(data.renderClaims)) {
167009
- // Replaces only the first instance of '-'
167010
- crClaimReferences.push(ref.replace('-', '/'));
167011
- }
167012
- const loadedCrs = await loadCRsWithFullSet(getPath('crs'), [getPath('globals'), getPath('initializers')], crClaimReferences);
167013
- data['crs'] = loadedCrs.crs;
167014
- data['fullCrs'] = loadedCrs.fullCrs;
167015
- return data;
167016
- }
167017
- function getOrg() {
167018
- const groupOrg = catalog_common/* default.environment.getFromEnvironmentWithDefault */.Z.environment.getFromEnvironmentWithDefault(catalog_common/* default.types.envVars.org */.Z.types.envVars.org, '');
167019
- return groupOrg;
167020
- }
167021
-
167022
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/references.ts
167023
- function validateTfStateKeyUniqueness(crs) {
167024
- const tfStateKeys = {};
167025
- for (const crKey of Object.keys(crs)) {
167026
- const cr = crs[crKey];
167027
- if (!isTerraformWorkspace(cr))
167028
- continue;
167029
- const tfKey = cr.spec.firestartr.tfStateKey;
167030
- if (tfStateKeys[tfKey]) {
167031
- throw new Error(`❗❗ ❌ RENDER ERROR ❌ ❗❗
167032
-
167033
- The tfStateKey ${tfKey} is not unique,
167034
-
167035
- It is being used by the following CRs:
167036
-
167037
- - ${tfStateKeys[tfKey].kind} ${tfStateKeys[tfKey].metadata.name}
167038
-
167039
- - ${cr.kind} ${cr.metadata.name}
167040
-
167041
- Check the following Claims to ensure that the tfStateKey is unique:
167042
-
167043
- - ${tfStateKeys[tfKey].metadata.annotations['firestartr.dev/claim-ref']}
167044
-
167045
- - ${cr.metadata.annotations['firestartr.dev/claim-ref']}
167046
- `);
167047
- }
167048
- tfStateKeys[cr.spec.firestartr.tfStateKey] = cr;
167049
- }
167050
- }
167051
- function references_isCatalogEntity(cr) {
167052
- return [
167053
- 'Component',
167054
- 'Domain',
167055
- 'System',
167056
- 'User',
167057
- 'Group',
167058
- 'Resource',
167059
- ].includes(cr.kind);
167060
- }
167061
- function isTerraformWorkspace(cr) {
167062
- return cr.kind === 'FirestartrTerraformWorkspace';
167063
- }
167064
-
167065
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/crSize.ts
167066
-
167067
- const K8S_OBJECT_SIZE_LIMIT = 1572864; // 1.5 MiB in bytes, etcd recommended limit: https://etcd.io/docs/latest/dev-guide/limit/
167068
- function validateCrSizes(crs) {
167069
- for (const key of Object.keys(crs)) {
167070
- const cr = crs[key];
167071
- const serialized = catalog_common/* default.io.toYaml */.Z.io.toYaml(cr);
167072
- const size = Buffer.byteLength(serialized, 'utf8');
167073
- if (size > K8S_OBJECT_SIZE_LIMIT) {
167074
- 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).`);
167075
- }
167076
- }
167077
- }
167078
-
167079
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/permissions.ts
167080
-
167081
- function validatePermissionsUniqueness(crs) {
167082
- for (const crkey in crs) {
167083
- const cr = crs[crkey];
167084
- if (cr.kind === 'FirestartrGithubRepository') {
167085
- const permissions = cr.spec.permissions;
167086
- logger.debug(`Validating Permissions Uniqueness of ${crkey}`);
167087
- if (permissions) {
167088
- const rolesByIdentifier = new Map();
167089
- for (const perm of permissions) {
167090
- const identifier = perm.collaborator
167091
- ? perm.collaborator
167092
- : `ref:${perm.ref.kind}:${perm.ref.name}`;
167093
- const role = perm.role;
167094
- const prevRole = rolesByIdentifier.get(identifier);
167095
- if (prevRole === undefined) {
167096
- rolesByIdentifier.set(identifier, role);
167097
- }
167098
- else if (prevRole !== role) {
167099
- throw new Error(`Conflicting permission role in FirestartrGithubRepository ${crkey}: ${identifier} has roles "${prevRole}" and "${role}".\nFull CR: ${JSON.stringify(cr, null, 2)}`);
167100
- }
167101
- }
167102
- }
167103
- }
167104
- }
167105
- }
167106
-
167107
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/validations/backstageAnnotations.ts
167108
- const backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX = 'firestartr.backstage.dev/';
167109
- const CATALOG_KINDS = new Set([
167110
- 'User',
167111
- 'Group',
167112
- 'Component',
167113
- 'System',
167114
- 'Domain',
167115
- 'API',
167116
- 'Resource',
167117
- 'CatalogKubernetesResource',
167118
- 'CatalogTerraformWorkspace',
167119
- 'CatalogArgoDeploy',
167120
- 'CatalogOrgWebhook',
167121
- 'CatalogSecrets',
167122
- ]);
167123
- function validateNoBackstageAnnotationsInK8sCrs(crs) {
167124
- for (const crKey in crs) {
167125
- const cr = crs[crKey];
167126
- if (CATALOG_KINDS.has(cr.kind))
167127
- continue;
167128
- if (cr.metadata?.annotations) {
167129
- const backstageKeys = Object.keys(cr.metadata.annotations).filter((k) => k.startsWith(backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX));
167130
- if (backstageKeys.length > 0) {
167131
- throw new Error(`K8s CR ${crKey} contains Backstage-only annotations (${backstageKeys.join(', ')}). ` +
167132
- `Annotations with prefix ${backstageAnnotations_BACKSTAGE_ANNOTATION_PREFIX} must only appear on Backstage catalog entities, not on Kubernetes CRs.`);
167133
- }
167134
- }
167135
- }
167136
- }
167137
-
167138
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/refsSorter/refsSorter.ts
167139
-
167140
- /**
167141
- * @param renderClaims renderClaims to sort
167142
- * @param kinds kinds to get from renderClaims
167143
- * @returns renderClaims with only the kinds specified
167144
- */
167145
- function sortRenderClaimsByKind(renderClaims, kinds) {
167146
- let result = {};
167147
- for (const kind of kinds) {
167148
- for (const key in renderClaims) {
167149
- if (renderClaims[key].claim.kind === kind) {
167150
- result[key] = renderClaims[key];
167151
- }
167152
- }
167153
- result = sortSelfKindRefs(result, kind);
167154
- }
167155
- return result;
167156
- }
167157
- function sortSelfKindRefs(renderClaims, kind) {
167158
- if (!hasSelfRefs(kind)) {
167159
- return renderClaims;
167160
- }
167161
- const newRenderClaims = {};
167162
- const data = extractRefs(renderClaims, kind);
167163
- const ordered = [];
167164
- const visiting = [];
167165
- const visited = [];
167166
- for (const key of Object.keys(data)) {
167167
- if (!visited.find((obj) => obj.name === key)) {
167168
- sortRefs(data, data[key], ordered, visiting, visited);
167169
- }
167170
- }
167171
- for (const cr of ordered) {
167172
- newRenderClaims[cr.name] = renderClaims[cr.name];
167173
- }
167174
- return newRenderClaims;
167175
- }
167176
- function sortRefs(data, claim, ordered = [], visiting = [], visited = []) {
167177
- if (visiting.find((obj) => obj.name === claim.name)) {
167178
- throw new Error(`Circular dependency detected for '${claim.name}'`);
167179
- }
167180
- if (!visited.find((obj) => obj.name === claim.name)) {
167181
- visiting.push(claim);
167182
- if (claim.refs) {
167183
- for (const ref of claim.refs) {
167184
- const referencedObject = data[ref];
167185
- if (!referencedObject) {
167186
- throw new Error(`Reference '${ref}' does not exist for '${claim.name}'`);
167187
- }
167188
- sortRefs(data, referencedObject, ordered, visiting, visited);
167189
- }
167190
- }
167191
- visited.push(visiting.pop());
167192
- ordered.push(claim);
167193
- }
167194
- }
167195
- function hasSelfRefs(kind) {
167196
- return ['TFWorkspaceClaim', 'GroupClaim'].includes(kind);
167197
- }
167198
-
167199
- ;// CONCATENATED MODULE: ../cdk8s_renderer/src/renderer/claims-sorter.ts
167200
-
167201
- function sortKinds(orderedKinds, claims) {
167202
- const result = [];
167203
- for (const kind of orderedKinds) {
167204
- const kindRenderClaims = sortRenderClaimsByKind(claims, [kind]);
167205
- result.push(kindRenderClaims);
167206
- }
167207
- return result;
167208
- }
167209
-
167210
166792
  ;// CONCATENATED MODULE: ../cdk8s_renderer/src/overriders/utils.ts
167211
166793
 
167212
166794
 
@@ -167248,12 +166830,14 @@ async function utils_renderFeature(featureName, featureVersion, featureOutputObj
167248
166830
 
167249
166831
 
167250
166832
 
166833
+
166834
+
167251
166835
  // this is the only way currently to make
167252
166836
  // work the system and inject our code
167253
166837
  // without having to download a feature
167254
- let featureOverride_MOCK_FEATURES_FN;
166838
+ let MOCK_FEATURES_FN;
167255
166839
  function MOCK_FEATURES(mock) {
167256
- featureOverride_MOCK_FEATURES_FN = mock;
166840
+ MOCK_FEATURES_FN = mock;
167257
166841
  if (mock) {
167258
166842
  log.warn('⚠️ Warning features system downloader has been mocked, it should only happen in unit testing');
167259
166843
  }
@@ -167284,8 +166868,8 @@ class FeaturesOverrider extends OverriderPatches {
167284
166868
  let owner = 'prefapp';
167285
166869
  let versionOrRef = this.featureVersion;
167286
166870
  let renderer = 'getFeatureConfig';
167287
- if (featureOverride_MOCK_FEATURES_FN) {
167288
- return featureOverride_MOCK_FEATURES_FN();
166871
+ if (MOCK_FEATURES_FN) {
166872
+ return MOCK_FEATURES_FN();
167289
166873
  }
167290
166874
  if (this.featureRepo) {
167291
166875
  [owner, repo] = this.featureRepo.split(/\//);
@@ -167303,8 +166887,8 @@ class FeaturesOverrider extends OverriderPatches {
167303
166887
  const featureName = this.featureName;
167304
166888
  const featureVersion = this.featureVersion || this.featureRef;
167305
166889
  const featureArgs = this.featureArgs;
167306
- const fSetUpAndRunRenderer = function (target) {
167307
- return this.setUpAndRunRenderer(target);
166890
+ const fSetUpAndRunRenderer = function (cr) {
166891
+ return this.setUpAndRunRenderer(cr);
167308
166892
  }.bind(this);
167309
166893
  let renderedFeatureConfig = null;
167310
166894
  return [
@@ -167313,7 +166897,7 @@ class FeaturesOverrider extends OverriderPatches {
167313
166897
  return true;
167314
166898
  },
167315
166899
  async apply(cr) {
167316
- renderedFeatureConfig = await fSetUpAndRunRenderer(claim);
166900
+ renderedFeatureConfig = await fSetUpAndRunRenderer(cr);
167317
166901
  return cr;
167318
166902
  },
167319
166903
  identify() {
@@ -167326,6 +166910,28 @@ class FeaturesOverrider extends OverriderPatches {
167326
166910
  return utils_renderFeature(featureName, featureVersion, renderedFeatureConfig, cr, claim.name);
167327
166911
  },
167328
166912
  },
166913
+ {
166914
+ isPostPatch: true,
166915
+ validate() {
166916
+ return true;
166917
+ },
166918
+ async apply(cr) {
166919
+ if (!renderedFeatureConfig) {
166920
+ logger.warn(`Feature ${featureName}: renderedFeatureConfig is null, skipping post-patch`);
166921
+ return cr;
166922
+ }
166923
+ const provider = helperCTX(this).provider;
166924
+ const jsonPatches = renderedFeatureConfig.patches?.[provider] || [];
166925
+ fast_json_patch_default().applyPatch(cr, jsonPatches);
166926
+ return cr;
166927
+ },
166928
+ identify() {
166929
+ return `feature-json-patch/${featureName}`;
166930
+ },
166931
+ applicable() {
166932
+ return { applicableProviders: ['catalog', 'github'] };
166933
+ },
166934
+ },
167329
166935
  ];
167330
166936
  }
167331
166937
  }
@@ -173220,6 +172826,7 @@ function walkArray(arr, symbolsToResolve, renderClaim) {
173220
172826
 
173221
172827
 
173222
172828
 
172829
+
173223
172830
  /*
173224
172831
  * Function called when rendering from the importer class.
173225
172832
  *
@@ -173257,6 +172864,10 @@ async function renderFromImports(rClaims, crs = {}, catalogOutputDir = '/tmp/.ca
173257
172864
  logger.error(`Error while resolving imported-refs: ${e.message}`);
173258
172865
  throw new Error(`Error while resolving imported-refs: ${e.message}`);
173259
172866
  }
172867
+ // Clear the rendered claims symbol table to ensure a clean state
172868
+ // after resolving imported refs, preventing setRenderedClaim() from throwing
172869
+ // 'already rendered' for claims that were previously seeded
172870
+ emptyRenderedClaims();
173260
172871
  // after resolving the imported-refs, we need to validate the claims again, and apply the defaults if needed
173261
172872
  for (const renderClaim of Object.values(rClaims)) {
173262
172873
  if (defaults) {
@@ -173338,6 +172949,8 @@ async function addLastStateAndLastClaimAnnotations(filePath, lastStatePRLink, la
173338
172949
 
173339
172950
 
173340
172951
 
172952
+
172953
+
173341
172954
 
173342
172955
 
173343
172956
 
@@ -173431,6 +173044,9 @@ async function runRenderer(globalsPath, initializersPath, claimsPath, crsPath, c
173431
173044
  }
173432
173045
 
173433
173046
  ;// CONCATENATED MODULE: ../importer/src/decanter/config.ts
173047
+
173048
+
173049
+
173434
173050
  let claimsPath = '';
173435
173051
  function setClaimsPath(path) {
173436
173052
  claimsPath = path;
@@ -173467,6 +173083,92 @@ function getConfigPath() {
173467
173083
  }
173468
173084
  return configPath;
173469
173085
  }
173086
+ /**
173087
+ * claimsDefaults path
173088
+ */
173089
+ let claimsDefaultsPath = '';
173090
+ function setClaimsDefaultsPath(path) {
173091
+ claimsDefaultsPath = path;
173092
+ }
173093
+ function getClaimsDefaultsPath() {
173094
+ if (!claimsDefaultsPath) {
173095
+ throw new Error('Claims defaults path not set');
173096
+ }
173097
+ return claimsDefaultsPath;
173098
+ }
173099
+ /**
173100
+ * Cached default owner claim reference from claims_defaults.yaml.
173101
+ * Loaded once via loadClaimsDefaultsOnce() to avoid repeated file reads.
173102
+ */
173103
+ let defaultOwnerClaimRef = null;
173104
+ /**
173105
+ * Resolved default owner CODEOWNERS handles, derived from defaultOwnerClaimRef
173106
+ * through the same claim-to-external-name mapping used by CODEOWNERS generation.
173107
+ */
173108
+ let defaultOwnerHandles = {
173109
+ team: null,
173110
+ user: null,
173111
+ };
173112
+ /**
173113
+ * Loads claims_defaults.yaml once and caches the default owner claim reference.
173114
+ * Must be called after setClaimsDefaultsPath() and before any decanter runs.
173115
+ */
173116
+ function loadClaimsDefaultsOnce() {
173117
+ try {
173118
+ const raw = external_fs_.readFileSync(`${getClaimsDefaultsPath()}/claims_defaults.yaml`, 'utf-8');
173119
+ const defaults = catalog_common/* default.io.fromYaml */.Z.io.fromYaml(raw);
173120
+ defaultOwnerClaimRef = defaults?.ComponentClaim?.owner ?? null;
173121
+ }
173122
+ catch {
173123
+ defaultOwnerClaimRef = null;
173124
+ }
173125
+ }
173126
+ /**
173127
+ * Returns the cached default owner claim reference (e.g., "group:group_a" or "user:user_a").
173128
+ * Returns null if not loaded or not set in claims_defaults.yaml.
173129
+ */
173130
+ function getDefaultOwnerClaimRef() {
173131
+ return defaultOwnerClaimRef;
173132
+ }
173133
+ /**
173134
+ * Resolves the default owner claim reference to CODEOWNERS handle(s) using the
173135
+ * same claim-to-external-name mapping used by CODEOWNERS generation.
173136
+ * Must be called after the referenced claims have been seeded into the renderer
173137
+ * (i.e., after setPreviousCRs) so resolveCodeownersRef can resolve them.
173138
+ */
173139
+ function resolveDefaultOwnerHandles(org) {
173140
+ defaultOwnerHandles = { team: null, user: null };
173141
+ const claimRef = defaultOwnerClaimRef;
173142
+ if (!org || !claimRef || !claimRef.includes(':')) {
173143
+ return;
173144
+ }
173145
+ try {
173146
+ const [kind] = claimRef.split(':');
173147
+ const handle = resolveCodeownersRef(claimRef, org).toLowerCase();
173148
+ if (kind === 'group') {
173149
+ defaultOwnerHandles.team = handle;
173150
+ }
173151
+ else if (kind === 'user') {
173152
+ defaultOwnerHandles.user = handle;
173153
+ }
173154
+ }
173155
+ catch {
173156
+ // referenced claim not resolvable → no default owner escape hatch
173157
+ }
173158
+ }
173159
+ /**
173160
+ * Returns the resolved default owner CODEOWNERS handles, if any.
173161
+ */
173162
+ function getDefaultOwnerHandles() {
173163
+ return defaultOwnerHandles;
173164
+ }
173165
+ /**
173166
+ * Resets the cached default owner claim ref and handles (useful for testing).
173167
+ */
173168
+ function resetClaimsDefaultsCache() {
173169
+ defaultOwnerClaimRef = null;
173170
+ defaultOwnerHandles = { team: null, user: null };
173171
+ }
173470
173172
  /* harmony default export */ const config = ({
173471
173173
  setClaimsPath,
173472
173174
  getClaimsPath,
@@ -173474,6 +173176,13 @@ function getConfigPath() {
173474
173176
  getResourcesPath,
173475
173177
  setConfigPath,
173476
173178
  getConfigPath,
173179
+ setClaimsDefaultsPath,
173180
+ getClaimsDefaultsPath,
173181
+ loadClaimsDefaultsOnce,
173182
+ getDefaultOwnerClaimRef,
173183
+ resolveDefaultOwnerHandles,
173184
+ getDefaultOwnerHandles,
173185
+ resetClaimsDefaultsCache,
173477
173186
  });
173478
173187
 
173479
173188
  ;// CONCATENATED MODULE: ../importer/src/logger.ts
@@ -174075,6 +173784,7 @@ function transformRepoName(repoName) {
174075
173784
 
174076
173785
 
174077
173786
 
173787
+
174078
173788
  const TYPE_MAP = {
174079
173789
  User: 'user',
174080
173790
  Team: 'group',
@@ -174220,15 +173930,43 @@ class RepoGithubDecanter extends GithubDecanter {
174220
173930
  return (this.data.teamsAndMembers.directMembers.some((member) => member.name?.toLowerCase() === normalizedUserName) ||
174221
173931
  this.data.teamsAndMembers.outsideMembers.some((member) => member.name?.toLowerCase() === normalizedUserName));
174222
173932
  };
173933
+ // The default owner is resolved once in the importer/configuration layer
173934
+ // from claims_defaults.yaml, through the same claim-to-external-name
173935
+ // mapping used by CODEOWNERS generation. ComponentClaim.owner is a claim
173936
+ // reference (e.g. "group:group_a" or "user:user_a"), not a GitHub handle,
173937
+ // so we must compare against the resolved external name.
173938
+ let defaultOwnerTeamHandle = null;
173939
+ let defaultOwnerUserHandle = null;
173940
+ const defaultOwnerHandles = getDefaultOwnerHandles();
173941
+ if (defaultOwnerHandles.team) {
173942
+ defaultOwnerTeamHandle = defaultOwnerHandles.team;
173943
+ }
173944
+ if (defaultOwnerHandles.user) {
173945
+ defaultOwnerUserHandle = defaultOwnerHandles.user;
173946
+ }
173947
+ const isDefaultOwner = (owner) => {
173948
+ const normalized = owner.name.toLowerCase();
173949
+ if (owner.isTeam)
173950
+ return normalized === defaultOwnerTeamHandle;
173951
+ return normalized === defaultOwnerUserHandle;
173952
+ };
174223
173953
  // let's validate that every element in the
174224
173954
  // CODEOWNERS file is either a team or a user in the repository,
174225
173955
  // otherwise we will have dangling references in the claim which will cause issues down the line
174226
173956
  for (const owner of owners) {
174227
173957
  if (owner.isTeam && !isInTeams(owner.name)) {
173958
+ if (isDefaultOwner(owner)) {
173959
+ importer_src_logger.warn(`[${this.org}/${this.data.repoDetails.name}] CODEOWNERS file references default owner team ${owner.name} which is not yet in the repository's teams — will be granted on provisioning; continuing.`);
173960
+ continue;
173961
+ }
174228
173962
  importer_src_logger.error(`[${this.org}/${this.data.repoDetails.name}] CODEOWNERS file references team ${owner.name} which is not present in the repository's teams.`);
174229
173963
  throw new Error(`[${this.org}/${this.data.repoDetails.name}] CODEOWNERS file references team ${owner.name} which is not present in the repository's teams.`);
174230
173964
  }
174231
173965
  else if (!owner.isTeam && !isInUsers(owner.name)) {
173966
+ if (isDefaultOwner(owner)) {
173967
+ importer_src_logger.warn(`[${this.org}/${this.data.repoDetails.name}] CODEOWNERS file references default owner user ${owner.name} not yet collaborator — will be granted on provisioning; continuing.`);
173968
+ continue;
173969
+ }
174232
173970
  importer_src_logger.error(`[${this.org}/${this.data.repoDetails.name}] CODEOWNERS file references user ${owner.name} which is not present in the repository's collaborators.`);
174233
173971
  throw new Error(`[${this.org}/${this.data.repoDetails.name}] CODEOWNERS file references user ${owner.name} which is not present in the repository's collaborators.`);
174234
173972
  }
@@ -174533,22 +174271,39 @@ function isInPreviousCRs(kind, name) {
174533
174271
  }
174534
174272
  const randomFolder = external_path_.join('/tmp', `claims_${catalog_common/* default.generic.randomString */.Z.generic.randomString(10)}`);
174535
174273
  const tmpRenderedCrsPath = external_path_.join('/tmp', '.resources');
174536
- async function setPreviousCRs(resourcesPath) {
174274
+ async function setPreviousCRs(resourcesPath, org) {
174537
174275
  const crs = await cdk8s_renderer.loadCRs(resourcesPath, [
174538
174276
  external_path_.join(resourcesPath, '.github'),
174539
174277
  external_path_.join(resourcesPath, '.config'),
174540
174278
  external_path_.join(resourcesPath, '.import'),
174541
174279
  ]);
174542
174280
  const alreadyImportedCrs = {};
174281
+ emptyRenderedClaims();
174282
+ const claimRefAnnotation = catalog_common/* default.generic.getFirestartrAnnotation */.Z.generic.getFirestartrAnnotation('claim-ref');
174543
174283
  for (const value of Object.values(crs)) {
174544
174284
  const resourceName = value.metadata.annotations[catalog_common/* default.generic.getFirestartrAnnotation */.Z.generic.getFirestartrAnnotation('external-name')];
174545
174285
  const resourceKind = value.kind;
174546
174286
  alreadyImportedCrs[`${resourceKind}-${resourceName}`] = value;
174287
+ // Seed the renderer's claim symbol table from already-imported CRs so that
174288
+ // claim references (e.g. the default owner "group:group_a") can be resolved
174289
+ // to their external names through the same mapping used by CODEOWNERS.
174290
+ const claimRef = value.metadata?.annotations?.[claimRefAnnotation];
174291
+ if (claimRef && typeof claimRef === 'string' && claimRef.includes('/')) {
174292
+ const [claimKind, claimName] = claimRef.split('/');
174293
+ if ((claimKind === 'GroupClaim' || claimKind === 'UserClaim') &&
174294
+ claimName) {
174295
+ setRenderedClaim({ kind: claimKind, name: claimName }, value);
174296
+ }
174297
+ }
174547
174298
  }
174548
174299
  previousCRs = alreadyImportedCrs;
174300
+ if (org) {
174301
+ resolveDefaultOwnerHandles(org);
174302
+ }
174303
+ emptyRenderedClaims();
174549
174304
  }
174550
174305
  async function importGithubGitopsRepository(org, skipPlan, claimsPath, resourcesPath, configPath, filters = [], needsReImport, force) {
174551
- await setPreviousCRs(resourcesPath);
174306
+ await setPreviousCRs(resourcesPath, org);
174552
174307
  configurePaths(randomFolder, '/tmp/tmp-resources', // Not used, but required by renderer
174553
174308
  configPath);
174554
174309
  importer_src_logger.info(`Importing gitops repository for organization: ${org}`);
@@ -174915,6 +174670,7 @@ async function reimporter_isDirectory(path) {
174915
174670
 
174916
174671
 
174917
174672
 
174673
+
174918
174674
  // const MAP_COLLECTION_KIND_CR_KIND: any = {
174919
174675
  // gh-members: 'FirestartrGithubMembership',
174920
174676
  // gh-group: 'FirestartrGithubGroup',
@@ -174925,6 +174681,8 @@ async function runImporter(force, skipPlan, claimsPath, crsPath, configPath, cla
174925
174681
  let generatedFilters = [];
174926
174682
  //gh-repo,REGEXP=.*vite.*
174927
174683
  cdk8s_renderer.setPath('claimsDefaults', claimsDefaultsPath);
174684
+ setClaimsDefaultsPath(claimsDefaultsPath);
174685
+ loadClaimsDefaultsOnce();
174928
174686
  generatedFilters = buildFilters(filters, force, crsPath, generatedFilters);
174929
174687
  if (needsReimport) {
174930
174688
  await reimportGithubGitopsRepository(org, crsPath, configPath, generatedFilters);
@@ -363653,7 +363411,7 @@ function helperCreateCheckRunName(cmd, item) {
363653
363411
 
363654
363412
  ;// CONCATENATED MODULE: ../operator/src/utils/index.ts
363655
363413
  const secretRegex = /\$\{\{ secrets\.(.*?) \}\}/g;
363656
- function utils_isRecord(value) {
363414
+ function isRecord(value) {
363657
363415
  return typeof value === 'object' && value !== null;
363658
363416
  }
363659
363417
  function getFirstNonEmptyString(...values) {
@@ -363691,7 +363449,7 @@ function replaceInlineSecrets(inline, secrets) {
363691
363449
  return result;
363692
363450
  }
363693
363451
  function extractErrorDetails(error) {
363694
- const exitCode = utils_isRecord(error) && typeof error.exitCode === 'number'
363452
+ const exitCode = isRecord(error) && typeof error.exitCode === 'number'
363695
363453
  ? error.exitCode
363696
363454
  : undefined;
363697
363455
  if (typeof error === 'string') {
@@ -363702,7 +363460,7 @@ function extractErrorDetails(error) {
363702
363460
  const output = getFirstNonEmptyString(errorWithOutput.output, error.message);
363703
363461
  return { output: output ?? String(error), exitCode };
363704
363462
  }
363705
- if (utils_isRecord(error)) {
363463
+ if (isRecord(error)) {
363706
363464
  const output = getFirstNonEmptyString(error.output, error.message, error.error);
363707
363465
  if (output !== undefined) {
363708
363466
  return { output, exitCode };
@@ -713815,7 +713573,7 @@ const crsStatusSubcommand = {
713815
713573
  };
713816
713574
 
713817
713575
  ;// CONCATENATED MODULE: ./package.json
713818
- const package_namespaceObject = JSON.parse('{"i8":"2.11.0-snapshot-patch-claims"}');
713576
+ const package_namespaceObject = JSON.parse('{"i8":"2.11.0-snapshot-mco-03"}');
713819
713577
  ;// CONCATENATED MODULE: ../../package.json
713820
713578
  const package_namespaceObject_1 = {"i8":"2.10.0"};
713821
713579
  // EXTERNAL MODULE: ../../node_modules/semver/index.js