@elek-io/core 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -444,7 +444,7 @@ var resolvedValueSchema = z4.union([
444
444
  function getValueContentSchemaFromDefinition(definition) {
445
445
  switch (definition.valueType) {
446
446
  case ValueTypeSchema.Enum.boolean:
447
- return getBooleanValueContentSchema(definition);
447
+ return getBooleanValueContentSchema();
448
448
  case ValueTypeSchema.Enum.number:
449
449
  return getNumberValueContentSchema(definition);
450
450
  case ValueTypeSchema.Enum.string:
@@ -458,7 +458,7 @@ function getValueContentSchemaFromDefinition(definition) {
458
458
  );
459
459
  }
460
460
  }
461
- function getBooleanValueContentSchema(definition) {
461
+ function getBooleanValueContentSchema() {
462
462
  return z4.boolean();
463
463
  }
464
464
  function getNumberValueContentSchema(definition) {
@@ -1082,6 +1082,12 @@ async function files(path, extension) {
1082
1082
 
1083
1083
  // src/service/AbstractCrudService.ts
1084
1084
  var AbstractCrudService = class {
1085
+ type;
1086
+ options;
1087
+ /**
1088
+ * Dynamically generated git messages for operations
1089
+ */
1090
+ gitMessage;
1085
1091
  /**
1086
1092
  * Do not instantiate directly as this is an abstract class
1087
1093
  */
@@ -1161,7 +1167,7 @@ var AbstractCrudService = class {
1161
1167
  possibleFiles.map(async (possibleFile) => {
1162
1168
  const fileNameArray = possibleFile.name.split(".");
1163
1169
  const fileReference = {
1164
- id: fileNameArray[0],
1170
+ id: fileNameArray[0] || "",
1165
1171
  language: fileNameArray.length === 3 ? fileNameArray[1] : void 0,
1166
1172
  extension: fileNameArray.length === 2 ? fileNameArray[1] : fileNameArray[2]
1167
1173
  };
@@ -1177,8 +1183,9 @@ var AbstractCrudService = class {
1177
1183
  };
1178
1184
 
1179
1185
  // src/service/AssetService.ts
1180
- import Fs3 from "fs-extra";
1181
- import IsSvg from "is-svg";
1186
+ import { fileTypeFromFile } from "file-type";
1187
+ import Fs2 from "fs-extra";
1188
+ import isSvg from "is-svg";
1182
1189
 
1183
1190
  // src/util/shared.ts
1184
1191
  import slugify from "slugify";
@@ -1194,8 +1201,7 @@ function slug(string) {
1194
1201
  return Slugify(string, {
1195
1202
  replacement: "-",
1196
1203
  // replace spaces with replacement character, defaults to `-`
1197
- remove: void 0,
1198
- // remove characters that match regex, defaults to `undefined`
1204
+ // remove: undefined, // remove characters that match regex, defaults to `undefined`
1199
1205
  lower: true,
1200
1206
  // convert to lower case, defaults to `false`
1201
1207
  strict: true
@@ -1203,81 +1209,10 @@ function slug(string) {
1203
1209
  });
1204
1210
  }
1205
1211
 
1206
- // src/service/JsonFileService.ts
1207
- import Fs2 from "fs-extra";
1208
- var JsonFileService = class extends AbstractCrudService {
1209
- constructor(options) {
1210
- super(serviceTypeSchema.Enum.JsonFile, options);
1211
- this.cache = /* @__PURE__ */ new Map();
1212
- }
1213
- /**
1214
- * Creates a new file on disk. Fails if path already exists
1215
- *
1216
- * @param data Data to write into the file
1217
- * @param path Path to write the file to
1218
- * @param schema Schema of the file to validate against
1219
- * @returns Validated content of the file from disk
1220
- */
1221
- async create(data, path, schema) {
1222
- const parsedData = schema.parse(data);
1223
- const string = this.serialize(parsedData);
1224
- await Fs2.writeFile(path, string, {
1225
- flag: "wx",
1226
- encoding: "utf8"
1227
- });
1228
- this.cache.set(path, parsedData);
1229
- return parsedData;
1230
- }
1231
- /**
1232
- * Reads the content of a file on disk. Fails if path does not exist
1233
- *
1234
- * @param path Path to read the file from
1235
- * @param schema Schema of the file to validate against
1236
- * @returns Validated content of the file from disk
1237
- */
1238
- async read(path, schema) {
1239
- if (this.cache.has(path)) {
1240
- return this.cache.get(path);
1241
- }
1242
- const data = await Fs2.readFile(path, {
1243
- flag: "r",
1244
- encoding: "utf8"
1245
- });
1246
- const json = this.deserialize(data);
1247
- const parsedData = schema.parse(json);
1248
- this.cache.set(path, parsedData);
1249
- return parsedData;
1250
- }
1251
- /**
1252
- * Overwrites an existing file on disk
1253
- *
1254
- * @todo Check how to error out if the file does not exist already
1255
- *
1256
- * @param data Data to write into the file
1257
- * @param path Path to the file to overwrite
1258
- * @param schema Schema of the file to validate against
1259
- * @returns Validated content of the file from disk
1260
- */
1261
- async update(data, path, schema) {
1262
- const parsedData = schema.parse(data);
1263
- const string = this.serialize(parsedData);
1264
- await Fs2.writeFile(path, string, {
1265
- flag: "w",
1266
- encoding: "utf8"
1267
- });
1268
- this.cache.set(path, parsedData);
1269
- return parsedData;
1270
- }
1271
- serialize(data) {
1272
- return JSON.stringify(data, null, this.options.file.json.indentation);
1273
- }
1274
- deserialize(data) {
1275
- return JSON.parse(data);
1276
- }
1277
- };
1278
-
1279
1212
  // src/service/AssetService.ts
1280
1213
  var AssetService = class extends AbstractCrudService {
1214
+ jsonFileService;
1215
+ gitService;
1281
1216
  constructor(options, jsonFileService, gitService) {
1282
1217
  super(serviceTypeSchema.Enum.Asset, options);
1283
1218
  this.jsonFileService = jsonFileService;
@@ -1310,7 +1245,7 @@ var AssetService = class extends AbstractCrudService {
1310
1245
  size
1311
1246
  };
1312
1247
  try {
1313
- await Fs3.copyFile(props.filePath, assetPath);
1248
+ await Fs2.copyFile(props.filePath, assetPath);
1314
1249
  await this.jsonFileService.create(
1315
1250
  assetFile,
1316
1251
  assetFilePath,
@@ -1371,8 +1306,8 @@ var AssetService = class extends AbstractCrudService {
1371
1306
  props.language,
1372
1307
  fileType.extension
1373
1308
  );
1374
- await Fs3.remove(prevAssetPath);
1375
- await Fs3.copyFile(props.newFilePath, assetPath);
1309
+ await Fs2.remove(prevAssetPath);
1310
+ await Fs2.copyFile(props.newFilePath, assetPath);
1376
1311
  assetFile.extension = fileType.extension;
1377
1312
  assetFile.mimeType = fileType.mimeType;
1378
1313
  assetFile.size = size;
@@ -1403,8 +1338,8 @@ var AssetService = class extends AbstractCrudService {
1403
1338
  props.language,
1404
1339
  props.extension
1405
1340
  );
1406
- await Fs3.remove(assetPath);
1407
- await Fs3.remove(assetFilePath);
1341
+ await Fs2.remove(assetPath);
1342
+ await Fs2.remove(assetFilePath);
1408
1343
  await this.gitService.add(projectPath, [assetFilePath, assetPath]);
1409
1344
  await this.gitService.commit(projectPath, this.gitMessage.delete);
1410
1345
  }
@@ -1453,7 +1388,7 @@ var AssetService = class extends AbstractCrudService {
1453
1388
  * @param path Path of the Asset to get the size from
1454
1389
  */
1455
1390
  async getAssetSize(path) {
1456
- return (await Fs3.stat(path)).size;
1391
+ return (await Fs2.stat(path)).size;
1457
1392
  }
1458
1393
  /**
1459
1394
  * Creates an Asset from given AssetFile
@@ -1481,17 +1416,16 @@ var AssetService = class extends AbstractCrudService {
1481
1416
  * @param filePath Path to the file to check
1482
1417
  */
1483
1418
  async getSupportedFileTypeOrThrow(filePath) {
1484
- const fileSize = (await Fs3.stat(filePath)).size;
1419
+ const fileSize = (await Fs2.stat(filePath)).size;
1485
1420
  if (fileSize / 1e3 <= 500) {
1486
- const fileBuffer = await Fs3.readFile(filePath);
1487
- if (IsSvg(fileBuffer.toString()) === true) {
1421
+ const fileBuffer = await Fs2.readFile(filePath);
1422
+ if (isSvg(fileBuffer.toString()) === true) {
1488
1423
  return {
1489
1424
  extension: supportedAssetExtensionSchema.Enum.svg,
1490
1425
  mimeType: supportedAssetMimeTypeSchema.Enum["image/svg+xml"]
1491
1426
  };
1492
1427
  }
1493
1428
  }
1494
- const { fileTypeFromFile } = await import("file-type");
1495
1429
  const fileType = await fileTypeFromFile(filePath);
1496
1430
  const result = supportedAssetTypeSchema.parse({
1497
1431
  extension: fileType?.ext,
@@ -1502,448 +1436,855 @@ var AssetService = class extends AbstractCrudService {
1502
1436
  };
1503
1437
 
1504
1438
  // src/service/CollectionService.ts
1505
- import Fs4 from "fs-extra";
1506
-
1507
- // src/service/GitService.ts
1508
- import { GitProcess } from "dugite";
1509
- import { EOL as EOL2 } from "os";
1510
- import PQueue from "p-queue";
1511
-
1512
- // src/service/GitTagService.ts
1513
- import { EOL } from "os";
1514
- var GitTagService = class extends AbstractCrudService {
1515
- constructor(options, git) {
1516
- super(serviceTypeSchema.Enum.GitTag, options);
1517
- this.git = git;
1439
+ import Fs3 from "fs-extra";
1440
+ var CollectionService = class extends AbstractCrudService {
1441
+ jsonFileService;
1442
+ gitService;
1443
+ constructor(options, jsonFileService, gitService) {
1444
+ super(serviceTypeSchema.Enum.Collection, options);
1445
+ this.jsonFileService = jsonFileService;
1446
+ this.gitService = gitService;
1518
1447
  }
1519
1448
  /**
1520
- * Creates a new tag
1521
- *
1522
- * @see https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---annotate
1449
+ * Creates a new Collection
1523
1450
  */
1524
1451
  async create(props) {
1525
- createGitTagSchema.parse(props);
1452
+ createCollectionSchema.parse(props);
1526
1453
  const id = uuid();
1527
- let args = ["tag", "--annotate", id];
1528
- if (props.hash) {
1529
- args = [...args, props.hash];
1530
- }
1531
- args = [...args, "-m", props.message];
1532
- await this.git(props.path, args);
1533
- const tag = await this.read({ path: props.path, id });
1534
- return tag;
1454
+ const projectPath = pathTo.project(props.projectId);
1455
+ const collectionPath = pathTo.collection(props.projectId, id);
1456
+ const collectionFilePath = pathTo.collectionFile(props.projectId, id);
1457
+ const collectionFile = {
1458
+ ...props,
1459
+ objectType: "collection",
1460
+ id,
1461
+ slug: {
1462
+ singular: slug(props.slug.singular),
1463
+ plural: slug(props.slug.plural)
1464
+ },
1465
+ created: currentTimestamp(),
1466
+ updated: null
1467
+ };
1468
+ await Fs3.ensureDir(collectionPath);
1469
+ await this.jsonFileService.create(
1470
+ collectionFile,
1471
+ collectionFilePath,
1472
+ collectionFileSchema
1473
+ );
1474
+ await this.gitService.add(projectPath, [collectionFilePath]);
1475
+ await this.gitService.commit(projectPath, this.gitMessage.create);
1476
+ return collectionFile;
1535
1477
  }
1536
1478
  /**
1537
- * Returns a tag by ID
1538
- *
1539
- * Internally uses list() but only returns the tag with matching ID.
1479
+ * Returns a Collection by ID
1540
1480
  */
1541
1481
  async read(props) {
1542
- readGitTagSchema.parse(props);
1543
- const tags = await this.list({ path: props.path });
1544
- const tag = tags.list.find((tag2) => {
1545
- return tag2.id === props.id;
1546
- });
1547
- if (!tag) {
1548
- throw new GitError(
1549
- `Provided tag with UUID "${props.id}" did not match any known tags`
1550
- );
1551
- }
1552
- return tag;
1482
+ readCollectionSchema.parse(props);
1483
+ const collection = await this.jsonFileService.read(
1484
+ pathTo.collectionFile(props.projectId, props.id),
1485
+ collectionFileSchema
1486
+ );
1487
+ return collection;
1553
1488
  }
1554
1489
  /**
1555
- * Updating a git tag is not supported.
1556
- * Please delete the old and create a new one
1490
+ * Updates given Collection
1557
1491
  *
1558
- * @see https://git-scm.com/docs/git-tag#_on_re_tagging
1492
+ * @todo finish implementing checks for FieldDefinitions and extract methods
1493
+ *
1494
+ * @param projectId Project ID of the collection to update
1495
+ * @param collection Collection to write to disk
1496
+ * @returns An object containing information about the actions needed to be taken,
1497
+ * before given update can be executed or void if the update was executed successfully
1559
1498
  */
1560
- async update() {
1561
- throw new Error(
1562
- "Updating a git tag is not supported. Please delete the old and create a new one"
1499
+ async update(props) {
1500
+ updateCollectionSchema.parse(props);
1501
+ const projectPath = pathTo.project(props.projectId);
1502
+ const collectionFilePath = pathTo.collectionFile(props.projectId, props.id);
1503
+ const prevCollectionFile = await this.read(props);
1504
+ const collectionFile = {
1505
+ ...prevCollectionFile,
1506
+ ...props,
1507
+ updated: currentTimestamp()
1508
+ };
1509
+ await this.jsonFileService.update(
1510
+ collectionFile,
1511
+ collectionFilePath,
1512
+ collectionFileSchema
1563
1513
  );
1514
+ await this.gitService.add(projectPath, [collectionFilePath]);
1515
+ await this.gitService.commit(projectPath, this.gitMessage.update);
1516
+ return collectionFile;
1564
1517
  }
1565
1518
  /**
1566
- * Deletes a tag
1567
- *
1568
- * @see https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---delete
1519
+ * Deletes given Collection (folder), including it's items
1569
1520
  *
1570
- * @param path Path to the repository
1571
- * @param id UUID of the tag to delete
1521
+ * The Fields that Collection used are not deleted.
1572
1522
  */
1573
1523
  async delete(props) {
1574
- deleteGitTagSchema.parse(props);
1575
- const args = ["tag", "--delete", props.id];
1576
- await this.git(props.path, args);
1524
+ deleteCollectionSchema.parse(props);
1525
+ const projectPath = pathTo.project(props.projectId);
1526
+ const collectionPath = pathTo.collection(props.projectId, props.id);
1527
+ await Fs3.remove(collectionPath);
1528
+ await this.gitService.add(projectPath, [collectionPath]);
1529
+ await this.gitService.commit(projectPath, this.gitMessage.delete);
1577
1530
  }
1578
- /**
1579
- * Gets all local tags or filter them by pattern
1580
- *
1581
- * They are sorted by authordate of the commit, not the timestamp the tag is created.
1582
- * This ensures tags are sorted correctly in the timeline of their commits.
1583
- *
1584
- * @see https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---list
1585
- */
1586
1531
  async list(props) {
1587
- listGitTagsSchema.parse(props);
1588
- let args = ["tag", "--list"];
1589
- args = [
1590
- ...args,
1591
- "--sort=-*authordate",
1592
- "--format=%(refname:short)|%(subject)|%(*authorname)|%(*authoremail)|%(*authordate:unix)"
1593
- ];
1594
- const result = await this.git(props.path, args);
1595
- const noEmptyLinesArr = result.stdout.split(EOL).filter((line) => {
1596
- return line.trim() !== "";
1597
- });
1598
- const lineObjArr = noEmptyLinesArr.map((line) => {
1599
- const lineArray = line.split("|");
1600
- return {
1601
- id: lineArray[0],
1602
- message: lineArray[1],
1603
- author: {
1604
- name: lineArray[2],
1605
- email: lineArray[3]
1606
- },
1607
- timestamp: parseInt(lineArray[4])
1608
- };
1609
- });
1610
- const gitTags = lineObjArr.filter(this.isGitTag.bind(this));
1532
+ listCollectionsSchema.parse(props);
1533
+ const offset = props.offset || 0;
1534
+ const limit = props.limit || 15;
1535
+ const collectionReferences = await this.listReferences(
1536
+ objectTypeSchema.Enum.collection,
1537
+ props.projectId
1538
+ );
1539
+ const partialCollectionReferences = collectionReferences.slice(
1540
+ offset,
1541
+ limit
1542
+ );
1543
+ const collections = await returnResolved(
1544
+ partialCollectionReferences.map((reference) => {
1545
+ return this.read({
1546
+ projectId: props.projectId,
1547
+ id: reference.id
1548
+ });
1549
+ })
1550
+ );
1611
1551
  return {
1612
- total: gitTags.length,
1613
- limit: 0,
1614
- offset: 0,
1615
- list: gitTags
1552
+ total: collectionReferences.length,
1553
+ limit,
1554
+ offset,
1555
+ list: collections
1616
1556
  };
1617
1557
  }
1618
- /**
1619
- * Returns the total number of tags inside given repository
1620
- *
1621
- * Internally uses list(), so do not use count()
1622
- * in conjuncion with it to avoid multiple git calls.
1623
- *
1624
- * @param path Path to the repository
1625
- */
1626
1558
  async count(props) {
1627
- countGitTagsSchema.parse(props);
1628
- const gitTags = await this.list({ path: props.path });
1629
- return gitTags.total;
1559
+ countCollectionsSchema.parse(props);
1560
+ const count = (await this.listReferences(
1561
+ objectTypeSchema.Enum.collection,
1562
+ props.projectId
1563
+ )).length;
1564
+ return count;
1630
1565
  }
1631
1566
  /**
1632
- * Type guard for GitTag
1633
- *
1634
- * @param obj The object to check
1567
+ * Checks if given object is of type Collection
1635
1568
  */
1636
- isGitTag(obj) {
1637
- return gitTagSchema.safeParse(obj).success;
1569
+ isCollection(obj) {
1570
+ return collectionFileSchema.safeParse(obj).success;
1638
1571
  }
1639
1572
  };
1640
1573
 
1641
- // src/service/UserService.ts
1642
- var UserService = class {
1643
- constructor(jsonFileService) {
1574
+ // src/service/EntryService.ts
1575
+ import Fs4 from "fs-extra";
1576
+ var EntryService = class extends AbstractCrudService {
1577
+ jsonFileService;
1578
+ gitService;
1579
+ collectionService;
1580
+ assetService;
1581
+ // private sharedValueService: SharedValueService;
1582
+ constructor(options, jsonFileService, gitService, collectionService, assetService) {
1583
+ super(serviceTypeSchema.Enum.Entry, options);
1644
1584
  this.jsonFileService = jsonFileService;
1585
+ this.gitService = gitService;
1586
+ this.collectionService = collectionService;
1587
+ this.assetService = assetService;
1645
1588
  }
1646
1589
  /**
1647
- * Returns the User currently working with Core
1590
+ * Creates a new Entry for given Collection
1648
1591
  */
1649
- async get() {
1650
- try {
1651
- return await this.jsonFileService.read(pathTo.userFile, userFileSchema);
1652
- } catch (error) {
1653
- return void 0;
1654
- }
1592
+ async create(props) {
1593
+ createEntrySchema.parse(props);
1594
+ const id = uuid();
1595
+ const projectPath = pathTo.project(props.projectId);
1596
+ const entryFilePath = pathTo.entryFile(
1597
+ props.projectId,
1598
+ props.collectionId,
1599
+ id
1600
+ );
1601
+ const collection = await this.collectionService.read({
1602
+ projectId: props.projectId,
1603
+ id: props.collectionId
1604
+ });
1605
+ const entryFile = {
1606
+ objectType: "entry",
1607
+ id,
1608
+ values: props.values,
1609
+ created: currentTimestamp(),
1610
+ updated: null
1611
+ };
1612
+ const entry = await this.toEntry({
1613
+ projectId: props.projectId,
1614
+ collectionId: props.collectionId,
1615
+ entryFile
1616
+ });
1617
+ this.validateValues({
1618
+ collectionId: props.collectionId,
1619
+ valueDefinitions: collection.valueDefinitions,
1620
+ values: entry.values
1621
+ });
1622
+ await this.jsonFileService.create(
1623
+ entryFile,
1624
+ entryFilePath,
1625
+ entryFileSchema
1626
+ );
1627
+ await this.gitService.add(projectPath, [entryFilePath]);
1628
+ await this.gitService.commit(projectPath, this.gitMessage.create);
1629
+ return entry;
1655
1630
  }
1656
1631
  /**
1657
- * Sets the User currently working with Core
1658
- *
1659
- * By doing so all git operations are done with the signature of this User
1632
+ * Returns an Entry from given Collection by ID and language
1660
1633
  */
1661
- async set(props) {
1662
- setUserSchema.parse(props);
1663
- const userFilePath = pathTo.userFile;
1664
- const userFile = {
1665
- ...props
1666
- };
1667
- if (userFile.userType === UserTypeSchema.Enum.cloud) {
1668
- }
1669
- await this.jsonFileService.update(userFile, userFilePath, userFileSchema);
1670
- return userFile;
1634
+ async read(props) {
1635
+ readEntrySchema.parse(props);
1636
+ const entryFile = await this.jsonFileService.read(
1637
+ pathTo.entryFile(props.projectId, props.collectionId, props.id),
1638
+ entryFileSchema
1639
+ );
1640
+ return await this.toEntry({
1641
+ projectId: props.projectId,
1642
+ collectionId: props.collectionId,
1643
+ entryFile
1644
+ });
1671
1645
  }
1672
- };
1673
-
1674
- // src/service/GitService.ts
1675
- var GitService2 = class {
1676
- constructor(options, userService) {
1677
- this.branches = {
1678
- /**
1679
- * List branches
1680
- *
1681
- * @see https://www.git-scm.com/docs/git-branch
1682
- *
1683
- * @param path Path to the repository
1684
- */
1685
- list: async (path) => {
1686
- const args = ["branch", "--list", "--all"];
1687
- const result = await this.git(path, args);
1688
- const normalizedLinesArr = result.stdout.split(EOL2).filter((line) => {
1689
- return line.trim() !== "";
1690
- }).map((line) => {
1691
- return line.trim().replace("* ", "");
1692
- });
1693
- const local = [];
1694
- const remote = [];
1695
- normalizedLinesArr.forEach((line) => {
1696
- if (line.startsWith("remotes/")) {
1697
- remote.push(line.replace("remotes/", ""));
1698
- } else {
1699
- local.push(line);
1700
- }
1701
- });
1702
- return {
1703
- local,
1704
- remote
1705
- };
1706
- },
1707
- /**
1708
- * Returns the name of the current branch. In detached HEAD state, an empty string is returned.
1709
- *
1710
- * @see https://www.git-scm.com/docs/git-branch#Documentation/git-branch.txt---show-current
1711
- *
1712
- * @param path Path to the repository
1713
- */
1714
- current: async (path) => {
1715
- const args = ["branch", "--show-current"];
1716
- const result = await this.git(path, args);
1717
- return result.stdout.trim();
1718
- },
1719
- /**
1720
- * Switch branches
1721
- *
1722
- * @see https://git-scm.com/docs/git-switch/
1723
- *
1724
- * @param path Path to the repository
1725
- * @param branch Name of the branch to switch to
1726
- * @param options Options specific to the switch operation
1727
- */
1728
- switch: async (path, branch, options) => {
1729
- await this.checkBranchOrTagName(path, branch);
1730
- let args = ["switch"];
1731
- if (options?.isNew === true) {
1732
- args = [...args, "--create", branch];
1733
- } else {
1734
- args = [...args, branch];
1735
- }
1736
- await this.git(path, args);
1737
- }
1646
+ /**
1647
+ * Updates an Entry of given Collection with new Values and shared Values
1648
+ */
1649
+ async update(props) {
1650
+ updateEntrySchema.parse(props);
1651
+ const projectPath = pathTo.project(props.projectId);
1652
+ const entryFilePath = pathTo.entryFile(
1653
+ props.projectId,
1654
+ props.collectionId,
1655
+ props.id
1656
+ );
1657
+ const collection = await this.collectionService.read({
1658
+ projectId: props.projectId,
1659
+ id: props.collectionId
1660
+ });
1661
+ const prevEntryFile = await this.read({
1662
+ projectId: props.projectId,
1663
+ collectionId: props.collectionId,
1664
+ id: props.id
1665
+ });
1666
+ const entryFile = {
1667
+ ...prevEntryFile,
1668
+ values: props.values,
1669
+ updated: currentTimestamp()
1738
1670
  };
1739
- this.remotes = {
1740
- /**
1741
- * Returns a list of currently tracked remotes
1742
- *
1743
- * @see https://git-scm.com/docs/git-remote
1744
- *
1745
- * @param path Path to the repository
1746
- */
1747
- list: async (path) => {
1748
- const args = ["remote"];
1749
- const result = await this.git(path, args);
1750
- const normalizedLinesArr = result.stdout.split(EOL2).filter((line) => {
1751
- return line.trim() !== "";
1671
+ const entry = await this.toEntry({
1672
+ projectId: props.projectId,
1673
+ collectionId: props.collectionId,
1674
+ entryFile
1675
+ });
1676
+ this.validateValues({
1677
+ collectionId: props.collectionId,
1678
+ valueDefinitions: collection.valueDefinitions,
1679
+ values: entry.values
1680
+ });
1681
+ await this.jsonFileService.update(
1682
+ entryFile,
1683
+ entryFilePath,
1684
+ entryFileSchema
1685
+ );
1686
+ await this.gitService.add(projectPath, [entryFilePath]);
1687
+ await this.gitService.commit(projectPath, this.gitMessage.update);
1688
+ return entry;
1689
+ }
1690
+ /**
1691
+ * Deletes given Entry from it's Collection
1692
+ */
1693
+ async delete(props) {
1694
+ deleteEntrySchema.parse(props);
1695
+ const projectPath = pathTo.project(props.projectId);
1696
+ const entryFilePath = pathTo.entryFile(
1697
+ props.projectId,
1698
+ props.collectionId,
1699
+ props.id
1700
+ );
1701
+ await Fs4.remove(entryFilePath);
1702
+ await this.gitService.add(projectPath, [entryFilePath]);
1703
+ await this.gitService.commit(projectPath, this.gitMessage.delete);
1704
+ }
1705
+ async list(props) {
1706
+ listEntriesSchema.parse(props);
1707
+ const offset = props.offset || 0;
1708
+ const limit = props.limit || 15;
1709
+ const entryReferences = await this.listReferences(
1710
+ objectTypeSchema.Enum.entry,
1711
+ props.projectId,
1712
+ props.collectionId
1713
+ );
1714
+ const partialEntryReferences = entryReferences.slice(offset, limit);
1715
+ const entries = await returnResolved(
1716
+ partialEntryReferences.map((reference) => {
1717
+ return this.read({
1718
+ projectId: props.projectId,
1719
+ collectionId: props.collectionId,
1720
+ id: reference.id
1752
1721
  });
1753
- return normalizedLinesArr;
1754
- },
1755
- /**
1756
- * Returns true if the `origin` remote exists, otherwise false
1757
- *
1758
- * @param path Path to the repository
1759
- */
1760
- hasOrigin: async (path) => {
1761
- const remotes = await this.remotes.list(path);
1762
- if (remotes.includes("origin")) {
1763
- return true;
1764
- }
1765
- return false;
1766
- },
1767
- /**
1768
- * Adds the `origin` remote with given URL
1769
- *
1770
- * Throws if `origin` remote is added already.
1771
- *
1772
- * @see https://git-scm.com/docs/git-remote#Documentation/git-remote.txt-emaddem
1773
- *
1774
- * @param path Path to the repository
1775
- */
1776
- addOrigin: async (path, url) => {
1777
- const args = ["remote", "add", "origin", url];
1778
- await this.git(path, args);
1779
- },
1780
- /**
1781
- * Returns the current `origin` remote URL
1782
- *
1783
- * Throws if no `origin` remote is added yet.
1784
- *
1785
- * @see https://git-scm.com/docs/git-remote#Documentation/git-remote.txt-emget-urlem
1786
- *
1787
- * @param path Path to the repository
1788
- */
1789
- getOriginUrl: async (path) => {
1790
- const args = ["remote", "get-url", "origin"];
1791
- const result = (await this.git(path, args)).stdout.trim();
1792
- return result.length === 0 ? null : result;
1793
- },
1794
- /**
1795
- * Sets the current `origin` remote URL
1796
- *
1797
- * Throws if no `origin` remote is added yet.
1798
- *
1799
- * @see https://git-scm.com/docs/git-remote#Documentation/git-remote.txt-emset-urlem
1800
- *
1801
- * @param path Path to the repository
1802
- */
1803
- setOriginUrl: async (path, url) => {
1804
- const args = ["remote", "set-url", "origin", url];
1805
- await this.git(path, args);
1806
- }
1722
+ })
1723
+ );
1724
+ return {
1725
+ total: entryReferences.length,
1726
+ limit,
1727
+ offset,
1728
+ list: entries
1807
1729
  };
1808
- this.version = null;
1809
- this.gitPath = null;
1810
- this.queue = new PQueue({
1811
- concurrency: 1
1812
- // No concurrency because git operations are sequencial
1813
- });
1814
- this.gitTagService = new GitTagService(options, this.git);
1815
- this.userService = userService;
1816
- this.updateVersion();
1817
- this.updateGitPath();
1730
+ }
1731
+ async count(props) {
1732
+ countEntriesSchema.parse(props);
1733
+ return (await this.listReferences(
1734
+ objectTypeSchema.Enum.entry,
1735
+ props.projectId,
1736
+ props.collectionId
1737
+ )).length;
1818
1738
  }
1819
1739
  /**
1820
- * CRUD methods to work with git tags
1740
+ * Checks if given object is of type Entry
1821
1741
  */
1822
- get tags() {
1823
- return this.gitTagService;
1742
+ isEntry(obj) {
1743
+ return entrySchema.safeParse(obj).success;
1824
1744
  }
1825
1745
  /**
1826
- * Create an empty Git repository or reinitialize an existing one
1827
- *
1828
- * @see https://git-scm.com/docs/git-init
1829
- *
1830
- * @param path Path to initialize in. Fails if path does not exist
1831
- * @param options Options specific to the init operation
1746
+ * Returns a Value definition by ID
1832
1747
  */
1833
- async init(path, options) {
1834
- let args = ["init"];
1835
- if (options?.initialBranch) {
1836
- args = [...args, `--initial-branch=${options.initialBranch}`];
1837
- }
1838
- await this.git(path, args);
1839
- await this.setLocalConfig(path);
1840
- await this.installLfs(path);
1748
+ getValueDefinitionById(props) {
1749
+ const definition = props.valueDefinitions.find((def) => {
1750
+ if (def.id === props.id) {
1751
+ return true;
1752
+ }
1753
+ return false;
1754
+ });
1755
+ if (!definition) {
1756
+ throw new Error(
1757
+ `No definition with ID "${props.id}" found in Collection "${props.collectionId}" for given Value reference`
1758
+ );
1759
+ }
1760
+ return definition;
1841
1761
  }
1842
1762
  /**
1843
- * Clone a repository into a directory
1844
- *
1845
- * @see https://git-scm.com/docs/git-clone
1846
- *
1847
- * @todo Implement progress callback / events
1848
- *
1849
- * @param url The remote repository URL to clone from
1850
- * @param path The destination path for the cloned repository.
1851
- * Which is only working if the directory is existing and empty.
1852
- * @param options Options specific to the clone operation
1763
+ * Validates given Values against it's Collections definitions
1853
1764
  */
1854
- async clone(url, path, options) {
1855
- let args = ["clone", "--progress"];
1856
- if (options?.branch) {
1857
- args = [...args, "--branch", options.branch];
1858
- }
1859
- if (options?.depth) {
1860
- args = [...args, "--depth", options.depth.toString()];
1765
+ validateValues(props) {
1766
+ props.values.map((value) => {
1767
+ const definition = this.getValueDefinitionById({
1768
+ collectionId: props.collectionId,
1769
+ valueDefinitions: props.valueDefinitions,
1770
+ id: value.definitionId
1771
+ });
1772
+ const schema = getValueContentSchemaFromDefinition(definition);
1773
+ try {
1774
+ for (const [, content] of Object.entries(value.content)) {
1775
+ schema.parse(content);
1776
+ }
1777
+ } catch (error) {
1778
+ console.log("Definition:", definition);
1779
+ console.log("Value:", value);
1780
+ throw error;
1781
+ }
1782
+ });
1783
+ }
1784
+ /**
1785
+ * Validates given shared Value references against it's Collections definitions
1786
+ */
1787
+ // private validateResolvedSharedValues(props: {
1788
+ // collectionId: string;
1789
+ // valueDefinitions: ValueDefinition[];
1790
+ // resolvedSharedValues: ResolvedSharedValueReference[];
1791
+ // }) {
1792
+ // props.resolvedSharedValues.map((value) => {
1793
+ // const definition = this.getValueDefinitionById({
1794
+ // collectionId: props.collectionId,
1795
+ // valueDefinitions: props.valueDefinitions,
1796
+ // id: value.definitionId,
1797
+ // });
1798
+ // const schema = getValueSchemaFromDefinition(definition);
1799
+ // schema.parse(value.resolved.content);
1800
+ // });
1801
+ // }
1802
+ async resolveValueContentReference(props) {
1803
+ switch (props.valueContentReference.objectType) {
1804
+ case objectTypeSchema.Enum.asset:
1805
+ return await this.assetService.read({
1806
+ projectId: props.projectId,
1807
+ id: props.valueContentReference.id,
1808
+ language: props.valueContentReference.language
1809
+ });
1810
+ case objectTypeSchema.Enum.entry:
1811
+ return await this.read({
1812
+ projectId: props.projectId,
1813
+ collectionId: props.collectionId,
1814
+ id: props.valueContentReference.id
1815
+ });
1816
+ default:
1817
+ throw new Error(
1818
+ // @ts-ignore
1819
+ `Tried to resolve unsupported Value reference "${props.valueContentReference.referenceObjectType}"`
1820
+ );
1861
1821
  }
1862
- if (options?.singleBranch === true) {
1863
- args = [...args, "--single-branch"];
1822
+ }
1823
+ async resolveValueContentReferences(props) {
1824
+ let resolvedContent = {};
1825
+ for (const language in props.valueReference.content) {
1826
+ const referencesOfLanguage = props.valueReference.content[language];
1827
+ if (!referencesOfLanguage) {
1828
+ throw new Error(
1829
+ `Trying to access content references by language "${language}" failed`
1830
+ );
1831
+ }
1832
+ const resolvedReferencesOfLanguage = await Promise.all(
1833
+ referencesOfLanguage.map(async (reference) => {
1834
+ return await this.resolveValueContentReference({
1835
+ projectId: props.projectId,
1836
+ collectionId: props.collectionId,
1837
+ valueContentReference: reference
1838
+ });
1839
+ })
1840
+ );
1841
+ resolvedContent = {
1842
+ ...resolvedContent,
1843
+ [language]: resolvedReferencesOfLanguage
1844
+ };
1864
1845
  }
1865
- await this.git("", [...args, url, path]);
1866
- await this.setLocalConfig(path);
1846
+ return resolvedContent;
1867
1847
  }
1868
1848
  /**
1869
- * Add file contents to the index
1870
- *
1871
- * @see https://git-scm.com/docs/git-add
1872
- *
1873
- * @param path Path to the repository
1874
- * @param files Files to add
1849
+ * Creates an Entry from given EntryFile by resolving it's Values
1875
1850
  */
1876
- async add(path, files2) {
1877
- const args = ["add", "--", ...files2];
1878
- await this.git(path, args);
1851
+ async toEntry(props) {
1852
+ return {
1853
+ ...props.entryFile,
1854
+ // @ts-ignore @todo fixme - I have no idea why this happens. The types seem to be compatible to me and they work
1855
+ values: await Promise.all(
1856
+ props.entryFile.values.map(async (value) => {
1857
+ if (value.valueType === ValueTypeSchema.Enum.reference) {
1858
+ const resolvedContentReferences = await this.resolveValueContentReferences({
1859
+ projectId: props.projectId,
1860
+ collectionId: props.collectionId,
1861
+ valueReference: value
1862
+ });
1863
+ return {
1864
+ ...value,
1865
+ content: resolvedContentReferences
1866
+ };
1867
+ }
1868
+ return value;
1869
+ })
1870
+ )
1871
+ };
1872
+ }
1873
+ };
1874
+
1875
+ // src/service/GitService.ts
1876
+ import { GitProcess } from "dugite";
1877
+ import { EOL as EOL2 } from "os";
1878
+ import PQueue from "p-queue";
1879
+
1880
+ // src/service/GitTagService.ts
1881
+ import { EOL } from "os";
1882
+ var GitTagService = class extends AbstractCrudService {
1883
+ git;
1884
+ constructor(options, git) {
1885
+ super(serviceTypeSchema.Enum.GitTag, options);
1886
+ this.git = git;
1879
1887
  }
1880
1888
  /**
1881
- * Reset current HEAD to the specified state
1882
- *
1883
- * @todo maybe add more options
1884
- * @see https://git-scm.com/docs/git-reset
1889
+ * Creates a new tag
1885
1890
  *
1886
- * @param path Path to the repository
1887
- * @param mode Modifies the working tree depending on given mode
1888
- * @param commit Resets the current branch head to this commit / tag
1891
+ * @see https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---annotate
1889
1892
  */
1890
- async reset(path, mode, commit) {
1891
- const args = ["reset", `--${mode}`, commit];
1892
- await this.git(path, args);
1893
+ async create(props) {
1894
+ createGitTagSchema.parse(props);
1895
+ const id = uuid();
1896
+ let args = ["tag", "--annotate", id];
1897
+ if (props.hash) {
1898
+ args = [...args, props.hash];
1899
+ }
1900
+ args = [...args, "-m", props.message];
1901
+ await this.git(props.path, args);
1902
+ const tag = await this.read({ path: props.path, id });
1903
+ return tag;
1893
1904
  }
1894
1905
  /**
1895
- * Restore working tree files
1896
- *
1897
- * @see https://git-scm.com/docs/git-restore/
1898
- *
1899
- * @todo It's probably a good idea to not use restore
1900
- * for a use case where someone just wants to have a look
1901
- * and maybe copy something from a deleted file.
1902
- * We should use `checkout` without `add .` and `commit` for that
1906
+ * Returns a tag by ID
1903
1907
  *
1904
- * @param path Path to the repository
1905
- * @param source Git commit SHA or tag name to restore to
1906
- * @param files Files to restore
1908
+ * Internally uses list() but only returns the tag with matching ID.
1907
1909
  */
1908
- // public async restore(
1909
- // path: string,
1910
- // source: string,
1911
- // files: string[]
1912
- // ): Promise<void> {
1913
- // const args = ['restore', `--source=${source}`, ...files];
1914
- // await this.git(path, args);
1915
- // }
1910
+ async read(props) {
1911
+ readGitTagSchema.parse(props);
1912
+ const tags = await this.list({ path: props.path });
1913
+ const tag = tags.list.find((tag2) => {
1914
+ return tag2.id === props.id;
1915
+ });
1916
+ if (!tag) {
1917
+ throw new GitError(
1918
+ `Provided tag with UUID "${props.id}" did not match any known tags`
1919
+ );
1920
+ }
1921
+ return tag;
1922
+ }
1916
1923
  /**
1917
- * Download objects and refs from remote `origin`
1918
- *
1919
- * @see https://www.git-scm.com/docs/git-fetch
1924
+ * Updating a git tag is not supported.
1925
+ * Please delete the old and create a new one
1920
1926
  *
1921
- * @param path Path to the repository
1927
+ * @see https://git-scm.com/docs/git-tag#_on_re_tagging
1922
1928
  */
1923
- async fetch(path) {
1924
- const args = ["fetch"];
1925
- await this.git(path, args);
1929
+ async update() {
1930
+ throw new Error(
1931
+ "Updating a git tag is not supported. Please delete the old and create a new one"
1932
+ );
1926
1933
  }
1927
1934
  /**
1928
- * Fetch from and integrate (rebase or merge) with a local branch
1935
+ * Deletes a tag
1929
1936
  *
1930
- * @see https://git-scm.com/docs/git-pull
1937
+ * @see https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---delete
1931
1938
  *
1932
- * @param path Path to the repository
1939
+ * @param path Path to the repository
1940
+ * @param id UUID of the tag to delete
1933
1941
  */
1934
- async pull(path) {
1935
- const args = ["pull"];
1936
- await this.git(path, args);
1942
+ async delete(props) {
1943
+ deleteGitTagSchema.parse(props);
1944
+ const args = ["tag", "--delete", props.id];
1945
+ await this.git(props.path, args);
1937
1946
  }
1938
1947
  /**
1939
- * Update remote refs along with associated objects to remote `origin`
1948
+ * Gets all local tags or filter them by pattern
1940
1949
  *
1941
- * @see https://git-scm.com/docs/git-push
1950
+ * They are sorted by authordate of the commit, not the timestamp the tag is created.
1951
+ * This ensures tags are sorted correctly in the timeline of their commits.
1942
1952
  *
1943
- * @param path Path to the repository
1953
+ * @see https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---list
1944
1954
  */
1945
- async push(path, options) {
1946
- let args = ["push", "origin"];
1955
+ async list(props) {
1956
+ listGitTagsSchema.parse(props);
1957
+ let args = ["tag", "--list"];
1958
+ args = [
1959
+ ...args,
1960
+ "--sort=-*authordate",
1961
+ "--format=%(refname:short)|%(subject)|%(*authorname)|%(*authoremail)|%(*authordate:unix)"
1962
+ ];
1963
+ const result = await this.git(props.path, args);
1964
+ const noEmptyLinesArr = result.stdout.split(EOL).filter((line) => {
1965
+ return line.trim() !== "";
1966
+ });
1967
+ const lineObjArr = noEmptyLinesArr.map((line) => {
1968
+ const lineArray = line.split("|");
1969
+ return {
1970
+ id: lineArray[0],
1971
+ message: lineArray[1],
1972
+ author: {
1973
+ name: lineArray[2],
1974
+ email: lineArray[3]
1975
+ },
1976
+ timestamp: parseInt(lineArray[4] || "")
1977
+ };
1978
+ });
1979
+ const gitTags = lineObjArr.filter(this.isGitTag.bind(this));
1980
+ return {
1981
+ total: gitTags.length,
1982
+ limit: 0,
1983
+ offset: 0,
1984
+ list: gitTags
1985
+ };
1986
+ }
1987
+ /**
1988
+ * Returns the total number of tags inside given repository
1989
+ *
1990
+ * Internally uses list(), so do not use count()
1991
+ * in conjuncion with it to avoid multiple git calls.
1992
+ *
1993
+ * @param path Path to the repository
1994
+ */
1995
+ async count(props) {
1996
+ countGitTagsSchema.parse(props);
1997
+ const gitTags = await this.list({ path: props.path });
1998
+ return gitTags.total;
1999
+ }
2000
+ /**
2001
+ * Type guard for GitTag
2002
+ *
2003
+ * @param obj The object to check
2004
+ */
2005
+ isGitTag(obj) {
2006
+ return gitTagSchema.safeParse(obj).success;
2007
+ }
2008
+ };
2009
+
2010
+ // src/service/GitService.ts
2011
+ var GitService = class {
2012
+ version;
2013
+ gitPath;
2014
+ queue;
2015
+ gitTagService;
2016
+ userService;
2017
+ constructor(options, userService) {
2018
+ this.version = null;
2019
+ this.gitPath = null;
2020
+ this.queue = new PQueue({
2021
+ concurrency: 1
2022
+ // No concurrency because git operations are sequencial
2023
+ });
2024
+ this.gitTagService = new GitTagService(options, this.git);
2025
+ this.userService = userService;
2026
+ this.updateVersion();
2027
+ this.updateGitPath();
2028
+ }
2029
+ /**
2030
+ * CRUD methods to work with git tags
2031
+ */
2032
+ get tags() {
2033
+ return this.gitTagService;
2034
+ }
2035
+ /**
2036
+ * Create an empty Git repository or reinitialize an existing one
2037
+ *
2038
+ * @see https://git-scm.com/docs/git-init
2039
+ *
2040
+ * @param path Path to initialize in. Fails if path does not exist
2041
+ * @param options Options specific to the init operation
2042
+ */
2043
+ async init(path, options) {
2044
+ let args = ["init"];
2045
+ if (options?.initialBranch) {
2046
+ args = [...args, `--initial-branch=${options.initialBranch}`];
2047
+ }
2048
+ await this.git(path, args);
2049
+ await this.setLocalConfig(path);
2050
+ await this.installLfs(path);
2051
+ }
2052
+ /**
2053
+ * Clone a repository into a directory
2054
+ *
2055
+ * @see https://git-scm.com/docs/git-clone
2056
+ *
2057
+ * @todo Implement progress callback / events
2058
+ *
2059
+ * @param url The remote repository URL to clone from
2060
+ * @param path The destination path for the cloned repository.
2061
+ * Which is only working if the directory is existing and empty.
2062
+ * @param options Options specific to the clone operation
2063
+ */
2064
+ async clone(url, path, options) {
2065
+ let args = ["clone", "--progress"];
2066
+ if (options?.branch) {
2067
+ args = [...args, "--branch", options.branch];
2068
+ }
2069
+ if (options?.depth) {
2070
+ args = [...args, "--depth", options.depth.toString()];
2071
+ }
2072
+ if (options?.singleBranch === true) {
2073
+ args = [...args, "--single-branch"];
2074
+ }
2075
+ await this.git("", [...args, url, path]);
2076
+ await this.setLocalConfig(path);
2077
+ }
2078
+ /**
2079
+ * Add file contents to the index
2080
+ *
2081
+ * @see https://git-scm.com/docs/git-add
2082
+ *
2083
+ * @param path Path to the repository
2084
+ * @param files Files to add
2085
+ */
2086
+ async add(path, files2) {
2087
+ const args = ["add", "--", ...files2];
2088
+ await this.git(path, args);
2089
+ }
2090
+ branches = {
2091
+ /**
2092
+ * List branches
2093
+ *
2094
+ * @see https://www.git-scm.com/docs/git-branch
2095
+ *
2096
+ * @param path Path to the repository
2097
+ */
2098
+ list: async (path) => {
2099
+ const args = ["branch", "--list", "--all"];
2100
+ const result = await this.git(path, args);
2101
+ const normalizedLinesArr = result.stdout.split(EOL2).filter((line) => {
2102
+ return line.trim() !== "";
2103
+ }).map((line) => {
2104
+ return line.trim().replace("* ", "");
2105
+ });
2106
+ const local = [];
2107
+ const remote = [];
2108
+ normalizedLinesArr.forEach((line) => {
2109
+ if (line.startsWith("remotes/")) {
2110
+ remote.push(line.replace("remotes/", ""));
2111
+ } else {
2112
+ local.push(line);
2113
+ }
2114
+ });
2115
+ return {
2116
+ local,
2117
+ remote
2118
+ };
2119
+ },
2120
+ /**
2121
+ * Returns the name of the current branch. In detached HEAD state, an empty string is returned.
2122
+ *
2123
+ * @see https://www.git-scm.com/docs/git-branch#Documentation/git-branch.txt---show-current
2124
+ *
2125
+ * @param path Path to the repository
2126
+ */
2127
+ current: async (path) => {
2128
+ const args = ["branch", "--show-current"];
2129
+ const result = await this.git(path, args);
2130
+ return result.stdout.trim();
2131
+ },
2132
+ /**
2133
+ * Switch branches
2134
+ *
2135
+ * @see https://git-scm.com/docs/git-switch/
2136
+ *
2137
+ * @param path Path to the repository
2138
+ * @param branch Name of the branch to switch to
2139
+ * @param options Options specific to the switch operation
2140
+ */
2141
+ switch: async (path, branch, options) => {
2142
+ await this.checkBranchOrTagName(path, branch);
2143
+ let args = ["switch"];
2144
+ if (options?.isNew === true) {
2145
+ args = [...args, "--create", branch];
2146
+ } else {
2147
+ args = [...args, branch];
2148
+ }
2149
+ await this.git(path, args);
2150
+ }
2151
+ };
2152
+ remotes = {
2153
+ /**
2154
+ * Returns a list of currently tracked remotes
2155
+ *
2156
+ * @see https://git-scm.com/docs/git-remote
2157
+ *
2158
+ * @param path Path to the repository
2159
+ */
2160
+ list: async (path) => {
2161
+ const args = ["remote"];
2162
+ const result = await this.git(path, args);
2163
+ const normalizedLinesArr = result.stdout.split(EOL2).filter((line) => {
2164
+ return line.trim() !== "";
2165
+ });
2166
+ return normalizedLinesArr;
2167
+ },
2168
+ /**
2169
+ * Returns true if the `origin` remote exists, otherwise false
2170
+ *
2171
+ * @param path Path to the repository
2172
+ */
2173
+ hasOrigin: async (path) => {
2174
+ const remotes = await this.remotes.list(path);
2175
+ if (remotes.includes("origin")) {
2176
+ return true;
2177
+ }
2178
+ return false;
2179
+ },
2180
+ /**
2181
+ * Adds the `origin` remote with given URL
2182
+ *
2183
+ * Throws if `origin` remote is added already.
2184
+ *
2185
+ * @see https://git-scm.com/docs/git-remote#Documentation/git-remote.txt-emaddem
2186
+ *
2187
+ * @param path Path to the repository
2188
+ */
2189
+ addOrigin: async (path, url) => {
2190
+ const args = ["remote", "add", "origin", url];
2191
+ await this.git(path, args);
2192
+ },
2193
+ /**
2194
+ * Returns the current `origin` remote URL
2195
+ *
2196
+ * Throws if no `origin` remote is added yet.
2197
+ *
2198
+ * @see https://git-scm.com/docs/git-remote#Documentation/git-remote.txt-emget-urlem
2199
+ *
2200
+ * @param path Path to the repository
2201
+ */
2202
+ getOriginUrl: async (path) => {
2203
+ const args = ["remote", "get-url", "origin"];
2204
+ const result = (await this.git(path, args)).stdout.trim();
2205
+ return result.length === 0 ? null : result;
2206
+ },
2207
+ /**
2208
+ * Sets the current `origin` remote URL
2209
+ *
2210
+ * Throws if no `origin` remote is added yet.
2211
+ *
2212
+ * @see https://git-scm.com/docs/git-remote#Documentation/git-remote.txt-emset-urlem
2213
+ *
2214
+ * @param path Path to the repository
2215
+ */
2216
+ setOriginUrl: async (path, url) => {
2217
+ const args = ["remote", "set-url", "origin", url];
2218
+ await this.git(path, args);
2219
+ }
2220
+ };
2221
+ /**
2222
+ * Reset current HEAD to the specified state
2223
+ *
2224
+ * @todo maybe add more options
2225
+ * @see https://git-scm.com/docs/git-reset
2226
+ *
2227
+ * @param path Path to the repository
2228
+ * @param mode Modifies the working tree depending on given mode
2229
+ * @param commit Resets the current branch head to this commit / tag
2230
+ */
2231
+ async reset(path, mode, commit) {
2232
+ const args = ["reset", `--${mode}`, commit];
2233
+ await this.git(path, args);
2234
+ }
2235
+ /**
2236
+ * Restore working tree files
2237
+ *
2238
+ * @see https://git-scm.com/docs/git-restore/
2239
+ *
2240
+ * @todo It's probably a good idea to not use restore
2241
+ * for a use case where someone just wants to have a look
2242
+ * and maybe copy something from a deleted file.
2243
+ * We should use `checkout` without `add .` and `commit` for that
2244
+ *
2245
+ * @param path Path to the repository
2246
+ * @param source Git commit SHA or tag name to restore to
2247
+ * @param files Files to restore
2248
+ */
2249
+ // public async restore(
2250
+ // path: string,
2251
+ // source: string,
2252
+ // files: string[]
2253
+ // ): Promise<void> {
2254
+ // const args = ['restore', `--source=${source}`, ...files];
2255
+ // await this.git(path, args);
2256
+ // }
2257
+ /**
2258
+ * Download objects and refs from remote `origin`
2259
+ *
2260
+ * @see https://www.git-scm.com/docs/git-fetch
2261
+ *
2262
+ * @param path Path to the repository
2263
+ */
2264
+ async fetch(path) {
2265
+ const args = ["fetch"];
2266
+ await this.git(path, args);
2267
+ }
2268
+ /**
2269
+ * Fetch from and integrate (rebase or merge) with a local branch
2270
+ *
2271
+ * @see https://git-scm.com/docs/git-pull
2272
+ *
2273
+ * @param path Path to the repository
2274
+ */
2275
+ async pull(path) {
2276
+ const args = ["pull"];
2277
+ await this.git(path, args);
2278
+ }
2279
+ /**
2280
+ * Update remote refs along with associated objects to remote `origin`
2281
+ *
2282
+ * @see https://git-scm.com/docs/git-push
2283
+ *
2284
+ * @param path Path to the repository
2285
+ */
2286
+ async push(path, options) {
2287
+ let args = ["push", "origin"];
1947
2288
  if (options?.all === true) {
1948
2289
  args = [...args, "--all"];
1949
2290
  }
@@ -2010,8 +2351,8 @@ var GitService2 = class {
2010
2351
  name: lineArray[2],
2011
2352
  email: lineArray[3]
2012
2353
  },
2013
- timestamp: parseInt(lineArray[4]),
2014
- tag: this.refNameToTagName(lineArray[5])
2354
+ timestamp: parseInt(lineArray[4] || ""),
2355
+ tag: this.refNameToTagName(lineArray[5] || "")
2015
2356
  };
2016
2357
  });
2017
2358
  return lineObjArr.filter(this.isGitCommit.bind(this));
@@ -2144,475 +2485,118 @@ var GitService2 = class {
2144
2485
  await this.git(path, autoSetupRemoteArgs);
2145
2486
  }
2146
2487
  /**
2147
- * Type guard for GitCommit
2148
- *
2149
- * @param obj The object to check
2150
- */
2151
- isGitCommit(obj) {
2152
- return gitCommitSchema.safeParse(obj).success;
2153
- }
2154
- /**
2155
- * Wraps the execution of any git command
2156
- * to use a FIFO queue for sequential processing
2157
- *
2158
- * @param path Path to the repository
2159
- * @param args Arguments to append after the `git` command
2160
- */
2161
- async git(path, args) {
2162
- const result = await this.queue.add(
2163
- () => GitProcess.exec(args, path, {
2164
- env: {
2165
- // @todo Nasty stuff - remove after update to dugite with git > v2.45.2 once available
2166
- // @see https://github.com/git-lfs/git-lfs/issues/5749
2167
- GIT_CLONE_PROTECTION_ACTIVE: "false"
2168
- }
2169
- })
2170
- );
2171
- if (!result) {
2172
- throw new GitError(
2173
- `Git ${this.version} (${this.gitPath}) command "git ${args.join(
2174
- " "
2175
- )}" failed to return a result`
2176
- );
2177
- }
2178
- if (result.exitCode !== 0) {
2179
- throw new GitError(
2180
- `Git ${this.version} (${this.gitPath}) command "git ${args.join(
2181
- " "
2182
- )}" failed with exit code "${result.exitCode}" and message "${result.stderr}"`
2183
- );
2184
- }
2185
- return result;
2186
- }
2187
- };
2188
-
2189
- // src/service/CollectionService.ts
2190
- var CollectionService = class extends AbstractCrudService {
2191
- constructor(options, jsonFileService, gitService) {
2192
- super(serviceTypeSchema.Enum.Collection, options);
2193
- this.jsonFileService = jsonFileService;
2194
- this.gitService = gitService;
2195
- }
2196
- /**
2197
- * Creates a new Collection
2198
- */
2199
- async create(props) {
2200
- createCollectionSchema.parse(props);
2201
- const id = uuid();
2202
- const projectPath = pathTo.project(props.projectId);
2203
- const collectionPath = pathTo.collection(props.projectId, id);
2204
- const collectionFilePath = pathTo.collectionFile(props.projectId, id);
2205
- const collectionFile = {
2206
- ...props,
2207
- objectType: "collection",
2208
- id,
2209
- slug: {
2210
- singular: slug(props.slug.singular),
2211
- plural: slug(props.slug.plural)
2212
- },
2213
- created: currentTimestamp(),
2214
- updated: null
2215
- };
2216
- await Fs4.ensureDir(collectionPath);
2217
- await this.jsonFileService.create(
2218
- collectionFile,
2219
- collectionFilePath,
2220
- collectionFileSchema
2221
- );
2222
- await this.gitService.add(projectPath, [collectionFilePath]);
2223
- await this.gitService.commit(projectPath, this.gitMessage.create);
2224
- return collectionFile;
2225
- }
2226
- /**
2227
- * Returns a Collection by ID
2228
- */
2229
- async read(props) {
2230
- readCollectionSchema.parse(props);
2231
- const collection = await this.jsonFileService.read(
2232
- pathTo.collectionFile(props.projectId, props.id),
2233
- collectionFileSchema
2234
- );
2235
- return collection;
2236
- }
2237
- /**
2238
- * Updates given Collection
2239
- *
2240
- * @todo finish implementing checks for FieldDefinitions and extract methods
2241
- *
2242
- * @param projectId Project ID of the collection to update
2243
- * @param collection Collection to write to disk
2244
- * @returns An object containing information about the actions needed to be taken,
2245
- * before given update can be executed or void if the update was executed successfully
2246
- */
2247
- async update(props) {
2248
- updateCollectionSchema.parse(props);
2249
- const projectPath = pathTo.project(props.projectId);
2250
- const collectionFilePath = pathTo.collectionFile(props.projectId, props.id);
2251
- const prevCollectionFile = await this.read(props);
2252
- const collectionFile = {
2253
- ...prevCollectionFile,
2254
- ...props,
2255
- updated: currentTimestamp()
2256
- };
2257
- await this.jsonFileService.update(
2258
- collectionFile,
2259
- collectionFilePath,
2260
- collectionFileSchema
2261
- );
2262
- await this.gitService.add(projectPath, [collectionFilePath]);
2263
- await this.gitService.commit(projectPath, this.gitMessage.update);
2264
- return collectionFile;
2265
- }
2266
- /**
2267
- * Deletes given Collection (folder), including it's items
2268
- *
2269
- * The Fields that Collection used are not deleted.
2270
- */
2271
- async delete(props) {
2272
- deleteCollectionSchema.parse(props);
2273
- const projectPath = pathTo.project(props.projectId);
2274
- const collectionPath = pathTo.collection(props.projectId, props.id);
2275
- await Fs4.remove(collectionPath);
2276
- await this.gitService.add(projectPath, [collectionPath]);
2277
- await this.gitService.commit(projectPath, this.gitMessage.delete);
2278
- }
2279
- async list(props) {
2280
- listCollectionsSchema.parse(props);
2281
- const offset = props.offset || 0;
2282
- const limit = props.limit || 15;
2283
- const collectionReferences = await this.listReferences(
2284
- objectTypeSchema.Enum.collection,
2285
- props.projectId
2286
- );
2287
- const partialCollectionReferences = collectionReferences.slice(
2288
- offset,
2289
- limit
2290
- );
2291
- const collections = await returnResolved(
2292
- partialCollectionReferences.map((reference) => {
2293
- return this.read({
2294
- projectId: props.projectId,
2295
- id: reference.id
2296
- });
2297
- })
2298
- );
2299
- return {
2300
- total: collectionReferences.length,
2301
- limit,
2302
- offset,
2303
- list: collections
2304
- };
2305
- }
2306
- async count(props) {
2307
- countCollectionsSchema.parse(props);
2308
- const count = (await this.listReferences(
2309
- objectTypeSchema.Enum.collection,
2310
- props.projectId
2311
- )).length;
2312
- return count;
2313
- }
2314
- /**
2315
- * Checks if given object is of type Collection
2316
- */
2317
- isCollection(obj) {
2318
- return collectionFileSchema.safeParse(obj).success;
2319
- }
2320
- };
2321
-
2322
- // src/service/EntryService.ts
2323
- import Fs5 from "fs-extra";
2324
- var EntryService = class extends AbstractCrudService {
2325
- // private sharedValueService: SharedValueService;
2326
- constructor(options, jsonFileService, gitService, collectionService, assetService) {
2327
- super(serviceTypeSchema.Enum.Entry, options);
2328
- this.jsonFileService = jsonFileService;
2329
- this.gitService = gitService;
2330
- this.collectionService = collectionService;
2331
- this.assetService = assetService;
2332
- }
2333
- /**
2334
- * Creates a new Entry for given Collection
2335
- */
2336
- async create(props) {
2337
- createEntrySchema.parse(props);
2338
- const id = uuid();
2339
- const projectPath = pathTo.project(props.projectId);
2340
- const entryFilePath = pathTo.entryFile(
2341
- props.projectId,
2342
- props.collectionId,
2343
- id
2344
- );
2345
- const collection = await this.collectionService.read({
2346
- projectId: props.projectId,
2347
- id: props.collectionId
2348
- });
2349
- const entryFile = {
2350
- objectType: "entry",
2351
- id,
2352
- values: props.values,
2353
- created: currentTimestamp(),
2354
- updated: null
2355
- };
2356
- const entry = await this.toEntry({
2357
- projectId: props.projectId,
2358
- collectionId: props.collectionId,
2359
- entryFile
2360
- });
2361
- this.validateValues({
2362
- collectionId: props.collectionId,
2363
- valueDefinitions: collection.valueDefinitions,
2364
- values: entry.values
2365
- });
2366
- await this.jsonFileService.create(
2367
- entryFile,
2368
- entryFilePath,
2369
- entryFileSchema
2370
- );
2371
- await this.gitService.add(projectPath, [entryFilePath]);
2372
- await this.gitService.commit(projectPath, this.gitMessage.create);
2373
- return entry;
2374
- }
2375
- /**
2376
- * Returns an Entry from given Collection by ID and language
2377
- */
2378
- async read(props) {
2379
- readEntrySchema.parse(props);
2380
- const entryFile = await this.jsonFileService.read(
2381
- pathTo.entryFile(props.projectId, props.collectionId, props.id),
2382
- entryFileSchema
2383
- );
2384
- return await this.toEntry({
2385
- projectId: props.projectId,
2386
- collectionId: props.collectionId,
2387
- entryFile
2388
- });
2389
- }
2390
- /**
2391
- * Updates an Entry of given Collection with new Values and shared Values
2392
- */
2393
- async update(props) {
2394
- updateEntrySchema.parse(props);
2395
- const projectPath = pathTo.project(props.projectId);
2396
- const entryFilePath = pathTo.entryFile(
2397
- props.projectId,
2398
- props.collectionId,
2399
- props.id
2400
- );
2401
- const collection = await this.collectionService.read({
2402
- projectId: props.projectId,
2403
- id: props.collectionId
2404
- });
2405
- const prevEntryFile = await this.read({
2406
- projectId: props.projectId,
2407
- collectionId: props.collectionId,
2408
- id: props.id
2409
- });
2410
- const entryFile = {
2411
- ...prevEntryFile,
2412
- values: props.values,
2413
- updated: currentTimestamp()
2414
- };
2415
- const entry = await this.toEntry({
2416
- projectId: props.projectId,
2417
- collectionId: props.collectionId,
2418
- entryFile
2419
- });
2420
- this.validateValues({
2421
- collectionId: props.collectionId,
2422
- valueDefinitions: collection.valueDefinitions,
2423
- values: entry.values
2424
- });
2425
- await this.jsonFileService.update(
2426
- entryFile,
2427
- entryFilePath,
2428
- entryFileSchema
2429
- );
2430
- await this.gitService.add(projectPath, [entryFilePath]);
2431
- await this.gitService.commit(projectPath, this.gitMessage.update);
2432
- return entry;
2433
- }
2434
- /**
2435
- * Deletes given Entry from it's Collection
2436
- */
2437
- async delete(props) {
2438
- deleteEntrySchema.parse(props);
2439
- const projectPath = pathTo.project(props.projectId);
2440
- const entryFilePath = pathTo.entryFile(
2441
- props.projectId,
2442
- props.collectionId,
2443
- props.id
2444
- );
2445
- await Fs5.remove(entryFilePath);
2446
- await this.gitService.add(projectPath, [entryFilePath]);
2447
- await this.gitService.commit(projectPath, this.gitMessage.delete);
2448
- }
2449
- async list(props) {
2450
- listEntriesSchema.parse(props);
2451
- const offset = props.offset || 0;
2452
- const limit = props.limit || 15;
2453
- const entryReferences = await this.listReferences(
2454
- objectTypeSchema.Enum.entry,
2455
- props.projectId,
2456
- props.collectionId
2457
- );
2458
- const partialEntryReferences = entryReferences.slice(offset, limit);
2459
- const entries = await returnResolved(
2460
- partialEntryReferences.map((reference) => {
2461
- return this.read({
2462
- projectId: props.projectId,
2463
- collectionId: props.collectionId,
2464
- id: reference.id
2465
- });
2466
- })
2467
- );
2468
- return {
2469
- total: entryReferences.length,
2470
- limit,
2471
- offset,
2472
- list: entries
2473
- };
2474
- }
2475
- async count(props) {
2476
- countEntriesSchema.parse(props);
2477
- return (await this.listReferences(
2478
- objectTypeSchema.Enum.entry,
2479
- props.projectId,
2480
- props.collectionId
2481
- )).length;
2482
- }
2483
- /**
2484
- * Checks if given object is of type Entry
2488
+ * Type guard for GitCommit
2489
+ *
2490
+ * @param obj The object to check
2485
2491
  */
2486
- isEntry(obj) {
2487
- return entrySchema.safeParse(obj).success;
2492
+ isGitCommit(obj) {
2493
+ return gitCommitSchema.safeParse(obj).success;
2488
2494
  }
2489
2495
  /**
2490
- * Returns a Value definition by ID
2496
+ * Wraps the execution of any git command
2497
+ * to use a FIFO queue for sequential processing
2498
+ *
2499
+ * @param path Path to the repository
2500
+ * @param args Arguments to append after the `git` command
2491
2501
  */
2492
- getValueDefinitionById(props) {
2493
- const definition = props.valueDefinitions.find((def) => {
2494
- if (def.id === props.id) {
2495
- return true;
2496
- }
2497
- return false;
2498
- });
2499
- if (!definition) {
2500
- throw new Error(
2501
- `No definition with ID "${props.id}" found in Collection "${props.collectionId}" for given Value reference`
2502
+ async git(path, args) {
2503
+ const result = await this.queue.add(
2504
+ () => GitProcess.exec(args, path, {
2505
+ env: {
2506
+ // @todo Nasty stuff - remove after update to dugite with git > v2.45.2 once available
2507
+ // @see https://github.com/git-lfs/git-lfs/issues/5749
2508
+ GIT_CLONE_PROTECTION_ACTIVE: "false"
2509
+ }
2510
+ })
2511
+ );
2512
+ if (!result) {
2513
+ throw new GitError(
2514
+ `Git ${this.version} (${this.gitPath}) command "git ${args.join(
2515
+ " "
2516
+ )}" failed to return a result`
2502
2517
  );
2503
2518
  }
2504
- return definition;
2519
+ if (result.exitCode !== 0) {
2520
+ throw new GitError(
2521
+ `Git ${this.version} (${this.gitPath}) command "git ${args.join(
2522
+ " "
2523
+ )}" failed with exit code "${result.exitCode}" and message "${result.stderr}"`
2524
+ );
2525
+ }
2526
+ return result;
2527
+ }
2528
+ };
2529
+
2530
+ // src/service/JsonFileService.ts
2531
+ import Fs5 from "fs-extra";
2532
+ var JsonFileService = class extends AbstractCrudService {
2533
+ cache = /* @__PURE__ */ new Map();
2534
+ constructor(options) {
2535
+ super(serviceTypeSchema.Enum.JsonFile, options);
2505
2536
  }
2506
2537
  /**
2507
- * Validates given Values against it's Collections definitions
2538
+ * Creates a new file on disk. Fails if path already exists
2539
+ *
2540
+ * @param data Data to write into the file
2541
+ * @param path Path to write the file to
2542
+ * @param schema Schema of the file to validate against
2543
+ * @returns Validated content of the file from disk
2508
2544
  */
2509
- validateValues(props) {
2510
- props.values.map((value) => {
2511
- const definition = this.getValueDefinitionById({
2512
- collectionId: props.collectionId,
2513
- valueDefinitions: props.valueDefinitions,
2514
- id: value.definitionId
2515
- });
2516
- const schema = getValueContentSchemaFromDefinition(definition);
2517
- try {
2518
- for (const [language, content] of Object.entries(value.content)) {
2519
- schema.parse(content);
2520
- }
2521
- } catch (error) {
2522
- console.log("Definition:", definition);
2523
- console.log("Value:", value);
2524
- throw error;
2525
- }
2545
+ async create(data, path, schema) {
2546
+ const parsedData = schema.parse(data);
2547
+ const string = this.serialize(parsedData);
2548
+ await Fs5.writeFile(path, string, {
2549
+ flag: "wx",
2550
+ encoding: "utf8"
2526
2551
  });
2552
+ this.cache.set(path, parsedData);
2553
+ return parsedData;
2527
2554
  }
2528
2555
  /**
2529
- * Validates given shared Value references against it's Collections definitions
2556
+ * Reads the content of a file on disk. Fails if path does not exist
2557
+ *
2558
+ * @param path Path to read the file from
2559
+ * @param schema Schema of the file to validate against
2560
+ * @returns Validated content of the file from disk
2530
2561
  */
2531
- // private validateResolvedSharedValues(props: {
2532
- // collectionId: string;
2533
- // valueDefinitions: ValueDefinition[];
2534
- // resolvedSharedValues: ResolvedSharedValueReference[];
2535
- // }) {
2536
- // props.resolvedSharedValues.map((value) => {
2537
- // const definition = this.getValueDefinitionById({
2538
- // collectionId: props.collectionId,
2539
- // valueDefinitions: props.valueDefinitions,
2540
- // id: value.definitionId,
2541
- // });
2542
- // const schema = getValueSchemaFromDefinition(definition);
2543
- // schema.parse(value.resolved.content);
2544
- // });
2545
- // }
2546
- async resolveValueContentReference(props) {
2547
- switch (props.valueContentReference.objectType) {
2548
- case objectTypeSchema.Enum.asset:
2549
- return await this.assetService.read({
2550
- projectId: props.projectId,
2551
- id: props.valueContentReference.id,
2552
- language: props.valueContentReference.language
2553
- });
2554
- case objectTypeSchema.Enum.entry:
2555
- return await this.read({
2556
- projectId: props.projectId,
2557
- collectionId: props.collectionId,
2558
- id: props.valueContentReference.id
2559
- });
2560
- default:
2561
- throw new Error(
2562
- // @ts-ignore
2563
- `Tried to resolve unsupported Value reference "${props.valueContentReference.referenceObjectType}"`
2564
- );
2565
- }
2566
- }
2567
- async resolveValueContentReferences(props) {
2568
- let resolvedContent = {};
2569
- for (const language in props.valueReference.content) {
2570
- const referencesOfLanguage = props.valueReference.content[language];
2571
- if (!referencesOfLanguage) {
2572
- throw new Error(
2573
- `Trying to access content references by language "${language}" failed`
2574
- );
2575
- }
2576
- const resolvedReferencesOfLanguage = await Promise.all(
2577
- referencesOfLanguage.map(async (reference) => {
2578
- return await this.resolveValueContentReference({
2579
- projectId: props.projectId,
2580
- collectionId: props.collectionId,
2581
- valueContentReference: reference
2582
- });
2583
- })
2584
- );
2585
- resolvedContent = {
2586
- ...resolvedContent,
2587
- [language]: resolvedReferencesOfLanguage
2588
- };
2562
+ async read(path, schema) {
2563
+ if (this.cache.has(path)) {
2564
+ return this.cache.get(path);
2589
2565
  }
2590
- return resolvedContent;
2566
+ const data = await Fs5.readFile(path, {
2567
+ flag: "r",
2568
+ encoding: "utf8"
2569
+ });
2570
+ const json = this.deserialize(data);
2571
+ const parsedData = schema.parse(json);
2572
+ this.cache.set(path, parsedData);
2573
+ return parsedData;
2591
2574
  }
2592
2575
  /**
2593
- * Creates an Entry from given EntryFile by resolving it's Values
2576
+ * Overwrites an existing file on disk
2577
+ *
2578
+ * @todo Check how to error out if the file does not exist already
2579
+ *
2580
+ * @param data Data to write into the file
2581
+ * @param path Path to the file to overwrite
2582
+ * @param schema Schema of the file to validate against
2583
+ * @returns Validated content of the file from disk
2594
2584
  */
2595
- async toEntry(props) {
2596
- return {
2597
- ...props.entryFile,
2598
- // @ts-ignore @todo fixme - I have no idea why this happens. The types seem to be compatible to me and they work
2599
- values: await Promise.all(
2600
- props.entryFile.values.map(async (value) => {
2601
- if (value.valueType === ValueTypeSchema.Enum.reference) {
2602
- const resolvedContentReferences = await this.resolveValueContentReferences({
2603
- projectId: props.projectId,
2604
- collectionId: props.collectionId,
2605
- valueReference: value
2606
- });
2607
- return {
2608
- ...value,
2609
- content: resolvedContentReferences
2610
- };
2611
- }
2612
- return value;
2613
- })
2614
- )
2615
- };
2585
+ async update(data, path, schema) {
2586
+ const parsedData = schema.parse(data);
2587
+ const string = this.serialize(parsedData);
2588
+ await Fs5.writeFile(path, string, {
2589
+ flag: "w",
2590
+ encoding: "utf8"
2591
+ });
2592
+ this.cache.set(path, parsedData);
2593
+ return parsedData;
2594
+ }
2595
+ serialize(data) {
2596
+ return JSON.stringify(data, null, this.options.file.json.indentation);
2597
+ }
2598
+ deserialize(data) {
2599
+ return JSON.parse(data);
2616
2600
  }
2617
2601
  };
2618
2602
 
@@ -2622,47 +2606,14 @@ import Os2 from "os";
2622
2606
  import Path2 from "path";
2623
2607
  import Semver from "semver";
2624
2608
  var ProjectService = class extends AbstractCrudService {
2609
+ jsonFileService;
2610
+ userService;
2611
+ gitService;
2612
+ assetService;
2613
+ collectionService;
2614
+ entryService;
2625
2615
  constructor(options, jsonFileService, userService, gitService, assetService, collectionService, entryService) {
2626
2616
  super(serviceTypeSchema.Enum.Project, options);
2627
- this.branches = {
2628
- list: async (props) => {
2629
- listBranchesProjectSchema.parse(props);
2630
- const projectPath = pathTo.project(props.id);
2631
- await this.gitService.fetch(projectPath);
2632
- return await this.gitService.branches.list(projectPath);
2633
- },
2634
- current: async (props) => {
2635
- currentBranchProjectSchema.parse(props);
2636
- const projectPath = pathTo.project(props.id);
2637
- return await this.gitService.branches.current(projectPath);
2638
- },
2639
- switch: async (props) => {
2640
- switchBranchProjectSchema.parse(props);
2641
- const projectPath = pathTo.project(props.id);
2642
- return await this.gitService.branches.switch(
2643
- projectPath,
2644
- props.branch,
2645
- props.options
2646
- );
2647
- }
2648
- };
2649
- this.remotes = {
2650
- getOriginUrl: async (props) => {
2651
- getRemoteOriginUrlProjectSchema.parse(props);
2652
- const projectPath = pathTo.project(props.id);
2653
- return await this.gitService.remotes.getOriginUrl(projectPath);
2654
- },
2655
- setOriginUrl: async (props) => {
2656
- setRemoteOriginUrlProjectSchema.parse(props);
2657
- const projectPath = pathTo.project(props.id);
2658
- const hasOrigin = await this.gitService.remotes.hasOrigin(projectPath);
2659
- if (!hasOrigin) {
2660
- await this.gitService.remotes.addOrigin(projectPath, props.url);
2661
- } else {
2662
- await this.gitService.remotes.setOriginUrl(projectPath, props.url);
2663
- }
2664
- }
2665
- };
2666
2617
  this.jsonFileService = jsonFileService;
2667
2618
  this.userService = userService;
2668
2619
  this.gitService = gitService;
@@ -2776,7 +2727,14 @@ var ProjectService = class extends AbstractCrudService {
2776
2727
  const prevProjectFile = await this.read(props);
2777
2728
  const projectFile = {
2778
2729
  ...prevProjectFile,
2779
- ...props,
2730
+ name: props.name || prevProjectFile.name,
2731
+ description: props.description || prevProjectFile.description,
2732
+ settings: {
2733
+ language: {
2734
+ supported: props.settings?.language.supported || prevProjectFile.settings.language.supported,
2735
+ default: props.settings?.language.default || prevProjectFile.settings.language.default
2736
+ }
2737
+ },
2780
2738
  updated: currentTimestamp()
2781
2739
  };
2782
2740
  await this.jsonFileService.update(projectFile, filePath, projectFileSchema);
@@ -2822,6 +2780,7 @@ var ProjectService = class extends AbstractCrudService {
2822
2780
  if (upgrade.to !== "0.0.0") {
2823
2781
  return upgrade;
2824
2782
  }
2783
+ return;
2825
2784
  });
2826
2785
  for (let index = 0; index < sortedUpgrades.length; index++) {
2827
2786
  const upgrade = sortedUpgrades[index];
@@ -2852,6 +2811,45 @@ var ProjectService = class extends AbstractCrudService {
2852
2811
  }
2853
2812
  }
2854
2813
  }
2814
+ branches = {
2815
+ list: async (props) => {
2816
+ listBranchesProjectSchema.parse(props);
2817
+ const projectPath = pathTo.project(props.id);
2818
+ await this.gitService.fetch(projectPath);
2819
+ return await this.gitService.branches.list(projectPath);
2820
+ },
2821
+ current: async (props) => {
2822
+ currentBranchProjectSchema.parse(props);
2823
+ const projectPath = pathTo.project(props.id);
2824
+ return await this.gitService.branches.current(projectPath);
2825
+ },
2826
+ switch: async (props) => {
2827
+ switchBranchProjectSchema.parse(props);
2828
+ const projectPath = pathTo.project(props.id);
2829
+ return await this.gitService.branches.switch(
2830
+ projectPath,
2831
+ props.branch,
2832
+ props.options
2833
+ );
2834
+ }
2835
+ };
2836
+ remotes = {
2837
+ getOriginUrl: async (props) => {
2838
+ getRemoteOriginUrlProjectSchema.parse(props);
2839
+ const projectPath = pathTo.project(props.id);
2840
+ return await this.gitService.remotes.getOriginUrl(projectPath);
2841
+ },
2842
+ setOriginUrl: async (props) => {
2843
+ setRemoteOriginUrlProjectSchema.parse(props);
2844
+ const projectPath = pathTo.project(props.id);
2845
+ const hasOrigin = await this.gitService.remotes.hasOrigin(projectPath);
2846
+ if (!hasOrigin) {
2847
+ await this.gitService.remotes.addOrigin(projectPath, props.url);
2848
+ } else {
2849
+ await this.gitService.remotes.setOriginUrl(projectPath, props.url);
2850
+ }
2851
+ }
2852
+ };
2855
2853
  /**
2856
2854
  * Returns the differences of the given Projects current branch
2857
2855
  * between the local and remote `origin` (commits ahead & behind)
@@ -3003,8 +3001,50 @@ var ProjectService = class extends AbstractCrudService {
3003
3001
  }
3004
3002
  };
3005
3003
 
3004
+ // src/service/UserService.ts
3005
+ var UserService = class {
3006
+ jsonFileService;
3007
+ constructor(jsonFileService) {
3008
+ this.jsonFileService = jsonFileService;
3009
+ }
3010
+ /**
3011
+ * Returns the User currently working with Core
3012
+ */
3013
+ async get() {
3014
+ try {
3015
+ return await this.jsonFileService.read(pathTo.userFile, userFileSchema);
3016
+ } catch (error) {
3017
+ return void 0;
3018
+ }
3019
+ }
3020
+ /**
3021
+ * Sets the User currently working with Core
3022
+ *
3023
+ * By doing so all git operations are done with the signature of this User
3024
+ */
3025
+ async set(props) {
3026
+ setUserSchema.parse(props);
3027
+ const userFilePath = pathTo.userFile;
3028
+ const userFile = {
3029
+ ...props
3030
+ };
3031
+ if (userFile.userType === UserTypeSchema.Enum.cloud) {
3032
+ }
3033
+ await this.jsonFileService.update(userFile, userFilePath, userFileSchema);
3034
+ return userFile;
3035
+ }
3036
+ };
3037
+
3006
3038
  // src/index.node.ts
3007
3039
  var ElekIoCore = class {
3040
+ options;
3041
+ userService;
3042
+ gitService;
3043
+ jsonFileService;
3044
+ assetService;
3045
+ projectService;
3046
+ collectionService;
3047
+ entryService;
3008
3048
  // private readonly sharedValueService: SharedValueService;
3009
3049
  constructor(props) {
3010
3050
  const parsedProps = constructorElekIoCoreSchema.parse(props);
@@ -3020,7 +3060,7 @@ var ElekIoCore = class {
3020
3060
  this.options = Object.assign({}, defaults, parsedProps);
3021
3061
  this.jsonFileService = new JsonFileService(this.options);
3022
3062
  this.userService = new UserService(this.jsonFileService);
3023
- this.gitService = new GitService2(this.options, this.userService);
3063
+ this.gitService = new GitService(this.options, this.userService);
3024
3064
  this.assetService = new AssetService(
3025
3065
  this.options,
3026
3066
  this.jsonFileService,