@contrail/flexplm 1.7.4-alpha.aaaa660 → 1.7.4-alpha.b088cfc

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.
@@ -921,6 +921,39 @@ describe('getDeleteChanges', () => {
921
921
  expect(itemIds).toContain('qNCQmTFcFxwbT-oG');
922
922
  spy.mockRestore();
923
923
  });
924
+ const stubDownloads = (ppa, history, priorBaseline) => {
925
+ jest.spyOn(ppa, 'downloadDeleteChanges').mockImplementation(async (apc) => {
926
+ if (!apc)
927
+ return [];
928
+ return history.find(h => h['id'] === apc['id'])?.['deleteData'] || [];
929
+ });
930
+ jest.spyOn(ppa, 'downloadAssortmentBaseline').mockResolvedValue(priorBaseline);
931
+ };
932
+ const famRow = (id, lifecycleStage = 'development') => ({
933
+ itemId: id,
934
+ item: { id, itemFamilyId: id, lifecycleStage, roles: ['family'] },
935
+ projectItem: { id: 'pi-' + id }
936
+ });
937
+ const optRow = (id, familyId) => ({
938
+ itemId: id,
939
+ item: { id, itemFamilyId: familyId, lifecycleStage: 'development', roles: ['color', 'option'] }
940
+ });
941
+ it('adds a family row that left the assortment while its option rows remain', async () => {
942
+ const history = new Array(...mockData_1.apc_2bOWR2j9R0QThDVu_delete_history);
943
+ const apcIndex = history.length - 1;
944
+ const apc = history[apcIndex];
945
+ const apcDate = new Date(history[apcIndex - 1]['createdOn']);
946
+ const ppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
947
+ const fam = famRow('FAM1');
948
+ const opt = optRow('OPT1', 'FAM1');
949
+ stubDownloads(ppa, history, { assortmentItems: [fam, opt] });
950
+ const deleteChanges = await ppa.getDeleteChanges(apc, history, { assortmentItems: [opt] }, apcDate);
951
+ const itemIds = deleteChanges.map(d => d['itemId']);
952
+ expect(itemIds).toContain('FAM1');
953
+ expect(itemIds).not.toContain('OPT1');
954
+ expect(deleteChanges.find(d => d['itemId'] === 'FAM1')['projectItem']['id']).toEqual('pi-FAM1');
955
+ jest.restoreAllMocks();
956
+ });
924
957
  });
925
958
  describe('buildDeleteChanges', () => {
926
959
  const config = {};
@@ -1337,6 +1370,163 @@ describe('getItemFamilyChanges', () => {
1337
1370
  expect(ifc.colorDeletes.length).toEqual(1);
1338
1371
  });
1339
1372
  });
1373
+ describe('getItemFamilyChanges - lifecycle-suppressed options', () => {
1374
+ const familyId = 'AERfdvdAt1HHEVvQ';
1375
+ const maroonId = 'YwWKVoL_vGN8khLy';
1376
+ const limeId = 'rsmWhWiXRNv_XBJG';
1377
+ const sinceDate = new Date('2026-08-01T00:00:00.000Z');
1378
+ const afterSince = '2026-08-26T00:00:00.000Z';
1379
+ const beforeSince = '2026-05-11T00:00:00.000Z';
1380
+ const noDetailChanges = () => ({ adds: [], deletes: [], updates: [], familyItemsRemoved: [] });
1381
+ const buildPpa = () => {
1382
+ const config = { itemPreDevelopmentLifecycleStages: ['concept'] };
1383
+ const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
1384
+ const dc = new data_converter_1.DataConverter(config, mapFileUtil);
1385
+ return new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1386
+ };
1387
+ const buildPcd = (releasedIds) => {
1388
+ const seasonFed = {
1389
+ entityReference: 'assortment:11EZML5dMn3tREuT',
1390
+ objectClass: 'LCSSeason'
1391
+ };
1392
+ const pcd = new publish_change_data_1.PublishChangeData('11EZML5dMn3tREuT', seasonFed, 'Pg3y2x5IclYxk3hM', sinceDate);
1393
+ pcd.itemToFederatedIdMapping = new Map();
1394
+ releasedIds.forEach(id => pcd.releasedForDevelopmentItemIds.push(id));
1395
+ return pcd;
1396
+ };
1397
+ const familyItem = (updatedOn = beforeSince, lifecycleStage = 'released') => ({
1398
+ id: familyId, itemFamilyId: familyId, lifecycleStage, name: 'Sweatpant', updatedOn
1399
+ });
1400
+ const familyRow = (updatedOn = beforeSince, lifecycleStage = 'released') => ({
1401
+ itemId: familyId,
1402
+ item: familyItem(updatedOn, lifecycleStage),
1403
+ projectItem: { id: 'pi-' + familyId, updatedOn: beforeSince }
1404
+ });
1405
+ const optionRow = (id, updatedOn, lifecycleStage = 'concept') => ({
1406
+ itemId: id,
1407
+ item: { id, itemFamilyId: familyId, lifecycleStage, updatedOn, itemFamily: familyItem() },
1408
+ projectItem: { id: 'pi-' + id, updatedOn: beforeSince },
1409
+ familyProjectItem: { id: 'pi-' + familyId, updatedOn: beforeSince }
1410
+ });
1411
+ it('records changed concept options as suppressed, not as colour updates', () => {
1412
+ const ppa = buildPpa();
1413
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({
1414
+ assortmentItems: [familyRow(), optionRow(maroonId, afterSince), optionRow(limeId, afterSince)]
1415
+ });
1416
+ const pcd = buildPcd([familyId]);
1417
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, noDetailChanges(), fullChangeMap, new Map());
1418
+ expect(pcd.itemFamilyChanges.size).toEqual(1);
1419
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1420
+ expect(ifc.colorSuppressed).toEqual([maroonId, limeId]);
1421
+ expect(ifc.colorAdds.length).toEqual(0);
1422
+ expect(ifc.colorUpdates.length).toEqual(0);
1423
+ expect(ifc.colorDeletes.length).toEqual(0);
1424
+ expect(ifc.familyUpdate).toBe(false); // family itself unchanged
1425
+ });
1426
+ it('keeps the family reachable when only concept option rows are in the baseline', () => {
1427
+ const ppa = buildPpa();
1428
+ const assortmentItems = [optionRow(maroonId, afterSince), optionRow(limeId, afterSince)];
1429
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({ assortmentItems });
1430
+ // derive the gate the way production does, rather than seeding it
1431
+ const releasedIds = ppa.getReleasedForDevelopmentItemAndFamilyIds({ assortmentItems }, []);
1432
+ expect(releasedIds).toEqual([familyId]);
1433
+ const pcd = buildPcd(releasedIds);
1434
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, noDetailChanges(), fullChangeMap, new Map());
1435
+ expect(pcd.itemFamilyChanges.size).toEqual(1);
1436
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1437
+ expect(ifc.itemFamilyObject.id).toEqual(familyId);
1438
+ expect(ifc.colorSuppressed.length).toEqual(2);
1439
+ expect(ppa.getProjectItem(ifc, familyId)).toEqual({ id: 'pi-' + familyId, updatedOn: beforeSince });
1440
+ });
1441
+ it('does not suppress-record an unchanged concept option', () => {
1442
+ const ppa = buildPpa();
1443
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({
1444
+ assortmentItems: [familyRow(), optionRow(maroonId, beforeSince)]
1445
+ });
1446
+ const pcd = buildPcd([familyId]);
1447
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, noDetailChanges(), fullChangeMap, new Map());
1448
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1449
+ expect(ifc.colorSuppressed.length).toEqual(0);
1450
+ });
1451
+ it('picks up a concept option listed in the change detail even when timestamps are stale', () => {
1452
+ const ppa = buildPpa();
1453
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({
1454
+ assortmentItems: [familyRow(), optionRow(maroonId, beforeSince)]
1455
+ });
1456
+ const details = noDetailChanges();
1457
+ details.updates = [{ id: maroonId }];
1458
+ const pcd = buildPcd([familyId]);
1459
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, details, fullChangeMap, new Map());
1460
+ expect(pcd.itemFamilyChanges.get(familyId).colorSuppressed).toEqual([maroonId]);
1461
+ });
1462
+ it('skips everything when the family itself is pre-development', () => {
1463
+ const ppa = buildPpa();
1464
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({
1465
+ assortmentItems: [familyRow(afterSince, 'concept'), optionRow(maroonId, afterSince)]
1466
+ });
1467
+ const pcd = buildPcd([]); // nothing released
1468
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, noDetailChanges(), fullChangeMap, new Map());
1469
+ expect(pcd.itemFamilyChanges.size).toEqual(0);
1470
+ });
1471
+ it('still classifies released options normally', () => {
1472
+ const ppa = buildPpa();
1473
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({
1474
+ assortmentItems: [familyRow(), optionRow(maroonId, afterSince, 'released')]
1475
+ });
1476
+ const details = noDetailChanges();
1477
+ details.adds = [{ id: maroonId }];
1478
+ const pcd = buildPcd([familyId, maroonId]);
1479
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, details, fullChangeMap, new Map());
1480
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1481
+ expect(ifc.colorAdds).toEqual([maroonId]);
1482
+ expect(ifc.colorSuppressed.length).toEqual(0);
1483
+ });
1484
+ it('publishes a family-only row whose lifecycle just flipped (item-level edit)', () => {
1485
+ const ppa = buildPpa();
1486
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({
1487
+ assortmentItems: [familyRow(afterSince)] // item.updatedOn moved, projectItem did not
1488
+ });
1489
+ const pcd = buildPcd([familyId]);
1490
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, noDetailChanges(), fullChangeMap, new Map());
1491
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1492
+ expect(ifc.familyUpdate).toBe(true); // fails without Step 3
1493
+ expect(ifc.colorSuppressed.length).toEqual(0);
1494
+ });
1495
+ it('leaves an unchanged family-only row with no flags', () => {
1496
+ const ppa = buildPpa();
1497
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({ assortmentItems: [familyRow()] });
1498
+ const pcd = buildPcd([familyId]);
1499
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, noDetailChanges(), fullChangeMap, new Map());
1500
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1501
+ expect(ifc.familyAdd).toBe(false);
1502
+ expect(ifc.familyUpdate).toBe(false);
1503
+ expect(ifc.familyDelete).toBe(false);
1504
+ expect(ifc.colorSuppressed.length).toEqual(0);
1505
+ });
1506
+ it('does not set familyUpdate when the family item was removed', () => {
1507
+ const ppa = buildPpa();
1508
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({ assortmentItems: [familyRow(afterSince)] });
1509
+ const details = noDetailChanges();
1510
+ details.familyItemsRemoved = [{ itemId: familyId }]; // note: itemId, not id
1511
+ const pcd = buildPcd([familyId]);
1512
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, details, fullChangeMap, new Map());
1513
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1514
+ expect(ifc.familyItemRemoved).toBe(true);
1515
+ expect(ifc.familyUpdate).toBe(false); // fails if the !familyItemRemoved guard is dropped
1516
+ });
1517
+ it('flags familyDelete when the family row left the assortment but option rows remain', () => {
1518
+ const ppa = buildPpa();
1519
+ const assortmentItems = [optionRow(maroonId, beforeSince), optionRow(limeId, beforeSince, 'released')];
1520
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({ assortmentItems });
1521
+ const deleteMap = new Map([[familyId, familyRow()]]);
1522
+ const pcd = buildPcd([familyId, limeId]);
1523
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, noDetailChanges(), fullChangeMap, deleteMap);
1524
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1525
+ expect(ifc.familyDelete).toBe(true);
1526
+ expect(ifc.colorDeletes.length).toEqual(0); // no SKU event
1527
+ expect(ppa.getProjectItem(ifc, familyId).id).toEqual('pi-' + familyId); // family's own projectItem
1528
+ });
1529
+ });
1340
1530
  describe('getProjectItem', () => {
1341
1531
  const config = {
1342
1532
  identifierAtts: {
@@ -1500,6 +1690,70 @@ describe('meetsCriteria', () => {
1500
1690
  expect(results).toBeTruthy();
1501
1691
  });
1502
1692
  });
1693
+ describe('getReleasedForDevelopmentItemAndFamilyIds', () => {
1694
+ const familyId = 'AERfdvdAt1HHEVvQ';
1695
+ const maroonId = 'YwWKVoL_vGN8khLy';
1696
+ const limeId = 'rsmWhWiXRNv_XBJG';
1697
+ const buildPpa = (preDev = ['concept']) => {
1698
+ const config = {
1699
+ identifierAtts: { LCSSeason: ['flexPLMSeasonName'] },
1700
+ itemPreDevelopmentLifecycleStages: preDev
1701
+ };
1702
+ const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
1703
+ const dc = new data_converter_1.DataConverter(config, mapFileUtil);
1704
+ return new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1705
+ };
1706
+ const familyItem = (lifecycleStage = 'released') => ({
1707
+ id: familyId, itemFamilyId: familyId, lifecycleStage, name: 'Sweatpant'
1708
+ });
1709
+ // option row as the hydrated baseline actually delivers it: itemFamily present
1710
+ const optionRow = (id, lifecycleStage = 'concept', family = familyItem()) => ({
1711
+ itemId: id,
1712
+ item: { id, itemFamilyId: familyId, lifecycleStage, itemFamily: family }
1713
+ });
1714
+ const familyRow = (lifecycleStage = 'released') => ({
1715
+ itemId: familyId,
1716
+ item: familyItem(lifecycleStage)
1717
+ });
1718
+ const run = (ppa, assortmentItems) => ppa.getReleasedForDevelopmentItemAndFamilyIds({ assortmentItems }, []);
1719
+ it('surfaces the family when every option is pre-development', () => {
1720
+ const ppa = buildPpa();
1721
+ expect(run(ppa, [optionRow(maroonId), optionRow(limeId)])).toEqual([familyId]);
1722
+ });
1723
+ it('does not duplicate the family id when a family row and a released option both exist', () => {
1724
+ const ppa = buildPpa();
1725
+ const ids = run(ppa, [familyRow(), optionRow(limeId, 'released')]);
1726
+ expect(ids.filter(id => id === familyId)).toHaveLength(1);
1727
+ });
1728
+ it('still resolves the family when an earlier row lacks itemFamily hydration', () => {
1729
+ const ppa = buildPpa();
1730
+ const noFamilyHydration = {
1731
+ itemId: maroonId,
1732
+ item: { id: maroonId, itemFamilyId: familyId, lifecycleStage: 'concept' }
1733
+ };
1734
+ expect(run(ppa, [noFamilyHydration, optionRow(limeId)])).toEqual([familyId]);
1735
+ });
1736
+ it('returns nothing when the family itself is pre-development', () => {
1737
+ const ppa = buildPpa();
1738
+ expect(run(ppa, [optionRow(maroonId, 'concept', familyItem('concept'))])).toEqual([]);
1739
+ });
1740
+ it('includes a family-only row exactly once', () => {
1741
+ const ppa = buildPpa();
1742
+ expect(run(ppa, [familyRow()])).toEqual([familyId]);
1743
+ });
1744
+ it('includes a released option and its family, excluding the concept option', () => {
1745
+ const ppa = buildPpa();
1746
+ const ids = run(ppa, [optionRow(maroonId, 'concept'), optionRow(limeId, 'released')]);
1747
+ expect(ids).toContain(familyId);
1748
+ expect(ids).toContain(limeId);
1749
+ expect(ids).not.toContain(maroonId);
1750
+ });
1751
+ it('treats the stage list as case-sensitive (documents the Concept vs concept trap)', () => {
1752
+ const ppa = buildPpa(['Concept']); // as mis-configured in praveen-dev-1
1753
+ const ids = run(ppa, [optionRow(maroonId), optionRow(limeId)]);
1754
+ expect(ids).toEqual([maroonId, familyId, limeId]); // filter inert: everything qualifies
1755
+ });
1756
+ });
1503
1757
  describe('getEventsForItemFamilyChanges - conditional eventType', () => {
1504
1758
  const config = {
1505
1759
  identifierAtts: {
@@ -1705,6 +1959,34 @@ describe('getEventsForItemFamilyChanges - conditional eventType', () => {
1705
1959
  // Verify SKU event called with assortment entity
1706
1960
  expect(type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenNthCalledWith(2, undefined, mapFileUtil, colorProjectItem, { item: itemData, assortment });
1707
1961
  });
1962
+ it('should emit only an LCSProductSeasonLink when the family row was removed', async () => {
1963
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1964
+ const itemFamilyId = 'AERfdvdAt1HHEVvQ';
1965
+ const prodEntityData = { id: itemFamilyId, identifier: 'FAM-11', itemNumber: 'FAM-11' };
1966
+ const projectItem = { id: 'proj-fam', updatedOn: '2022-12-01T00:00:00.000Z' };
1967
+ const assortment = { id: 'assort123', name: 'Fall 2023 Assortment', publishToFlexPLM: true };
1968
+ const seasonFed = {
1969
+ entityReference: 'assortment:assort123',
1970
+ objectClass: 'LCSSeason',
1971
+ flexPLMSeasonName: 'Fall 2023'
1972
+ };
1973
+ const itemFamilyChanges = new item_family_changes_1.ItemFamilyChanges(itemFamilyId, new Date('2023-01-01T00:00:00.000Z'));
1974
+ itemFamilyChanges.familyDelete = true; // the only trigger
1975
+ itemFamilyChanges.colorUnchanged.push('rsmWhWiXRNv_XBJG'); // surviving option must not emit
1976
+ itemFamilyChanges.itemFamilyObject = prodEntityData;
1977
+ jest.spyOn(bppa, 'getAssortment').mockResolvedValue(assortment);
1978
+ jest.spyOn(bppa, 'getProjectItem').mockReturnValue(projectItem);
1979
+ jest.spyOn(bppa, 'getSeasonalData').mockResolvedValue({ someData: 'value' });
1980
+ jest.spyOn(map_utils_1.MapUtil, 'applyTransformMap').mockResolvedValue({ transformedData: 'value' });
1981
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierProperties').mockResolvedValue(['identifier']);
1982
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getInformationalProperties').mockResolvedValue([]);
1983
+ jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'isOutboundCreatableFromEntity').mockResolvedValue(false);
1984
+ const events = await bppa.getEventsForItemFamilyChanges(itemFamilyChanges, 'assort123', seasonFed, new Map());
1985
+ expect(events).toHaveLength(1);
1986
+ expect(events[0].objectClass).toBe('LCSProductSeasonLink');
1987
+ expect(events[0].eventType).toBe('UPDATE_ON_SEASON');
1988
+ expect(events[0].entityReference).toBe('item:' + itemFamilyId);
1989
+ });
1708
1990
  });
1709
1991
  describe('sendToFlexPLM / handleVibeIQFile / sendPublishPayloadEvent', () => {
1710
1992
  const config = {
package/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "@contrail/flexplm",
3
- "version": "1.7.4-alpha.aaaa660",
3
+ "version": "1.7.4-alpha.b088cfc",
4
4
  "description": "Library used for integration with flexplm.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
7
7
  "bin": {
8
- "flexplm-mapping": "lib/cli/index.js",
9
- "flexplm-config": "lib/cli/config-index.js"
8
+ "flexplm-mapping": "lib/cli/index.js"
10
9
  },
11
10
  "files": [
12
11
  "lib/**/*",
@@ -2,13 +2,9 @@
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
4
 
5
- const TEMPLATES = ['mapping-template.ts.template', 'config-template.json.template'];
5
+ const SRC = path.join('src', 'cli', 'template', 'mapping-template.ts.template');
6
+ const DST = path.join('lib', 'cli', 'template', 'mapping-template.ts.template');
6
7
 
7
- for (const templateFilename of TEMPLATES) {
8
- const SRC = path.join('src', 'cli', 'template', templateFilename);
9
- const DST = path.join('lib', 'cli', 'template', templateFilename);
10
-
11
- fs.mkdirSync(path.dirname(DST), { recursive: true });
12
- fs.copyFileSync(SRC, DST);
13
- console.log(`Copied ${SRC} -> ${DST}`);
14
- }
8
+ fs.mkdirSync(path.dirname(DST), { recursive: true });
9
+ fs.copyFileSync(SRC, DST);
10
+ console.log(`Copied ${SRC} -> ${DST}`);
@@ -1,5 +0,0 @@
1
- export declare class ConfigCreateCommand {
2
- private prompt;
3
- private findTemplate;
4
- run(): Promise<void>;
5
- }
@@ -1,85 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.ConfigCreateCommand = void 0;
27
- const fs = __importStar(require("fs"));
28
- const path = __importStar(require("path"));
29
- const readline = __importStar(require("readline"));
30
- const TEMPLATE_FILENAME = 'config-template.json.template';
31
- const ORG_PLACEHOLDER = '<ORG_NAME>';
32
- const APP_IDENTIFIER_PLACEHOLDER = '<APP_IDENTIFIER>';
33
- const DEFAULT_APP_IDENTIFIER = '@vibeiq/flexplm-connector';
34
- class ConfigCreateCommand {
35
- prompt(rl, question) {
36
- return new Promise((resolve) => {
37
- rl.question(question, (answer) => resolve(answer));
38
- });
39
- }
40
- findTemplate() {
41
- const candidates = [
42
- path.join(__dirname, '..', 'template', TEMPLATE_FILENAME),
43
- path.join(__dirname, '..', '..', '..', 'src', 'cli', 'template', TEMPLATE_FILENAME),
44
- ];
45
- for (const candidate of candidates) {
46
- if (fs.existsSync(candidate)) {
47
- return candidate;
48
- }
49
- }
50
- throw new Error(`Could not locate ${TEMPLATE_FILENAME}. Tried:\n ${candidates.join('\n ')}`);
51
- }
52
- async run() {
53
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
54
- const onSigint = () => {
55
- rl.close();
56
- process.stdout.write('\n');
57
- process.exit(130);
58
- };
59
- process.once('SIGINT', onSigint);
60
- let orgName;
61
- let appIdentifier;
62
- try {
63
- orgName = (await this.prompt(rl, 'orgName: ')).trim();
64
- appIdentifier = (await this.prompt(rl, `appIdentifier (default: ${DEFAULT_APP_IDENTIFIER}): `)).trim() || DEFAULT_APP_IDENTIFIER;
65
- }
66
- finally {
67
- process.removeListener('SIGINT', onSigint);
68
- rl.close();
69
- }
70
- if (!orgName) {
71
- throw new Error('orgName is required');
72
- }
73
- const templatePath = this.findTemplate();
74
- const templateBody = fs.readFileSync(templatePath, 'utf8');
75
- const rendered = templateBody.split(ORG_PLACEHOLDER).join(orgName).split(APP_IDENTIFIER_PLACEHOLDER).join(appIdentifier);
76
- const outPath = path.resolve(process.cwd(), `${orgName}-flexplmConfig.json`);
77
- if (fs.existsSync(outPath)) {
78
- throw new Error(`Refusing to overwrite existing file: ${outPath}`);
79
- }
80
- fs.writeFileSync(outPath, rendered, 'utf8');
81
- console.log(`Created ${outPath}`);
82
- console.log('"orgName" and "appIdentifier" are pre-filled, but this file is not complete yet: FlexPLM connectivity also requires apiHost, userName, and password. See the "_availableAttributes" block in the file for these and other attributes this org can set (identifierAtts, LCSMaterial, etc.) — add the ones you need as real top-level keys, then delete "_availableAttributes"; the connector ignores it and applies defaults at runtime for anything you omit.');
83
- }
84
- }
85
- exports.ConfigCreateCommand = ConfigCreateCommand;
@@ -1 +0,0 @@
1
- export {};
@@ -1,85 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- Object.defineProperty(exports, "__esModule", { value: true });
26
- const fs = __importStar(require("fs"));
27
- const os = __importStar(require("os"));
28
- const path = __importStar(require("path"));
29
- let answers = [];
30
- jest.mock('readline', () => ({
31
- createInterface: () => ({
32
- question: (_q, cb) => cb(answers.shift() || ''),
33
- close: () => { },
34
- }),
35
- }));
36
- const config_create_1 = require("./config-create");
37
- describe('ConfigCreateCommand', () => {
38
- let tempDir;
39
- let originalCwd;
40
- let logSpy;
41
- beforeEach(() => {
42
- originalCwd = process.cwd();
43
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'flexplm-config-create-'));
44
- process.chdir(tempDir);
45
- logSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
46
- answers = [];
47
- });
48
- afterEach(() => {
49
- logSpy.mockRestore();
50
- process.chdir(originalCwd);
51
- fs.rmSync(tempDir, { recursive: true, force: true });
52
- });
53
- it('writes <orgName>-flexplmConfig.json with orgName and appIdentifier filled in', async () => {
54
- answers = ['acme', '@vibeiq/flexplm-connector'];
55
- await new config_create_1.ConfigCreateCommand().run();
56
- const outPath = path.join(tempDir, 'acme-flexplmConfig.json');
57
- expect(fs.existsSync(outPath)).toBe(true);
58
- const config = JSON.parse(fs.readFileSync(outPath, 'utf8'));
59
- expect(config.orgName).toEqual('acme');
60
- expect(config.appIdentifier).toEqual('@vibeiq/flexplm-connector');
61
- });
62
- it('trims whitespace around the orgName input', async () => {
63
- answers = [' acme ', '@vibeiq/flexplm-connector'];
64
- await new config_create_1.ConfigCreateCommand().run();
65
- expect(fs.existsSync(path.join(tempDir, 'acme-flexplmConfig.json'))).toBe(true);
66
- });
67
- it('defaults appIdentifier to @vibeiq/flexplm-connector when left blank', async () => {
68
- answers = ['acme', ''];
69
- await new config_create_1.ConfigCreateCommand().run();
70
- const outPath = path.join(tempDir, 'acme-flexplmConfig.json');
71
- const config = JSON.parse(fs.readFileSync(outPath, 'utf8'));
72
- expect(config.appIdentifier).toEqual('@vibeiq/flexplm-connector');
73
- });
74
- it('throws when orgName is empty', async () => {
75
- answers = [' '];
76
- await expect(new config_create_1.ConfigCreateCommand().run()).rejects.toThrow(/orgName is required/);
77
- });
78
- it('refuses to overwrite an existing file', async () => {
79
- const existing = path.join(tempDir, 'acme-flexplmConfig.json');
80
- fs.writeFileSync(existing, 'do not clobber', 'utf8');
81
- answers = ['acme', '@vibeiq/flexplm-connector'];
82
- await expect(new config_create_1.ConfigCreateCommand().run()).rejects.toThrow(/Refusing to overwrite/);
83
- expect(fs.readFileSync(existing, 'utf8')).toEqual('do not clobber');
84
- });
85
- });
@@ -1,19 +0,0 @@
1
- interface ConfigUploadOptions {
2
- filePath: string;
3
- message?: string;
4
- branch?: string;
5
- skipGit: boolean;
6
- updateConfig: boolean;
7
- }
8
- export declare class ConfigUploadCommand {
9
- static parseArgs(args: string[]): ConfigUploadOptions;
10
- static buildCommitMessage(userMessage: string, fileId: string): string;
11
- private prompt;
12
- private promptHidden;
13
- private runGit;
14
- private tryRunGit;
15
- private commitToGit;
16
- run(args: string[]): Promise<void>;
17
- private setConfigFileOnAppOrg;
18
- }
19
- export {};