@elek-io/core 0.5.4 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.browser.d.ts +1069 -260
- package/dist/browser/index.browser.js +19 -22
- package/dist/browser/index.browser.js.map +1 -1
- package/dist/node/index.node.d.ts +268 -292
- package/dist/node/index.node.js +1022 -1040
- package/dist/node/index.node.js.map +1 -1
- package/package.json +28 -37
- package/dist/browser/index.browser.cjs +0 -1082
- package/dist/browser/index.browser.cjs.map +0 -1
- package/dist/browser/index.browser.d.cts +0 -12551
- package/dist/node/index.node.cjs +0 -3409
- package/dist/node/index.node.cjs.map +0 -1
- package/dist/node/index.node.d.cts +0 -13384
package/dist/node/index.node.js
CHANGED
|
@@ -136,13 +136,13 @@ var baseFileSchema = z2.object({
|
|
|
136
136
|
*/
|
|
137
137
|
id: uuidSchema.readonly(),
|
|
138
138
|
/**
|
|
139
|
-
* The
|
|
139
|
+
* The datetime of the file being created is set by the service of "objectType" while creating it
|
|
140
140
|
*/
|
|
141
|
-
created: z2.
|
|
141
|
+
created: z2.string().datetime().readonly(),
|
|
142
142
|
/**
|
|
143
|
-
* The
|
|
143
|
+
* The datetime of the file being updated is set by the service of "objectType" while updating it
|
|
144
144
|
*/
|
|
145
|
-
updated: z2.
|
|
145
|
+
updated: z2.string().datetime().nullable()
|
|
146
146
|
});
|
|
147
147
|
var baseFileWithLanguageSchema = baseFileSchema.extend({
|
|
148
148
|
/**
|
|
@@ -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(
|
|
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(
|
|
461
|
+
function getBooleanValueContentSchema() {
|
|
462
462
|
return z4.boolean();
|
|
463
463
|
}
|
|
464
464
|
function getNumberValueContentSchema(definition) {
|
|
@@ -662,7 +662,7 @@ var gitCommitSchema = z8.object({
|
|
|
662
662
|
hash: z8.string(),
|
|
663
663
|
message: z8.string(),
|
|
664
664
|
author: gitSignatureSchema,
|
|
665
|
-
|
|
665
|
+
datetime: z8.string().datetime(),
|
|
666
666
|
tag: z8.string().nullable()
|
|
667
667
|
});
|
|
668
668
|
var GitCommitIconNative = /* @__PURE__ */ ((GitCommitIconNative2) => {
|
|
@@ -731,7 +731,7 @@ var gitTagSchema = z9.object({
|
|
|
731
731
|
id: uuidSchema,
|
|
732
732
|
message: z9.string(),
|
|
733
733
|
author: gitSignatureSchema,
|
|
734
|
-
|
|
734
|
+
datetime: z9.string().datetime()
|
|
735
735
|
});
|
|
736
736
|
var createGitTagSchema = gitTagSchema.pick({
|
|
737
737
|
message: true
|
|
@@ -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,107 +1183,34 @@ var AbstractCrudService = class {
|
|
|
1177
1183
|
};
|
|
1178
1184
|
|
|
1179
1185
|
// src/service/AssetService.ts
|
|
1180
|
-
import
|
|
1181
|
-
import
|
|
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
|
-
import slugify from "slugify";
|
|
1191
|
+
import slugify from "@sindresorhus/slugify";
|
|
1185
1192
|
import { v4 as generateUuid } from "uuid";
|
|
1186
|
-
var Slugify = slugify.default || slugify;
|
|
1187
1193
|
function uuid() {
|
|
1188
1194
|
return generateUuid();
|
|
1189
1195
|
}
|
|
1190
|
-
function
|
|
1191
|
-
|
|
1196
|
+
function datetime(value) {
|
|
1197
|
+
if (!value) {
|
|
1198
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1199
|
+
}
|
|
1200
|
+
return new Date(value).toISOString();
|
|
1192
1201
|
}
|
|
1193
1202
|
function slug(string) {
|
|
1194
|
-
return
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
// remove characters that match regex, defaults to `undefined`
|
|
1199
|
-
lower: true,
|
|
1200
|
-
// convert to lower case, defaults to `false`
|
|
1201
|
-
strict: true
|
|
1202
|
-
// strip special characters except replacement, defaults to `false`
|
|
1203
|
+
return slugify(string, {
|
|
1204
|
+
separator: "-",
|
|
1205
|
+
lowercase: true,
|
|
1206
|
+
decamelize: true
|
|
1203
1207
|
});
|
|
1204
1208
|
}
|
|
1205
1209
|
|
|
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
1210
|
// src/service/AssetService.ts
|
|
1280
1211
|
var AssetService = class extends AbstractCrudService {
|
|
1212
|
+
jsonFileService;
|
|
1213
|
+
gitService;
|
|
1281
1214
|
constructor(options, jsonFileService, gitService) {
|
|
1282
1215
|
super(serviceTypeSchema.Enum.Asset, options);
|
|
1283
1216
|
this.jsonFileService = jsonFileService;
|
|
@@ -1303,14 +1236,14 @@ var AssetService = class extends AbstractCrudService {
|
|
|
1303
1236
|
...props,
|
|
1304
1237
|
objectType: "asset",
|
|
1305
1238
|
id,
|
|
1306
|
-
created:
|
|
1239
|
+
created: datetime(),
|
|
1307
1240
|
updated: null,
|
|
1308
1241
|
extension: fileType.extension,
|
|
1309
1242
|
mimeType: fileType.mimeType,
|
|
1310
1243
|
size
|
|
1311
1244
|
};
|
|
1312
1245
|
try {
|
|
1313
|
-
await
|
|
1246
|
+
await Fs2.copyFile(props.filePath, assetPath);
|
|
1314
1247
|
await this.jsonFileService.create(
|
|
1315
1248
|
assetFile,
|
|
1316
1249
|
assetFilePath,
|
|
@@ -1352,7 +1285,7 @@ var AssetService = class extends AbstractCrudService {
|
|
|
1352
1285
|
const assetFile = {
|
|
1353
1286
|
...prevAssetFile,
|
|
1354
1287
|
...props,
|
|
1355
|
-
updated:
|
|
1288
|
+
updated: datetime()
|
|
1356
1289
|
};
|
|
1357
1290
|
if (props.newFilePath) {
|
|
1358
1291
|
const fileType = await this.getSupportedFileTypeOrThrow(
|
|
@@ -1371,8 +1304,8 @@ var AssetService = class extends AbstractCrudService {
|
|
|
1371
1304
|
props.language,
|
|
1372
1305
|
fileType.extension
|
|
1373
1306
|
);
|
|
1374
|
-
await
|
|
1375
|
-
await
|
|
1307
|
+
await Fs2.remove(prevAssetPath);
|
|
1308
|
+
await Fs2.copyFile(props.newFilePath, assetPath);
|
|
1376
1309
|
assetFile.extension = fileType.extension;
|
|
1377
1310
|
assetFile.mimeType = fileType.mimeType;
|
|
1378
1311
|
assetFile.size = size;
|
|
@@ -1403,8 +1336,8 @@ var AssetService = class extends AbstractCrudService {
|
|
|
1403
1336
|
props.language,
|
|
1404
1337
|
props.extension
|
|
1405
1338
|
);
|
|
1406
|
-
await
|
|
1407
|
-
await
|
|
1339
|
+
await Fs2.remove(assetPath);
|
|
1340
|
+
await Fs2.remove(assetFilePath);
|
|
1408
1341
|
await this.gitService.add(projectPath, [assetFilePath, assetPath]);
|
|
1409
1342
|
await this.gitService.commit(projectPath, this.gitMessage.delete);
|
|
1410
1343
|
}
|
|
@@ -1453,7 +1386,7 @@ var AssetService = class extends AbstractCrudService {
|
|
|
1453
1386
|
* @param path Path of the Asset to get the size from
|
|
1454
1387
|
*/
|
|
1455
1388
|
async getAssetSize(path) {
|
|
1456
|
-
return (await
|
|
1389
|
+
return (await Fs2.stat(path)).size;
|
|
1457
1390
|
}
|
|
1458
1391
|
/**
|
|
1459
1392
|
* Creates an Asset from given AssetFile
|
|
@@ -1481,17 +1414,16 @@ var AssetService = class extends AbstractCrudService {
|
|
|
1481
1414
|
* @param filePath Path to the file to check
|
|
1482
1415
|
*/
|
|
1483
1416
|
async getSupportedFileTypeOrThrow(filePath) {
|
|
1484
|
-
const fileSize = (await
|
|
1417
|
+
const fileSize = (await Fs2.stat(filePath)).size;
|
|
1485
1418
|
if (fileSize / 1e3 <= 500) {
|
|
1486
|
-
const fileBuffer = await
|
|
1487
|
-
if (
|
|
1419
|
+
const fileBuffer = await Fs2.readFile(filePath);
|
|
1420
|
+
if (isSvg(fileBuffer.toString()) === true) {
|
|
1488
1421
|
return {
|
|
1489
1422
|
extension: supportedAssetExtensionSchema.Enum.svg,
|
|
1490
1423
|
mimeType: supportedAssetMimeTypeSchema.Enum["image/svg+xml"]
|
|
1491
1424
|
};
|
|
1492
1425
|
}
|
|
1493
1426
|
}
|
|
1494
|
-
const { fileTypeFromFile } = await import("file-type");
|
|
1495
1427
|
const fileType = await fileTypeFromFile(filePath);
|
|
1496
1428
|
const result = supportedAssetTypeSchema.parse({
|
|
1497
1429
|
extension: fileType?.ext,
|
|
@@ -1502,454 +1434,861 @@ var AssetService = class extends AbstractCrudService {
|
|
|
1502
1434
|
};
|
|
1503
1435
|
|
|
1504
1436
|
// src/service/CollectionService.ts
|
|
1505
|
-
import
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
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;
|
|
1437
|
+
import Fs3 from "fs-extra";
|
|
1438
|
+
var CollectionService = class extends AbstractCrudService {
|
|
1439
|
+
jsonFileService;
|
|
1440
|
+
gitService;
|
|
1441
|
+
constructor(options, jsonFileService, gitService) {
|
|
1442
|
+
super(serviceTypeSchema.Enum.Collection, options);
|
|
1443
|
+
this.jsonFileService = jsonFileService;
|
|
1444
|
+
this.gitService = gitService;
|
|
1518
1445
|
}
|
|
1519
1446
|
/**
|
|
1520
|
-
* Creates a new
|
|
1521
|
-
*
|
|
1522
|
-
* @see https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---annotate
|
|
1447
|
+
* Creates a new Collection
|
|
1523
1448
|
*/
|
|
1524
1449
|
async create(props) {
|
|
1525
|
-
|
|
1450
|
+
createCollectionSchema.parse(props);
|
|
1526
1451
|
const id = uuid();
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1452
|
+
const projectPath = pathTo.project(props.projectId);
|
|
1453
|
+
const collectionPath = pathTo.collection(props.projectId, id);
|
|
1454
|
+
const collectionFilePath = pathTo.collectionFile(props.projectId, id);
|
|
1455
|
+
const collectionFile = {
|
|
1456
|
+
...props,
|
|
1457
|
+
objectType: "collection",
|
|
1458
|
+
id,
|
|
1459
|
+
slug: {
|
|
1460
|
+
singular: slug(props.slug.singular),
|
|
1461
|
+
plural: slug(props.slug.plural)
|
|
1462
|
+
},
|
|
1463
|
+
created: datetime(),
|
|
1464
|
+
updated: null
|
|
1465
|
+
};
|
|
1466
|
+
await Fs3.ensureDir(collectionPath);
|
|
1467
|
+
await this.jsonFileService.create(
|
|
1468
|
+
collectionFile,
|
|
1469
|
+
collectionFilePath,
|
|
1470
|
+
collectionFileSchema
|
|
1471
|
+
);
|
|
1472
|
+
await this.gitService.add(projectPath, [collectionFilePath]);
|
|
1473
|
+
await this.gitService.commit(projectPath, this.gitMessage.create);
|
|
1474
|
+
return collectionFile;
|
|
1535
1475
|
}
|
|
1536
1476
|
/**
|
|
1537
|
-
* Returns a
|
|
1538
|
-
*
|
|
1539
|
-
* Internally uses list() but only returns the tag with matching ID.
|
|
1477
|
+
* Returns a Collection by ID
|
|
1540
1478
|
*/
|
|
1541
1479
|
async read(props) {
|
|
1542
|
-
|
|
1543
|
-
const
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
throw new GitError(
|
|
1549
|
-
`Provided tag with UUID "${props.id}" did not match any known tags`
|
|
1550
|
-
);
|
|
1551
|
-
}
|
|
1552
|
-
return tag;
|
|
1480
|
+
readCollectionSchema.parse(props);
|
|
1481
|
+
const collection = await this.jsonFileService.read(
|
|
1482
|
+
pathTo.collectionFile(props.projectId, props.id),
|
|
1483
|
+
collectionFileSchema
|
|
1484
|
+
);
|
|
1485
|
+
return collection;
|
|
1553
1486
|
}
|
|
1554
1487
|
/**
|
|
1555
|
-
*
|
|
1556
|
-
* Please delete the old and create a new one
|
|
1488
|
+
* Updates given Collection
|
|
1557
1489
|
*
|
|
1558
|
-
* @
|
|
1490
|
+
* @todo finish implementing checks for FieldDefinitions and extract methods
|
|
1491
|
+
*
|
|
1492
|
+
* @param projectId Project ID of the collection to update
|
|
1493
|
+
* @param collection Collection to write to disk
|
|
1494
|
+
* @returns An object containing information about the actions needed to be taken,
|
|
1495
|
+
* before given update can be executed or void if the update was executed successfully
|
|
1559
1496
|
*/
|
|
1560
|
-
async update() {
|
|
1561
|
-
|
|
1562
|
-
|
|
1497
|
+
async update(props) {
|
|
1498
|
+
updateCollectionSchema.parse(props);
|
|
1499
|
+
const projectPath = pathTo.project(props.projectId);
|
|
1500
|
+
const collectionFilePath = pathTo.collectionFile(props.projectId, props.id);
|
|
1501
|
+
const prevCollectionFile = await this.read(props);
|
|
1502
|
+
const collectionFile = {
|
|
1503
|
+
...prevCollectionFile,
|
|
1504
|
+
...props,
|
|
1505
|
+
updated: datetime()
|
|
1506
|
+
};
|
|
1507
|
+
await this.jsonFileService.update(
|
|
1508
|
+
collectionFile,
|
|
1509
|
+
collectionFilePath,
|
|
1510
|
+
collectionFileSchema
|
|
1563
1511
|
);
|
|
1512
|
+
await this.gitService.add(projectPath, [collectionFilePath]);
|
|
1513
|
+
await this.gitService.commit(projectPath, this.gitMessage.update);
|
|
1514
|
+
return collectionFile;
|
|
1564
1515
|
}
|
|
1565
1516
|
/**
|
|
1566
|
-
* Deletes
|
|
1567
|
-
*
|
|
1568
|
-
* @see https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---delete
|
|
1517
|
+
* Deletes given Collection (folder), including it's items
|
|
1569
1518
|
*
|
|
1570
|
-
*
|
|
1571
|
-
* @param id UUID of the tag to delete
|
|
1519
|
+
* The Fields that Collection used are not deleted.
|
|
1572
1520
|
*/
|
|
1573
1521
|
async delete(props) {
|
|
1574
|
-
|
|
1575
|
-
const
|
|
1576
|
-
|
|
1522
|
+
deleteCollectionSchema.parse(props);
|
|
1523
|
+
const projectPath = pathTo.project(props.projectId);
|
|
1524
|
+
const collectionPath = pathTo.collection(props.projectId, props.id);
|
|
1525
|
+
await Fs3.remove(collectionPath);
|
|
1526
|
+
await this.gitService.add(projectPath, [collectionPath]);
|
|
1527
|
+
await this.gitService.commit(projectPath, this.gitMessage.delete);
|
|
1577
1528
|
}
|
|
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
1529
|
async list(props) {
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
const
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
const
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
},
|
|
1607
|
-
timestamp: parseInt(lineArray[4])
|
|
1608
|
-
};
|
|
1609
|
-
});
|
|
1610
|
-
const gitTags = lineObjArr.filter(this.isGitTag.bind(this));
|
|
1530
|
+
listCollectionsSchema.parse(props);
|
|
1531
|
+
const offset = props.offset || 0;
|
|
1532
|
+
const limit = props.limit || 15;
|
|
1533
|
+
const collectionReferences = await this.listReferences(
|
|
1534
|
+
objectTypeSchema.Enum.collection,
|
|
1535
|
+
props.projectId
|
|
1536
|
+
);
|
|
1537
|
+
const partialCollectionReferences = collectionReferences.slice(
|
|
1538
|
+
offset,
|
|
1539
|
+
limit
|
|
1540
|
+
);
|
|
1541
|
+
const collections = await returnResolved(
|
|
1542
|
+
partialCollectionReferences.map((reference) => {
|
|
1543
|
+
return this.read({
|
|
1544
|
+
projectId: props.projectId,
|
|
1545
|
+
id: reference.id
|
|
1546
|
+
});
|
|
1547
|
+
})
|
|
1548
|
+
);
|
|
1611
1549
|
return {
|
|
1612
|
-
total:
|
|
1613
|
-
limit
|
|
1614
|
-
offset
|
|
1615
|
-
list:
|
|
1550
|
+
total: collectionReferences.length,
|
|
1551
|
+
limit,
|
|
1552
|
+
offset,
|
|
1553
|
+
list: collections
|
|
1616
1554
|
};
|
|
1617
1555
|
}
|
|
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
1556
|
async count(props) {
|
|
1627
|
-
|
|
1628
|
-
const
|
|
1629
|
-
|
|
1557
|
+
countCollectionsSchema.parse(props);
|
|
1558
|
+
const count = (await this.listReferences(
|
|
1559
|
+
objectTypeSchema.Enum.collection,
|
|
1560
|
+
props.projectId
|
|
1561
|
+
)).length;
|
|
1562
|
+
return count;
|
|
1630
1563
|
}
|
|
1631
1564
|
/**
|
|
1632
|
-
*
|
|
1633
|
-
*
|
|
1634
|
-
* @param obj The object to check
|
|
1565
|
+
* Checks if given object is of type Collection
|
|
1635
1566
|
*/
|
|
1636
|
-
|
|
1637
|
-
return
|
|
1567
|
+
isCollection(obj) {
|
|
1568
|
+
return collectionFileSchema.safeParse(obj).success;
|
|
1638
1569
|
}
|
|
1639
1570
|
};
|
|
1640
1571
|
|
|
1641
|
-
// src/service/
|
|
1642
|
-
|
|
1643
|
-
|
|
1572
|
+
// src/service/EntryService.ts
|
|
1573
|
+
import Fs4 from "fs-extra";
|
|
1574
|
+
var EntryService = class extends AbstractCrudService {
|
|
1575
|
+
jsonFileService;
|
|
1576
|
+
gitService;
|
|
1577
|
+
collectionService;
|
|
1578
|
+
assetService;
|
|
1579
|
+
// private sharedValueService: SharedValueService;
|
|
1580
|
+
constructor(options, jsonFileService, gitService, collectionService, assetService) {
|
|
1581
|
+
super(serviceTypeSchema.Enum.Entry, options);
|
|
1644
1582
|
this.jsonFileService = jsonFileService;
|
|
1583
|
+
this.gitService = gitService;
|
|
1584
|
+
this.collectionService = collectionService;
|
|
1585
|
+
this.assetService = assetService;
|
|
1645
1586
|
}
|
|
1646
1587
|
/**
|
|
1647
|
-
*
|
|
1588
|
+
* Creates a new Entry for given Collection
|
|
1648
1589
|
*/
|
|
1649
|
-
async
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1590
|
+
async create(props) {
|
|
1591
|
+
createEntrySchema.parse(props);
|
|
1592
|
+
const id = uuid();
|
|
1593
|
+
const projectPath = pathTo.project(props.projectId);
|
|
1594
|
+
const entryFilePath = pathTo.entryFile(
|
|
1595
|
+
props.projectId,
|
|
1596
|
+
props.collectionId,
|
|
1597
|
+
id
|
|
1598
|
+
);
|
|
1599
|
+
const collection = await this.collectionService.read({
|
|
1600
|
+
projectId: props.projectId,
|
|
1601
|
+
id: props.collectionId
|
|
1602
|
+
});
|
|
1603
|
+
const entryFile = {
|
|
1604
|
+
objectType: "entry",
|
|
1605
|
+
id,
|
|
1606
|
+
values: props.values,
|
|
1607
|
+
created: datetime(),
|
|
1608
|
+
updated: null
|
|
1609
|
+
};
|
|
1610
|
+
const entry = await this.toEntry({
|
|
1611
|
+
projectId: props.projectId,
|
|
1612
|
+
collectionId: props.collectionId,
|
|
1613
|
+
entryFile
|
|
1614
|
+
});
|
|
1615
|
+
this.validateValues({
|
|
1616
|
+
collectionId: props.collectionId,
|
|
1617
|
+
valueDefinitions: collection.valueDefinitions,
|
|
1618
|
+
values: entry.values
|
|
1619
|
+
});
|
|
1620
|
+
await this.jsonFileService.create(
|
|
1621
|
+
entryFile,
|
|
1622
|
+
entryFilePath,
|
|
1623
|
+
entryFileSchema
|
|
1624
|
+
);
|
|
1625
|
+
await this.gitService.add(projectPath, [entryFilePath]);
|
|
1626
|
+
await this.gitService.commit(projectPath, this.gitMessage.create);
|
|
1627
|
+
return entry;
|
|
1655
1628
|
}
|
|
1656
1629
|
/**
|
|
1657
|
-
*
|
|
1658
|
-
*
|
|
1659
|
-
* By doing so all git operations are done with the signature of this User
|
|
1630
|
+
* Returns an Entry from given Collection by ID and language
|
|
1660
1631
|
*/
|
|
1661
|
-
async
|
|
1662
|
-
|
|
1663
|
-
const
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1632
|
+
async read(props) {
|
|
1633
|
+
readEntrySchema.parse(props);
|
|
1634
|
+
const entryFile = await this.jsonFileService.read(
|
|
1635
|
+
pathTo.entryFile(props.projectId, props.collectionId, props.id),
|
|
1636
|
+
entryFileSchema
|
|
1637
|
+
);
|
|
1638
|
+
return await this.toEntry({
|
|
1639
|
+
projectId: props.projectId,
|
|
1640
|
+
collectionId: props.collectionId,
|
|
1641
|
+
entryFile
|
|
1642
|
+
});
|
|
1671
1643
|
}
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
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
|
-
}
|
|
1738
|
-
};
|
|
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() !== "";
|
|
1752
|
-
});
|
|
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
|
-
}
|
|
1644
|
+
/**
|
|
1645
|
+
* Updates an Entry of given Collection with new Values and shared Values
|
|
1646
|
+
*/
|
|
1647
|
+
async update(props) {
|
|
1648
|
+
updateEntrySchema.parse(props);
|
|
1649
|
+
const projectPath = pathTo.project(props.projectId);
|
|
1650
|
+
const entryFilePath = pathTo.entryFile(
|
|
1651
|
+
props.projectId,
|
|
1652
|
+
props.collectionId,
|
|
1653
|
+
props.id
|
|
1654
|
+
);
|
|
1655
|
+
const collection = await this.collectionService.read({
|
|
1656
|
+
projectId: props.projectId,
|
|
1657
|
+
id: props.collectionId
|
|
1658
|
+
});
|
|
1659
|
+
const prevEntryFile = await this.read({
|
|
1660
|
+
projectId: props.projectId,
|
|
1661
|
+
collectionId: props.collectionId,
|
|
1662
|
+
id: props.id
|
|
1663
|
+
});
|
|
1664
|
+
const entryFile = {
|
|
1665
|
+
...prevEntryFile,
|
|
1666
|
+
values: props.values,
|
|
1667
|
+
updated: datetime()
|
|
1807
1668
|
};
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
// No concurrency because git operations are sequencial
|
|
1669
|
+
const entry = await this.toEntry({
|
|
1670
|
+
projectId: props.projectId,
|
|
1671
|
+
collectionId: props.collectionId,
|
|
1672
|
+
entryFile
|
|
1813
1673
|
});
|
|
1814
|
-
this.
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1674
|
+
this.validateValues({
|
|
1675
|
+
collectionId: props.collectionId,
|
|
1676
|
+
valueDefinitions: collection.valueDefinitions,
|
|
1677
|
+
values: entry.values
|
|
1678
|
+
});
|
|
1679
|
+
await this.jsonFileService.update(
|
|
1680
|
+
entryFile,
|
|
1681
|
+
entryFilePath,
|
|
1682
|
+
entryFileSchema
|
|
1683
|
+
);
|
|
1684
|
+
await this.gitService.add(projectPath, [entryFilePath]);
|
|
1685
|
+
await this.gitService.commit(projectPath, this.gitMessage.update);
|
|
1686
|
+
return entry;
|
|
1818
1687
|
}
|
|
1819
1688
|
/**
|
|
1820
|
-
*
|
|
1689
|
+
* Deletes given Entry from it's Collection
|
|
1821
1690
|
*/
|
|
1822
|
-
|
|
1823
|
-
|
|
1691
|
+
async delete(props) {
|
|
1692
|
+
deleteEntrySchema.parse(props);
|
|
1693
|
+
const projectPath = pathTo.project(props.projectId);
|
|
1694
|
+
const entryFilePath = pathTo.entryFile(
|
|
1695
|
+
props.projectId,
|
|
1696
|
+
props.collectionId,
|
|
1697
|
+
props.id
|
|
1698
|
+
);
|
|
1699
|
+
await Fs4.remove(entryFilePath);
|
|
1700
|
+
await this.gitService.add(projectPath, [entryFilePath]);
|
|
1701
|
+
await this.gitService.commit(projectPath, this.gitMessage.delete);
|
|
1702
|
+
}
|
|
1703
|
+
async list(props) {
|
|
1704
|
+
listEntriesSchema.parse(props);
|
|
1705
|
+
const offset = props.offset || 0;
|
|
1706
|
+
const limit = props.limit || 15;
|
|
1707
|
+
const entryReferences = await this.listReferences(
|
|
1708
|
+
objectTypeSchema.Enum.entry,
|
|
1709
|
+
props.projectId,
|
|
1710
|
+
props.collectionId
|
|
1711
|
+
);
|
|
1712
|
+
const partialEntryReferences = entryReferences.slice(offset, limit);
|
|
1713
|
+
const entries = await returnResolved(
|
|
1714
|
+
partialEntryReferences.map((reference) => {
|
|
1715
|
+
return this.read({
|
|
1716
|
+
projectId: props.projectId,
|
|
1717
|
+
collectionId: props.collectionId,
|
|
1718
|
+
id: reference.id
|
|
1719
|
+
});
|
|
1720
|
+
})
|
|
1721
|
+
);
|
|
1722
|
+
return {
|
|
1723
|
+
total: entryReferences.length,
|
|
1724
|
+
limit,
|
|
1725
|
+
offset,
|
|
1726
|
+
list: entries
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
async count(props) {
|
|
1730
|
+
countEntriesSchema.parse(props);
|
|
1731
|
+
return (await this.listReferences(
|
|
1732
|
+
objectTypeSchema.Enum.entry,
|
|
1733
|
+
props.projectId,
|
|
1734
|
+
props.collectionId
|
|
1735
|
+
)).length;
|
|
1824
1736
|
}
|
|
1825
1737
|
/**
|
|
1826
|
-
*
|
|
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
|
|
1738
|
+
* Checks if given object is of type Entry
|
|
1832
1739
|
*/
|
|
1833
|
-
|
|
1834
|
-
|
|
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);
|
|
1740
|
+
isEntry(obj) {
|
|
1741
|
+
return entrySchema.safeParse(obj).success;
|
|
1841
1742
|
}
|
|
1842
1743
|
/**
|
|
1843
|
-
*
|
|
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
|
|
1744
|
+
* Returns a Value definition by ID
|
|
1853
1745
|
*/
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1746
|
+
getValueDefinitionById(props) {
|
|
1747
|
+
const definition = props.valueDefinitions.find((def) => {
|
|
1748
|
+
if (def.id === props.id) {
|
|
1749
|
+
return true;
|
|
1750
|
+
}
|
|
1751
|
+
return false;
|
|
1752
|
+
});
|
|
1753
|
+
if (!definition) {
|
|
1754
|
+
throw new Error(
|
|
1755
|
+
`No definition with ID "${props.id}" found in Collection "${props.collectionId}" for given Value reference`
|
|
1756
|
+
);
|
|
1858
1757
|
}
|
|
1859
|
-
|
|
1860
|
-
|
|
1758
|
+
return definition;
|
|
1759
|
+
}
|
|
1760
|
+
/**
|
|
1761
|
+
* Validates given Values against it's Collections definitions
|
|
1762
|
+
*/
|
|
1763
|
+
validateValues(props) {
|
|
1764
|
+
props.values.map((value) => {
|
|
1765
|
+
const definition = this.getValueDefinitionById({
|
|
1766
|
+
collectionId: props.collectionId,
|
|
1767
|
+
valueDefinitions: props.valueDefinitions,
|
|
1768
|
+
id: value.definitionId
|
|
1769
|
+
});
|
|
1770
|
+
const schema = getValueContentSchemaFromDefinition(definition);
|
|
1771
|
+
try {
|
|
1772
|
+
for (const [, content] of Object.entries(value.content)) {
|
|
1773
|
+
schema.parse(content);
|
|
1774
|
+
}
|
|
1775
|
+
} catch (error) {
|
|
1776
|
+
console.log("Definition:", definition);
|
|
1777
|
+
console.log("Value:", value);
|
|
1778
|
+
throw error;
|
|
1779
|
+
}
|
|
1780
|
+
});
|
|
1781
|
+
}
|
|
1782
|
+
/**
|
|
1783
|
+
* Validates given shared Value references against it's Collections definitions
|
|
1784
|
+
*/
|
|
1785
|
+
// private validateResolvedSharedValues(props: {
|
|
1786
|
+
// collectionId: string;
|
|
1787
|
+
// valueDefinitions: ValueDefinition[];
|
|
1788
|
+
// resolvedSharedValues: ResolvedSharedValueReference[];
|
|
1789
|
+
// }) {
|
|
1790
|
+
// props.resolvedSharedValues.map((value) => {
|
|
1791
|
+
// const definition = this.getValueDefinitionById({
|
|
1792
|
+
// collectionId: props.collectionId,
|
|
1793
|
+
// valueDefinitions: props.valueDefinitions,
|
|
1794
|
+
// id: value.definitionId,
|
|
1795
|
+
// });
|
|
1796
|
+
// const schema = getValueSchemaFromDefinition(definition);
|
|
1797
|
+
// schema.parse(value.resolved.content);
|
|
1798
|
+
// });
|
|
1799
|
+
// }
|
|
1800
|
+
async resolveValueContentReference(props) {
|
|
1801
|
+
switch (props.valueContentReference.objectType) {
|
|
1802
|
+
case objectTypeSchema.Enum.asset:
|
|
1803
|
+
return await this.assetService.read({
|
|
1804
|
+
projectId: props.projectId,
|
|
1805
|
+
id: props.valueContentReference.id,
|
|
1806
|
+
language: props.valueContentReference.language
|
|
1807
|
+
});
|
|
1808
|
+
case objectTypeSchema.Enum.entry:
|
|
1809
|
+
return await this.read({
|
|
1810
|
+
projectId: props.projectId,
|
|
1811
|
+
collectionId: props.collectionId,
|
|
1812
|
+
id: props.valueContentReference.id
|
|
1813
|
+
});
|
|
1814
|
+
default:
|
|
1815
|
+
throw new Error(
|
|
1816
|
+
// @ts-ignore
|
|
1817
|
+
`Tried to resolve unsupported Value reference "${props.valueContentReference.referenceObjectType}"`
|
|
1818
|
+
);
|
|
1861
1819
|
}
|
|
1862
|
-
|
|
1863
|
-
|
|
1820
|
+
}
|
|
1821
|
+
async resolveValueContentReferences(props) {
|
|
1822
|
+
let resolvedContent = {};
|
|
1823
|
+
for (const language in props.valueReference.content) {
|
|
1824
|
+
const referencesOfLanguage = props.valueReference.content[language];
|
|
1825
|
+
if (!referencesOfLanguage) {
|
|
1826
|
+
throw new Error(
|
|
1827
|
+
`Trying to access content references by language "${language}" failed`
|
|
1828
|
+
);
|
|
1829
|
+
}
|
|
1830
|
+
const resolvedReferencesOfLanguage = await Promise.all(
|
|
1831
|
+
referencesOfLanguage.map(async (reference) => {
|
|
1832
|
+
return await this.resolveValueContentReference({
|
|
1833
|
+
projectId: props.projectId,
|
|
1834
|
+
collectionId: props.collectionId,
|
|
1835
|
+
valueContentReference: reference
|
|
1836
|
+
});
|
|
1837
|
+
})
|
|
1838
|
+
);
|
|
1839
|
+
resolvedContent = {
|
|
1840
|
+
...resolvedContent,
|
|
1841
|
+
[language]: resolvedReferencesOfLanguage
|
|
1842
|
+
};
|
|
1864
1843
|
}
|
|
1865
|
-
|
|
1866
|
-
await this.setLocalConfig(path);
|
|
1844
|
+
return resolvedContent;
|
|
1867
1845
|
}
|
|
1868
1846
|
/**
|
|
1869
|
-
*
|
|
1870
|
-
*
|
|
1871
|
-
* @see https://git-scm.com/docs/git-add
|
|
1872
|
-
*
|
|
1873
|
-
* @param path Path to the repository
|
|
1874
|
-
* @param files Files to add
|
|
1847
|
+
* Creates an Entry from given EntryFile by resolving it's Values
|
|
1875
1848
|
*/
|
|
1876
|
-
async
|
|
1877
|
-
|
|
1878
|
-
|
|
1849
|
+
async toEntry(props) {
|
|
1850
|
+
return {
|
|
1851
|
+
...props.entryFile,
|
|
1852
|
+
// @ts-ignore @todo fixme - I have no idea why this happens. The types seem to be compatible to me and they work
|
|
1853
|
+
values: await Promise.all(
|
|
1854
|
+
props.entryFile.values.map(async (value) => {
|
|
1855
|
+
if (value.valueType === ValueTypeSchema.Enum.reference) {
|
|
1856
|
+
const resolvedContentReferences = await this.resolveValueContentReferences({
|
|
1857
|
+
projectId: props.projectId,
|
|
1858
|
+
collectionId: props.collectionId,
|
|
1859
|
+
valueReference: value
|
|
1860
|
+
});
|
|
1861
|
+
return {
|
|
1862
|
+
...value,
|
|
1863
|
+
content: resolvedContentReferences
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
return value;
|
|
1867
|
+
})
|
|
1868
|
+
)
|
|
1869
|
+
};
|
|
1870
|
+
}
|
|
1871
|
+
};
|
|
1872
|
+
|
|
1873
|
+
// src/service/GitService.ts
|
|
1874
|
+
import { GitProcess } from "dugite";
|
|
1875
|
+
import { EOL as EOL2 } from "os";
|
|
1876
|
+
import PQueue from "p-queue";
|
|
1877
|
+
|
|
1878
|
+
// src/service/GitTagService.ts
|
|
1879
|
+
import { EOL } from "os";
|
|
1880
|
+
var GitTagService = class extends AbstractCrudService {
|
|
1881
|
+
git;
|
|
1882
|
+
constructor(options, git) {
|
|
1883
|
+
super(serviceTypeSchema.Enum.GitTag, options);
|
|
1884
|
+
this.git = git;
|
|
1879
1885
|
}
|
|
1880
1886
|
/**
|
|
1881
|
-
*
|
|
1882
|
-
*
|
|
1883
|
-
* @todo maybe add more options
|
|
1884
|
-
* @see https://git-scm.com/docs/git-reset
|
|
1887
|
+
* Creates a new tag
|
|
1885
1888
|
*
|
|
1886
|
-
* @
|
|
1887
|
-
* @param mode Modifies the working tree depending on given mode
|
|
1888
|
-
* @param commit Resets the current branch head to this commit / tag
|
|
1889
|
+
* @see https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---annotate
|
|
1889
1890
|
*/
|
|
1890
|
-
async
|
|
1891
|
-
|
|
1892
|
-
|
|
1891
|
+
async create(props) {
|
|
1892
|
+
createGitTagSchema.parse(props);
|
|
1893
|
+
const id = uuid();
|
|
1894
|
+
let args = ["tag", "--annotate", id];
|
|
1895
|
+
if (props.hash) {
|
|
1896
|
+
args = [...args, props.hash];
|
|
1897
|
+
}
|
|
1898
|
+
args = [...args, "-m", props.message];
|
|
1899
|
+
await this.git(props.path, args);
|
|
1900
|
+
const tag = await this.read({ path: props.path, id });
|
|
1901
|
+
return tag;
|
|
1893
1902
|
}
|
|
1894
1903
|
/**
|
|
1895
|
-
*
|
|
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
|
|
1904
|
+
* Returns a tag by ID
|
|
1903
1905
|
*
|
|
1904
|
-
*
|
|
1905
|
-
* @param source Git commit SHA or tag name to restore to
|
|
1906
|
-
* @param files Files to restore
|
|
1906
|
+
* Internally uses list() but only returns the tag with matching ID.
|
|
1907
1907
|
*/
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1908
|
+
async read(props) {
|
|
1909
|
+
readGitTagSchema.parse(props);
|
|
1910
|
+
const tags = await this.list({ path: props.path });
|
|
1911
|
+
const tag = tags.list.find((tag2) => {
|
|
1912
|
+
return tag2.id === props.id;
|
|
1913
|
+
});
|
|
1914
|
+
if (!tag) {
|
|
1915
|
+
throw new GitError(
|
|
1916
|
+
`Provided tag with UUID "${props.id}" did not match any known tags`
|
|
1917
|
+
);
|
|
1918
|
+
}
|
|
1919
|
+
return tag;
|
|
1920
|
+
}
|
|
1916
1921
|
/**
|
|
1917
|
-
*
|
|
1918
|
-
*
|
|
1919
|
-
* @see https://www.git-scm.com/docs/git-fetch
|
|
1922
|
+
* Updating a git tag is not supported.
|
|
1923
|
+
* Please delete the old and create a new one
|
|
1920
1924
|
*
|
|
1921
|
-
* @
|
|
1925
|
+
* @see https://git-scm.com/docs/git-tag#_on_re_tagging
|
|
1922
1926
|
*/
|
|
1923
|
-
async
|
|
1924
|
-
|
|
1925
|
-
|
|
1927
|
+
async update() {
|
|
1928
|
+
throw new Error(
|
|
1929
|
+
"Updating a git tag is not supported. Please delete the old and create a new one"
|
|
1930
|
+
);
|
|
1926
1931
|
}
|
|
1927
1932
|
/**
|
|
1928
|
-
*
|
|
1933
|
+
* Deletes a tag
|
|
1929
1934
|
*
|
|
1930
|
-
* @see https://git-scm.com/docs/git-
|
|
1935
|
+
* @see https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---delete
|
|
1931
1936
|
*
|
|
1932
|
-
* @param path
|
|
1937
|
+
* @param path Path to the repository
|
|
1938
|
+
* @param id UUID of the tag to delete
|
|
1933
1939
|
*/
|
|
1934
|
-
async
|
|
1935
|
-
|
|
1936
|
-
|
|
1940
|
+
async delete(props) {
|
|
1941
|
+
deleteGitTagSchema.parse(props);
|
|
1942
|
+
const args = ["tag", "--delete", props.id];
|
|
1943
|
+
await this.git(props.path, args);
|
|
1937
1944
|
}
|
|
1938
1945
|
/**
|
|
1939
|
-
*
|
|
1946
|
+
* Gets all local tags or filter them by pattern
|
|
1940
1947
|
*
|
|
1941
|
-
*
|
|
1948
|
+
* They are sorted by authordate of the commit, not when the tag is created.
|
|
1949
|
+
* This ensures tags are sorted correctly in the timeline of their commits.
|
|
1942
1950
|
*
|
|
1943
|
-
* @
|
|
1951
|
+
* @see https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---list
|
|
1944
1952
|
*/
|
|
1945
|
-
async
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
+
async list(props) {
|
|
1954
|
+
listGitTagsSchema.parse(props);
|
|
1955
|
+
let args = ["tag", "--list"];
|
|
1956
|
+
args = [
|
|
1957
|
+
...args,
|
|
1958
|
+
"--sort=-*authordate",
|
|
1959
|
+
"--format=%(refname:short)|%(subject)|%(*authorname)|%(*authoremail)|%(*authordate:iso-strict)"
|
|
1960
|
+
];
|
|
1961
|
+
const result = await this.git(props.path, args);
|
|
1962
|
+
const noEmptyLinesArr = result.stdout.split(EOL).filter((line) => {
|
|
1963
|
+
return line.trim() !== "";
|
|
1964
|
+
});
|
|
1965
|
+
const lineObjArr = noEmptyLinesArr.map((line) => {
|
|
1966
|
+
const lineArray = line.split("|");
|
|
1967
|
+
return {
|
|
1968
|
+
id: lineArray[0],
|
|
1969
|
+
message: lineArray[1],
|
|
1970
|
+
author: {
|
|
1971
|
+
name: lineArray[2],
|
|
1972
|
+
email: lineArray[3]
|
|
1973
|
+
},
|
|
1974
|
+
datetime: datetime(lineArray[4])
|
|
1975
|
+
};
|
|
1976
|
+
});
|
|
1977
|
+
const gitTags = lineObjArr.filter(this.isGitTag.bind(this));
|
|
1978
|
+
return {
|
|
1979
|
+
total: gitTags.length,
|
|
1980
|
+
limit: 0,
|
|
1981
|
+
offset: 0,
|
|
1982
|
+
list: gitTags
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
/**
|
|
1986
|
+
* Returns the total number of tags inside given repository
|
|
1987
|
+
*
|
|
1988
|
+
* Internally uses list(), so do not use count()
|
|
1989
|
+
* in conjuncion with it to avoid multiple git calls.
|
|
1990
|
+
*
|
|
1991
|
+
* @param path Path to the repository
|
|
1992
|
+
*/
|
|
1993
|
+
async count(props) {
|
|
1994
|
+
countGitTagsSchema.parse(props);
|
|
1995
|
+
const gitTags = await this.list({ path: props.path });
|
|
1996
|
+
return gitTags.total;
|
|
1997
|
+
}
|
|
1998
|
+
/**
|
|
1999
|
+
* Type guard for GitTag
|
|
2000
|
+
*
|
|
2001
|
+
* @param obj The object to check
|
|
2002
|
+
*/
|
|
2003
|
+
isGitTag(obj) {
|
|
2004
|
+
return gitTagSchema.safeParse(obj).success;
|
|
2005
|
+
}
|
|
2006
|
+
};
|
|
2007
|
+
|
|
2008
|
+
// src/service/GitService.ts
|
|
2009
|
+
var GitService = class {
|
|
2010
|
+
version;
|
|
2011
|
+
gitPath;
|
|
2012
|
+
queue;
|
|
2013
|
+
gitTagService;
|
|
2014
|
+
userService;
|
|
2015
|
+
constructor(options, userService) {
|
|
2016
|
+
this.version = null;
|
|
2017
|
+
this.gitPath = null;
|
|
2018
|
+
this.queue = new PQueue({
|
|
2019
|
+
concurrency: 1
|
|
2020
|
+
// No concurrency because git operations are sequencial
|
|
2021
|
+
});
|
|
2022
|
+
this.gitTagService = new GitTagService(options, this.git);
|
|
2023
|
+
this.userService = userService;
|
|
2024
|
+
this.updateVersion();
|
|
2025
|
+
this.updateGitPath();
|
|
2026
|
+
}
|
|
2027
|
+
/**
|
|
2028
|
+
* CRUD methods to work with git tags
|
|
2029
|
+
*/
|
|
2030
|
+
get tags() {
|
|
2031
|
+
return this.gitTagService;
|
|
2032
|
+
}
|
|
2033
|
+
/**
|
|
2034
|
+
* Create an empty Git repository or reinitialize an existing one
|
|
2035
|
+
*
|
|
2036
|
+
* @see https://git-scm.com/docs/git-init
|
|
2037
|
+
*
|
|
2038
|
+
* @param path Path to initialize in. Fails if path does not exist
|
|
2039
|
+
* @param options Options specific to the init operation
|
|
2040
|
+
*/
|
|
2041
|
+
async init(path, options) {
|
|
2042
|
+
let args = ["init"];
|
|
2043
|
+
if (options?.initialBranch) {
|
|
2044
|
+
args = [...args, `--initial-branch=${options.initialBranch}`];
|
|
2045
|
+
}
|
|
2046
|
+
await this.git(path, args);
|
|
2047
|
+
await this.setLocalConfig(path);
|
|
2048
|
+
await this.installLfs(path);
|
|
2049
|
+
}
|
|
2050
|
+
/**
|
|
2051
|
+
* Clone a repository into a directory
|
|
2052
|
+
*
|
|
2053
|
+
* @see https://git-scm.com/docs/git-clone
|
|
2054
|
+
*
|
|
2055
|
+
* @todo Implement progress callback / events
|
|
2056
|
+
*
|
|
2057
|
+
* @param url The remote repository URL to clone from
|
|
2058
|
+
* @param path The destination path for the cloned repository.
|
|
2059
|
+
* Which is only working if the directory is existing and empty.
|
|
2060
|
+
* @param options Options specific to the clone operation
|
|
2061
|
+
*/
|
|
2062
|
+
async clone(url, path, options) {
|
|
2063
|
+
let args = ["clone", "--progress"];
|
|
2064
|
+
if (options?.branch) {
|
|
2065
|
+
args = [...args, "--branch", options.branch];
|
|
2066
|
+
}
|
|
2067
|
+
if (options?.depth) {
|
|
2068
|
+
args = [...args, "--depth", options.depth.toString()];
|
|
2069
|
+
}
|
|
2070
|
+
if (options?.singleBranch === true) {
|
|
2071
|
+
args = [...args, "--single-branch"];
|
|
2072
|
+
}
|
|
2073
|
+
await this.git("", [...args, url, path]);
|
|
2074
|
+
await this.setLocalConfig(path);
|
|
2075
|
+
}
|
|
2076
|
+
/**
|
|
2077
|
+
* Add file contents to the index
|
|
2078
|
+
*
|
|
2079
|
+
* @see https://git-scm.com/docs/git-add
|
|
2080
|
+
*
|
|
2081
|
+
* @param path Path to the repository
|
|
2082
|
+
* @param files Files to add
|
|
2083
|
+
*/
|
|
2084
|
+
async add(path, files2) {
|
|
2085
|
+
const args = ["add", "--", ...files2];
|
|
2086
|
+
await this.git(path, args);
|
|
2087
|
+
}
|
|
2088
|
+
branches = {
|
|
2089
|
+
/**
|
|
2090
|
+
* List branches
|
|
2091
|
+
*
|
|
2092
|
+
* @see https://www.git-scm.com/docs/git-branch
|
|
2093
|
+
*
|
|
2094
|
+
* @param path Path to the repository
|
|
2095
|
+
*/
|
|
2096
|
+
list: async (path) => {
|
|
2097
|
+
const args = ["branch", "--list", "--all"];
|
|
2098
|
+
const result = await this.git(path, args);
|
|
2099
|
+
const normalizedLinesArr = result.stdout.split(EOL2).filter((line) => {
|
|
2100
|
+
return line.trim() !== "";
|
|
2101
|
+
}).map((line) => {
|
|
2102
|
+
return line.trim().replace("* ", "");
|
|
2103
|
+
});
|
|
2104
|
+
const local = [];
|
|
2105
|
+
const remote = [];
|
|
2106
|
+
normalizedLinesArr.forEach((line) => {
|
|
2107
|
+
if (line.startsWith("remotes/")) {
|
|
2108
|
+
remote.push(line.replace("remotes/", ""));
|
|
2109
|
+
} else {
|
|
2110
|
+
local.push(line);
|
|
2111
|
+
}
|
|
2112
|
+
});
|
|
2113
|
+
return {
|
|
2114
|
+
local,
|
|
2115
|
+
remote
|
|
2116
|
+
};
|
|
2117
|
+
},
|
|
2118
|
+
/**
|
|
2119
|
+
* Returns the name of the current branch. In detached HEAD state, an empty string is returned.
|
|
2120
|
+
*
|
|
2121
|
+
* @see https://www.git-scm.com/docs/git-branch#Documentation/git-branch.txt---show-current
|
|
2122
|
+
*
|
|
2123
|
+
* @param path Path to the repository
|
|
2124
|
+
*/
|
|
2125
|
+
current: async (path) => {
|
|
2126
|
+
const args = ["branch", "--show-current"];
|
|
2127
|
+
const result = await this.git(path, args);
|
|
2128
|
+
return result.stdout.trim();
|
|
2129
|
+
},
|
|
2130
|
+
/**
|
|
2131
|
+
* Switch branches
|
|
2132
|
+
*
|
|
2133
|
+
* @see https://git-scm.com/docs/git-switch/
|
|
2134
|
+
*
|
|
2135
|
+
* @param path Path to the repository
|
|
2136
|
+
* @param branch Name of the branch to switch to
|
|
2137
|
+
* @param options Options specific to the switch operation
|
|
2138
|
+
*/
|
|
2139
|
+
switch: async (path, branch, options) => {
|
|
2140
|
+
await this.checkBranchOrTagName(path, branch);
|
|
2141
|
+
let args = ["switch"];
|
|
2142
|
+
if (options?.isNew === true) {
|
|
2143
|
+
args = [...args, "--create", branch];
|
|
2144
|
+
} else {
|
|
2145
|
+
args = [...args, branch];
|
|
2146
|
+
}
|
|
2147
|
+
await this.git(path, args);
|
|
2148
|
+
}
|
|
2149
|
+
};
|
|
2150
|
+
remotes = {
|
|
2151
|
+
/**
|
|
2152
|
+
* Returns a list of currently tracked remotes
|
|
2153
|
+
*
|
|
2154
|
+
* @see https://git-scm.com/docs/git-remote
|
|
2155
|
+
*
|
|
2156
|
+
* @param path Path to the repository
|
|
2157
|
+
*/
|
|
2158
|
+
list: async (path) => {
|
|
2159
|
+
const args = ["remote"];
|
|
2160
|
+
const result = await this.git(path, args);
|
|
2161
|
+
const normalizedLinesArr = result.stdout.split(EOL2).filter((line) => {
|
|
2162
|
+
return line.trim() !== "";
|
|
2163
|
+
});
|
|
2164
|
+
return normalizedLinesArr;
|
|
2165
|
+
},
|
|
2166
|
+
/**
|
|
2167
|
+
* Returns true if the `origin` remote exists, otherwise false
|
|
2168
|
+
*
|
|
2169
|
+
* @param path Path to the repository
|
|
2170
|
+
*/
|
|
2171
|
+
hasOrigin: async (path) => {
|
|
2172
|
+
const remotes = await this.remotes.list(path);
|
|
2173
|
+
if (remotes.includes("origin")) {
|
|
2174
|
+
return true;
|
|
2175
|
+
}
|
|
2176
|
+
return false;
|
|
2177
|
+
},
|
|
2178
|
+
/**
|
|
2179
|
+
* Adds the `origin` remote with given URL
|
|
2180
|
+
*
|
|
2181
|
+
* Throws if `origin` remote is added already.
|
|
2182
|
+
*
|
|
2183
|
+
* @see https://git-scm.com/docs/git-remote#Documentation/git-remote.txt-emaddem
|
|
2184
|
+
*
|
|
2185
|
+
* @param path Path to the repository
|
|
2186
|
+
*/
|
|
2187
|
+
addOrigin: async (path, url) => {
|
|
2188
|
+
const args = ["remote", "add", "origin", url];
|
|
2189
|
+
await this.git(path, args);
|
|
2190
|
+
},
|
|
2191
|
+
/**
|
|
2192
|
+
* Returns the current `origin` remote URL
|
|
2193
|
+
*
|
|
2194
|
+
* Throws if no `origin` remote is added yet.
|
|
2195
|
+
*
|
|
2196
|
+
* @see https://git-scm.com/docs/git-remote#Documentation/git-remote.txt-emget-urlem
|
|
2197
|
+
*
|
|
2198
|
+
* @param path Path to the repository
|
|
2199
|
+
*/
|
|
2200
|
+
getOriginUrl: async (path) => {
|
|
2201
|
+
const args = ["remote", "get-url", "origin"];
|
|
2202
|
+
const result = (await this.git(path, args)).stdout.trim();
|
|
2203
|
+
return result.length === 0 ? null : result;
|
|
2204
|
+
},
|
|
2205
|
+
/**
|
|
2206
|
+
* Sets the current `origin` remote URL
|
|
2207
|
+
*
|
|
2208
|
+
* Throws if no `origin` remote is added yet.
|
|
2209
|
+
*
|
|
2210
|
+
* @see https://git-scm.com/docs/git-remote#Documentation/git-remote.txt-emset-urlem
|
|
2211
|
+
*
|
|
2212
|
+
* @param path Path to the repository
|
|
2213
|
+
*/
|
|
2214
|
+
setOriginUrl: async (path, url) => {
|
|
2215
|
+
const args = ["remote", "set-url", "origin", url];
|
|
2216
|
+
await this.git(path, args);
|
|
2217
|
+
}
|
|
2218
|
+
};
|
|
2219
|
+
/**
|
|
2220
|
+
* Reset current HEAD to the specified state
|
|
2221
|
+
*
|
|
2222
|
+
* @todo maybe add more options
|
|
2223
|
+
* @see https://git-scm.com/docs/git-reset
|
|
2224
|
+
*
|
|
2225
|
+
* @param path Path to the repository
|
|
2226
|
+
* @param mode Modifies the working tree depending on given mode
|
|
2227
|
+
* @param commit Resets the current branch head to this commit / tag
|
|
2228
|
+
*/
|
|
2229
|
+
async reset(path, mode, commit) {
|
|
2230
|
+
const args = ["reset", `--${mode}`, commit];
|
|
2231
|
+
await this.git(path, args);
|
|
2232
|
+
}
|
|
2233
|
+
/**
|
|
2234
|
+
* Restore working tree files
|
|
2235
|
+
*
|
|
2236
|
+
* @see https://git-scm.com/docs/git-restore/
|
|
2237
|
+
*
|
|
2238
|
+
* @todo It's probably a good idea to not use restore
|
|
2239
|
+
* for a use case where someone just wants to have a look
|
|
2240
|
+
* and maybe copy something from a deleted file.
|
|
2241
|
+
* We should use `checkout` without `add .` and `commit` for that
|
|
2242
|
+
*
|
|
2243
|
+
* @param path Path to the repository
|
|
2244
|
+
* @param source Git commit SHA or tag name to restore to
|
|
2245
|
+
* @param files Files to restore
|
|
2246
|
+
*/
|
|
2247
|
+
// public async restore(
|
|
2248
|
+
// path: string,
|
|
2249
|
+
// source: string,
|
|
2250
|
+
// files: string[]
|
|
2251
|
+
// ): Promise<void> {
|
|
2252
|
+
// const args = ['restore', `--source=${source}`, ...files];
|
|
2253
|
+
// await this.git(path, args);
|
|
2254
|
+
// }
|
|
2255
|
+
/**
|
|
2256
|
+
* Download objects and refs from remote `origin`
|
|
2257
|
+
*
|
|
2258
|
+
* @see https://www.git-scm.com/docs/git-fetch
|
|
2259
|
+
*
|
|
2260
|
+
* @param path Path to the repository
|
|
2261
|
+
*/
|
|
2262
|
+
async fetch(path) {
|
|
2263
|
+
const args = ["fetch"];
|
|
2264
|
+
await this.git(path, args);
|
|
2265
|
+
}
|
|
2266
|
+
/**
|
|
2267
|
+
* Fetch from and integrate (rebase or merge) with a local branch
|
|
2268
|
+
*
|
|
2269
|
+
* @see https://git-scm.com/docs/git-pull
|
|
2270
|
+
*
|
|
2271
|
+
* @param path Path to the repository
|
|
2272
|
+
*/
|
|
2273
|
+
async pull(path) {
|
|
2274
|
+
const args = ["pull"];
|
|
2275
|
+
await this.git(path, args);
|
|
2276
|
+
}
|
|
2277
|
+
/**
|
|
2278
|
+
* Update remote refs along with associated objects to remote `origin`
|
|
2279
|
+
*
|
|
2280
|
+
* @see https://git-scm.com/docs/git-push
|
|
2281
|
+
*
|
|
2282
|
+
* @param path Path to the repository
|
|
2283
|
+
*/
|
|
2284
|
+
async push(path, options) {
|
|
2285
|
+
let args = ["push", "origin"];
|
|
2286
|
+
if (options?.all === true) {
|
|
2287
|
+
args = [...args, "--all"];
|
|
2288
|
+
}
|
|
2289
|
+
if (options?.force === true) {
|
|
2290
|
+
args = [...args, "--force"];
|
|
2291
|
+
}
|
|
1953
2292
|
await this.git(path, args);
|
|
1954
2293
|
}
|
|
1955
2294
|
/**
|
|
@@ -1996,7 +2335,7 @@ var GitService2 = class {
|
|
|
1996
2335
|
}
|
|
1997
2336
|
const result = await this.git(path, [
|
|
1998
2337
|
...args,
|
|
1999
|
-
"--format=%H|%s|%an|%ae|%
|
|
2338
|
+
"--format=%H|%s|%an|%ae|%aI|%D"
|
|
2000
2339
|
]);
|
|
2001
2340
|
const noEmptyLinesArr = result.stdout.split(EOL2).filter((line) => {
|
|
2002
2341
|
return line.trim() !== "";
|
|
@@ -2010,8 +2349,8 @@ var GitService2 = class {
|
|
|
2010
2349
|
name: lineArray[2],
|
|
2011
2350
|
email: lineArray[3]
|
|
2012
2351
|
},
|
|
2013
|
-
|
|
2014
|
-
tag: this.refNameToTagName(lineArray[5])
|
|
2352
|
+
datetime: datetime(lineArray[4]),
|
|
2353
|
+
tag: this.refNameToTagName(lineArray[5] || "")
|
|
2015
2354
|
};
|
|
2016
2355
|
});
|
|
2017
2356
|
return lineObjArr.filter(this.isGitCommit.bind(this));
|
|
@@ -2024,69 +2363,13 @@ var GitService2 = class {
|
|
|
2024
2363
|
return tagName;
|
|
2025
2364
|
}
|
|
2026
2365
|
/**
|
|
2027
|
-
*
|
|
2028
|
-
*
|
|
2029
|
-
* Git only returns the timestamp the file was added,
|
|
2030
|
-
* which could be different from the file being created.
|
|
2031
|
-
* But since file operations will always be committed
|
|
2032
|
-
* immediately, this is practically the same.
|
|
2366
|
+
* Reads the currently used version of Git
|
|
2033
2367
|
*
|
|
2034
|
-
*
|
|
2035
|
-
* @param file File to get timestamp from
|
|
2368
|
+
* This can help debugging
|
|
2036
2369
|
*/
|
|
2037
|
-
async
|
|
2038
|
-
const result = await this.git(
|
|
2039
|
-
|
|
2040
|
-
"--diff-filter=A",
|
|
2041
|
-
"--follow",
|
|
2042
|
-
"--format=%at",
|
|
2043
|
-
"--max-count=1",
|
|
2044
|
-
"--",
|
|
2045
|
-
file
|
|
2046
|
-
]);
|
|
2047
|
-
return parseInt(result.stdout);
|
|
2048
|
-
}
|
|
2049
|
-
/**
|
|
2050
|
-
* Returns a timestamp of the files last modification
|
|
2051
|
-
*
|
|
2052
|
-
* @param path Path to the repository
|
|
2053
|
-
* @param file File to get timestamp from
|
|
2054
|
-
*/
|
|
2055
|
-
async getFileLastUpdatedTimestamp(path, file) {
|
|
2056
|
-
const result = await this.git(path, [
|
|
2057
|
-
"log",
|
|
2058
|
-
"--follow",
|
|
2059
|
-
"--format=%at",
|
|
2060
|
-
"--max-count=1",
|
|
2061
|
-
"--",
|
|
2062
|
-
file
|
|
2063
|
-
]);
|
|
2064
|
-
return parseInt(result.stdout);
|
|
2065
|
-
}
|
|
2066
|
-
/**
|
|
2067
|
-
* Returns created and updated timestamps from given file
|
|
2068
|
-
*
|
|
2069
|
-
* @param path Path to the project
|
|
2070
|
-
* @param file Path to the file
|
|
2071
|
-
*/
|
|
2072
|
-
async getFileCreatedUpdatedMeta(path, file) {
|
|
2073
|
-
const meta = await Promise.all([
|
|
2074
|
-
this.getFileCreatedTimestamp(path, file),
|
|
2075
|
-
this.getFileLastUpdatedTimestamp(path, file)
|
|
2076
|
-
]);
|
|
2077
|
-
return {
|
|
2078
|
-
created: meta[0],
|
|
2079
|
-
updated: meta[1]
|
|
2080
|
-
};
|
|
2081
|
-
}
|
|
2082
|
-
/**
|
|
2083
|
-
* Reads the currently used version of Git
|
|
2084
|
-
*
|
|
2085
|
-
* This can help debugging
|
|
2086
|
-
*/
|
|
2087
|
-
async updateVersion() {
|
|
2088
|
-
const result = await this.git("", ["--version"]);
|
|
2089
|
-
this.version = result.stdout.replace("git version", "").trim();
|
|
2370
|
+
async updateVersion() {
|
|
2371
|
+
const result = await this.git("", ["--version"]);
|
|
2372
|
+
this.version = result.stdout.replace("git version", "").trim();
|
|
2090
2373
|
}
|
|
2091
2374
|
/**
|
|
2092
2375
|
* Reads the path to the executable of Git that is used
|
|
@@ -2144,475 +2427,118 @@ var GitService2 = class {
|
|
|
2144
2427
|
await this.git(path, autoSetupRemoteArgs);
|
|
2145
2428
|
}
|
|
2146
2429
|
/**
|
|
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
|
|
2430
|
+
* Type guard for GitCommit
|
|
2431
|
+
*
|
|
2432
|
+
* @param obj The object to check
|
|
2485
2433
|
*/
|
|
2486
|
-
|
|
2487
|
-
return
|
|
2434
|
+
isGitCommit(obj) {
|
|
2435
|
+
return gitCommitSchema.safeParse(obj).success;
|
|
2488
2436
|
}
|
|
2489
2437
|
/**
|
|
2490
|
-
*
|
|
2438
|
+
* Wraps the execution of any git command
|
|
2439
|
+
* to use a FIFO queue for sequential processing
|
|
2440
|
+
*
|
|
2441
|
+
* @param path Path to the repository
|
|
2442
|
+
* @param args Arguments to append after the `git` command
|
|
2491
2443
|
*/
|
|
2492
|
-
|
|
2493
|
-
const
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2444
|
+
async git(path, args) {
|
|
2445
|
+
const result = await this.queue.add(
|
|
2446
|
+
() => GitProcess.exec(args, path, {
|
|
2447
|
+
env: {
|
|
2448
|
+
// @todo Nasty stuff - remove after update to dugite with git > v2.45.2 once available
|
|
2449
|
+
// @see https://github.com/git-lfs/git-lfs/issues/5749
|
|
2450
|
+
GIT_CLONE_PROTECTION_ACTIVE: "false"
|
|
2451
|
+
}
|
|
2452
|
+
})
|
|
2453
|
+
);
|
|
2454
|
+
if (!result) {
|
|
2455
|
+
throw new GitError(
|
|
2456
|
+
`Git ${this.version} (${this.gitPath}) command "git ${args.join(
|
|
2457
|
+
" "
|
|
2458
|
+
)}" failed to return a result`
|
|
2502
2459
|
);
|
|
2503
2460
|
}
|
|
2504
|
-
|
|
2461
|
+
if (result.exitCode !== 0) {
|
|
2462
|
+
throw new GitError(
|
|
2463
|
+
`Git ${this.version} (${this.gitPath}) command "git ${args.join(
|
|
2464
|
+
" "
|
|
2465
|
+
)}" failed with exit code "${result.exitCode}" and message "${result.stderr}"`
|
|
2466
|
+
);
|
|
2467
|
+
}
|
|
2468
|
+
return result;
|
|
2469
|
+
}
|
|
2470
|
+
};
|
|
2471
|
+
|
|
2472
|
+
// src/service/JsonFileService.ts
|
|
2473
|
+
import Fs5 from "fs-extra";
|
|
2474
|
+
var JsonFileService = class extends AbstractCrudService {
|
|
2475
|
+
cache = /* @__PURE__ */ new Map();
|
|
2476
|
+
constructor(options) {
|
|
2477
|
+
super(serviceTypeSchema.Enum.JsonFile, options);
|
|
2505
2478
|
}
|
|
2506
2479
|
/**
|
|
2507
|
-
*
|
|
2480
|
+
* Creates a new file on disk. Fails if path already exists
|
|
2481
|
+
*
|
|
2482
|
+
* @param data Data to write into the file
|
|
2483
|
+
* @param path Path to write the file to
|
|
2484
|
+
* @param schema Schema of the file to validate against
|
|
2485
|
+
* @returns Validated content of the file from disk
|
|
2508
2486
|
*/
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
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
|
-
}
|
|
2487
|
+
async create(data, path, schema) {
|
|
2488
|
+
const parsedData = schema.parse(data);
|
|
2489
|
+
const string = this.serialize(parsedData);
|
|
2490
|
+
await Fs5.writeFile(path, string, {
|
|
2491
|
+
flag: "wx",
|
|
2492
|
+
encoding: "utf8"
|
|
2526
2493
|
});
|
|
2494
|
+
this.cache.set(path, parsedData);
|
|
2495
|
+
return parsedData;
|
|
2527
2496
|
}
|
|
2528
2497
|
/**
|
|
2529
|
-
*
|
|
2498
|
+
* Reads the content of a file on disk. Fails if path does not exist
|
|
2499
|
+
*
|
|
2500
|
+
* @param path Path to read the file from
|
|
2501
|
+
* @param schema Schema of the file to validate against
|
|
2502
|
+
* @returns Validated content of the file from disk
|
|
2530
2503
|
*/
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
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
|
-
};
|
|
2504
|
+
async read(path, schema) {
|
|
2505
|
+
if (this.cache.has(path)) {
|
|
2506
|
+
return this.cache.get(path);
|
|
2589
2507
|
}
|
|
2590
|
-
|
|
2508
|
+
const data = await Fs5.readFile(path, {
|
|
2509
|
+
flag: "r",
|
|
2510
|
+
encoding: "utf8"
|
|
2511
|
+
});
|
|
2512
|
+
const json = this.deserialize(data);
|
|
2513
|
+
const parsedData = schema.parse(json);
|
|
2514
|
+
this.cache.set(path, parsedData);
|
|
2515
|
+
return parsedData;
|
|
2591
2516
|
}
|
|
2592
2517
|
/**
|
|
2593
|
-
*
|
|
2518
|
+
* Overwrites an existing file on disk
|
|
2519
|
+
*
|
|
2520
|
+
* @todo Check how to error out if the file does not exist already
|
|
2521
|
+
*
|
|
2522
|
+
* @param data Data to write into the file
|
|
2523
|
+
* @param path Path to the file to overwrite
|
|
2524
|
+
* @param schema Schema of the file to validate against
|
|
2525
|
+
* @returns Validated content of the file from disk
|
|
2594
2526
|
*/
|
|
2595
|
-
async
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
};
|
|
2611
|
-
}
|
|
2612
|
-
return value;
|
|
2613
|
-
})
|
|
2614
|
-
)
|
|
2615
|
-
};
|
|
2527
|
+
async update(data, path, schema) {
|
|
2528
|
+
const parsedData = schema.parse(data);
|
|
2529
|
+
const string = this.serialize(parsedData);
|
|
2530
|
+
await Fs5.writeFile(path, string, {
|
|
2531
|
+
flag: "w",
|
|
2532
|
+
encoding: "utf8"
|
|
2533
|
+
});
|
|
2534
|
+
this.cache.set(path, parsedData);
|
|
2535
|
+
return parsedData;
|
|
2536
|
+
}
|
|
2537
|
+
serialize(data) {
|
|
2538
|
+
return JSON.stringify(data, null, this.options.file.json.indentation);
|
|
2539
|
+
}
|
|
2540
|
+
deserialize(data) {
|
|
2541
|
+
return JSON.parse(data);
|
|
2616
2542
|
}
|
|
2617
2543
|
};
|
|
2618
2544
|
|
|
@@ -2622,47 +2548,14 @@ import Os2 from "os";
|
|
|
2622
2548
|
import Path2 from "path";
|
|
2623
2549
|
import Semver from "semver";
|
|
2624
2550
|
var ProjectService = class extends AbstractCrudService {
|
|
2551
|
+
jsonFileService;
|
|
2552
|
+
userService;
|
|
2553
|
+
gitService;
|
|
2554
|
+
assetService;
|
|
2555
|
+
collectionService;
|
|
2556
|
+
entryService;
|
|
2625
2557
|
constructor(options, jsonFileService, userService, gitService, assetService, collectionService, entryService) {
|
|
2626
2558
|
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
2559
|
this.jsonFileService = jsonFileService;
|
|
2667
2560
|
this.userService = userService;
|
|
2668
2561
|
this.gitService = gitService;
|
|
@@ -2692,7 +2585,7 @@ var ProjectService = class extends AbstractCrudService {
|
|
|
2692
2585
|
id,
|
|
2693
2586
|
description: props.description || "",
|
|
2694
2587
|
settings: Object.assign({}, defaultSettings, props.settings),
|
|
2695
|
-
created:
|
|
2588
|
+
created: datetime(),
|
|
2696
2589
|
updated: null,
|
|
2697
2590
|
coreVersion: this.options.version,
|
|
2698
2591
|
// @todo should be read from package.json to avoid duplicates
|
|
@@ -2776,8 +2669,15 @@ var ProjectService = class extends AbstractCrudService {
|
|
|
2776
2669
|
const prevProjectFile = await this.read(props);
|
|
2777
2670
|
const projectFile = {
|
|
2778
2671
|
...prevProjectFile,
|
|
2779
|
-
|
|
2780
|
-
|
|
2672
|
+
name: props.name || prevProjectFile.name,
|
|
2673
|
+
description: props.description || prevProjectFile.description,
|
|
2674
|
+
settings: {
|
|
2675
|
+
language: {
|
|
2676
|
+
supported: props.settings?.language.supported || prevProjectFile.settings.language.supported,
|
|
2677
|
+
default: props.settings?.language.default || prevProjectFile.settings.language.default
|
|
2678
|
+
}
|
|
2679
|
+
},
|
|
2680
|
+
updated: datetime()
|
|
2781
2681
|
};
|
|
2782
2682
|
await this.jsonFileService.update(projectFile, filePath, projectFileSchema);
|
|
2783
2683
|
await this.gitService.add(projectPath, [filePath]);
|
|
@@ -2822,6 +2722,7 @@ var ProjectService = class extends AbstractCrudService {
|
|
|
2822
2722
|
if (upgrade.to !== "0.0.0") {
|
|
2823
2723
|
return upgrade;
|
|
2824
2724
|
}
|
|
2725
|
+
return;
|
|
2825
2726
|
});
|
|
2826
2727
|
for (let index = 0; index < sortedUpgrades.length; index++) {
|
|
2827
2728
|
const upgrade = sortedUpgrades[index];
|
|
@@ -2852,6 +2753,45 @@ var ProjectService = class extends AbstractCrudService {
|
|
|
2852
2753
|
}
|
|
2853
2754
|
}
|
|
2854
2755
|
}
|
|
2756
|
+
branches = {
|
|
2757
|
+
list: async (props) => {
|
|
2758
|
+
listBranchesProjectSchema.parse(props);
|
|
2759
|
+
const projectPath = pathTo.project(props.id);
|
|
2760
|
+
await this.gitService.fetch(projectPath);
|
|
2761
|
+
return await this.gitService.branches.list(projectPath);
|
|
2762
|
+
},
|
|
2763
|
+
current: async (props) => {
|
|
2764
|
+
currentBranchProjectSchema.parse(props);
|
|
2765
|
+
const projectPath = pathTo.project(props.id);
|
|
2766
|
+
return await this.gitService.branches.current(projectPath);
|
|
2767
|
+
},
|
|
2768
|
+
switch: async (props) => {
|
|
2769
|
+
switchBranchProjectSchema.parse(props);
|
|
2770
|
+
const projectPath = pathTo.project(props.id);
|
|
2771
|
+
return await this.gitService.branches.switch(
|
|
2772
|
+
projectPath,
|
|
2773
|
+
props.branch,
|
|
2774
|
+
props.options
|
|
2775
|
+
);
|
|
2776
|
+
}
|
|
2777
|
+
};
|
|
2778
|
+
remotes = {
|
|
2779
|
+
getOriginUrl: async (props) => {
|
|
2780
|
+
getRemoteOriginUrlProjectSchema.parse(props);
|
|
2781
|
+
const projectPath = pathTo.project(props.id);
|
|
2782
|
+
return await this.gitService.remotes.getOriginUrl(projectPath);
|
|
2783
|
+
},
|
|
2784
|
+
setOriginUrl: async (props) => {
|
|
2785
|
+
setRemoteOriginUrlProjectSchema.parse(props);
|
|
2786
|
+
const projectPath = pathTo.project(props.id);
|
|
2787
|
+
const hasOrigin = await this.gitService.remotes.hasOrigin(projectPath);
|
|
2788
|
+
if (!hasOrigin) {
|
|
2789
|
+
await this.gitService.remotes.addOrigin(projectPath, props.url);
|
|
2790
|
+
} else {
|
|
2791
|
+
await this.gitService.remotes.setOriginUrl(projectPath, props.url);
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
};
|
|
2855
2795
|
/**
|
|
2856
2796
|
* Returns the differences of the given Projects current branch
|
|
2857
2797
|
* between the local and remote `origin` (commits ahead & behind)
|
|
@@ -3003,8 +2943,50 @@ var ProjectService = class extends AbstractCrudService {
|
|
|
3003
2943
|
}
|
|
3004
2944
|
};
|
|
3005
2945
|
|
|
2946
|
+
// src/service/UserService.ts
|
|
2947
|
+
var UserService = class {
|
|
2948
|
+
jsonFileService;
|
|
2949
|
+
constructor(jsonFileService) {
|
|
2950
|
+
this.jsonFileService = jsonFileService;
|
|
2951
|
+
}
|
|
2952
|
+
/**
|
|
2953
|
+
* Returns the User currently working with Core
|
|
2954
|
+
*/
|
|
2955
|
+
async get() {
|
|
2956
|
+
try {
|
|
2957
|
+
return await this.jsonFileService.read(pathTo.userFile, userFileSchema);
|
|
2958
|
+
} catch (error) {
|
|
2959
|
+
return void 0;
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
/**
|
|
2963
|
+
* Sets the User currently working with Core
|
|
2964
|
+
*
|
|
2965
|
+
* By doing so all git operations are done with the signature of this User
|
|
2966
|
+
*/
|
|
2967
|
+
async set(props) {
|
|
2968
|
+
setUserSchema.parse(props);
|
|
2969
|
+
const userFilePath = pathTo.userFile;
|
|
2970
|
+
const userFile = {
|
|
2971
|
+
...props
|
|
2972
|
+
};
|
|
2973
|
+
if (userFile.userType === UserTypeSchema.Enum.cloud) {
|
|
2974
|
+
}
|
|
2975
|
+
await this.jsonFileService.update(userFile, userFilePath, userFileSchema);
|
|
2976
|
+
return userFile;
|
|
2977
|
+
}
|
|
2978
|
+
};
|
|
2979
|
+
|
|
3006
2980
|
// src/index.node.ts
|
|
3007
2981
|
var ElekIoCore = class {
|
|
2982
|
+
options;
|
|
2983
|
+
userService;
|
|
2984
|
+
gitService;
|
|
2985
|
+
jsonFileService;
|
|
2986
|
+
assetService;
|
|
2987
|
+
projectService;
|
|
2988
|
+
collectionService;
|
|
2989
|
+
entryService;
|
|
3008
2990
|
// private readonly sharedValueService: SharedValueService;
|
|
3009
2991
|
constructor(props) {
|
|
3010
2992
|
const parsedProps = constructorElekIoCoreSchema.parse(props);
|
|
@@ -3020,7 +3002,7 @@ var ElekIoCore = class {
|
|
|
3020
3002
|
this.options = Object.assign({}, defaults, parsedProps);
|
|
3021
3003
|
this.jsonFileService = new JsonFileService(this.options);
|
|
3022
3004
|
this.userService = new UserService(this.jsonFileService);
|
|
3023
|
-
this.gitService = new
|
|
3005
|
+
this.gitService = new GitService(this.options, this.userService);
|
|
3024
3006
|
this.assetService = new AssetService(
|
|
3025
3007
|
this.options,
|
|
3026
3008
|
this.jsonFileService,
|
|
@@ -3142,8 +3124,8 @@ export {
|
|
|
3142
3124
|
createGitTagSchema,
|
|
3143
3125
|
createProjectSchema,
|
|
3144
3126
|
currentBranchProjectSchema,
|
|
3145
|
-
currentTimestamp,
|
|
3146
3127
|
dateValueDefinitionSchema,
|
|
3128
|
+
datetime,
|
|
3147
3129
|
datetimeValueDefinitionSchema,
|
|
3148
3130
|
ElekIoCore as default,
|
|
3149
3131
|
deleteAssetSchema,
|