@elek-io/core 0.9.1 → 0.10.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.
@@ -7,12 +7,14 @@ var __export = (target, all) => {
7
7
  // src/index.node.ts
8
8
  import Fs7 from "fs-extra";
9
9
 
10
+ // package.json
11
+ var version = "0.10.0";
12
+
10
13
  // src/schema/assetSchema.ts
11
- import z3 from "zod";
14
+ import z4 from "zod";
12
15
 
13
16
  // src/schema/baseSchema.ts
14
17
  import z from "zod";
15
- var environmentSchema = z.enum(["production", "development", "test"]);
16
18
  var supportedLanguageSchema = z.enum([
17
19
  /**
18
20
  * Bulgarian
@@ -108,6 +110,7 @@ var objectTypeSchema = z.enum([
108
110
  "value",
109
111
  "sharedValue"
110
112
  ]);
113
+ var logLevelSchema = z.enum(["error", "warn", "info", "debug"]);
111
114
  var versionSchema = z.string();
112
115
  var uuidSchema = z.string().uuid("shared.invalidUuid");
113
116
  var translatableStringSchema = z.record(
@@ -129,6 +132,10 @@ function translatableArrayOf(schema) {
129
132
  // src/schema/fileSchema.ts
130
133
  import z2 from "zod";
131
134
  var baseFileSchema = z2.object({
135
+ /**
136
+ * The object type of the file
137
+ */
138
+ objectType: objectTypeSchema.readonly(),
132
139
  /**
133
140
  * The ID of the file
134
141
  *
@@ -161,23 +168,108 @@ var fileReferenceSchema = z2.object({
161
168
  extension: supportedAssetExtensionSchema.optional()
162
169
  });
163
170
 
171
+ // src/schema/gitSchema.ts
172
+ import { z as z3 } from "zod";
173
+ var gitRepositoryPathSchema = z3.string();
174
+ var gitSignatureSchema = z3.object({
175
+ name: z3.string(),
176
+ email: z3.string()
177
+ });
178
+ var gitCommitSchema = z3.object({
179
+ /**
180
+ * SHA-1 hash of the commit
181
+ */
182
+ hash: z3.string(),
183
+ message: z3.string(),
184
+ author: gitSignatureSchema,
185
+ datetime: z3.string().datetime(),
186
+ tag: z3.string().nullable()
187
+ });
188
+ var GitCommitIconNative = /* @__PURE__ */ ((GitCommitIconNative2) => {
189
+ GitCommitIconNative2["INIT"] = ":tada:";
190
+ GitCommitIconNative2["CREATE"] = ":heavy_plus_sign:";
191
+ GitCommitIconNative2["UPDATE"] = ":wrench:";
192
+ GitCommitIconNative2["DELETE"] = ":heavy_minus_sign:";
193
+ return GitCommitIconNative2;
194
+ })(GitCommitIconNative || {});
195
+ var gitCommitIconSchema = z3.nativeEnum(GitCommitIconNative);
196
+ var gitInitOptionsSchema = z3.object({
197
+ /**
198
+ * Use the specified name for the initial branch in the newly created repository. If not specified, fall back to the default name (currently master, but this is subject to change in the future; the name can be customized via the init.defaultBranch configuration variable).
199
+ */
200
+ initialBranch: z3.string()
201
+ });
202
+ var gitCloneOptionsSchema = z3.object({
203
+ /**
204
+ * Create a shallow clone with a history truncated to the specified number of commits. Implies --single-branch unless --no-single-branch is given to fetch the histories near the tips of all branches. If you want to clone submodules shallowly, also pass --shallow-submodules.
205
+ */
206
+ depth: z3.number(),
207
+ /**
208
+ * Clone only the history leading to the tip of a single branch, either specified by the --branch option or the primary branch remote’s HEAD points at. Further fetches into the resulting repository will only update the remote-tracking branch for the branch this option was used for the initial cloning. If the HEAD at the remote did not point at any branch when --single-branch clone was made, no remote-tracking branch is created.
209
+ */
210
+ singleBranch: z3.boolean(),
211
+ /**
212
+ * Instead of pointing the newly created HEAD to the branch pointed to by the cloned repository’s HEAD, point to <name> branch instead. In a non-bare repository, this is the branch that will be checked out. --branch can also take tags and detaches the HEAD at that commit in the resulting repository.
213
+ */
214
+ branch: z3.string()
215
+ });
216
+ var gitSwitchOptionsSchema = z3.object({
217
+ /**
218
+ * If true, creates a new local branch and then switches to it
219
+ *
220
+ * @see https://git-scm.com/docs/git-switch#Documentation/git-switch.txt---createltnew-branchgt
221
+ */
222
+ isNew: z3.boolean().optional()
223
+ });
224
+ var gitLogOptionsSchema = z3.object({
225
+ /**
226
+ * Limit the result to given number of commits
227
+ */
228
+ limit: z3.number().optional(),
229
+ /**
230
+ * Only list commits that are between given SHAs or tag names
231
+ *
232
+ * Note that the commits of from and to are not included in the result
233
+ */
234
+ between: z3.object({
235
+ /**
236
+ * From the oldest commit
237
+ */
238
+ from: z3.string(),
239
+ /**
240
+ * To the newest commit
241
+ *
242
+ * Defaults to the current HEAD
243
+ */
244
+ to: z3.string().optional()
245
+ }),
246
+ /**
247
+ * Only shows commits of given file
248
+ */
249
+ filePath: z3.string().optional()
250
+ });
251
+
164
252
  // src/schema/assetSchema.ts
165
253
  var assetFileSchema = baseFileSchema.extend({
166
- objectType: z3.literal(objectTypeSchema.Enum.asset).readonly(),
167
- name: z3.string(),
168
- description: z3.string(),
254
+ objectType: z4.literal(objectTypeSchema.Enum.asset).readonly(),
255
+ name: z4.string(),
256
+ description: z4.string(),
169
257
  extension: supportedAssetExtensionSchema.readonly(),
170
258
  mimeType: supportedAssetMimeTypeSchema.readonly(),
171
259
  /**
172
260
  * Total size in bytes
173
261
  */
174
- size: z3.number().readonly()
262
+ size: z4.number().readonly()
175
263
  });
176
264
  var assetSchema = assetFileSchema.extend({
177
265
  /**
178
266
  * Absolute path on this filesystem
179
267
  */
180
- absolutePath: z3.string().readonly()
268
+ absolutePath: z4.string().readonly(),
269
+ /**
270
+ * Commit history of this Asset
271
+ */
272
+ history: z4.array(gitCommitSchema)
181
273
  });
182
274
  var assetExportSchema = assetSchema.extend({});
183
275
  var createAssetSchema = assetFileSchema.pick({
@@ -188,12 +280,20 @@ var createAssetSchema = assetFileSchema.pick({
188
280
  /**
189
281
  * Path of the file to add as a new Asset
190
282
  */
191
- filePath: z3.string().readonly()
283
+ filePath: z4.string().readonly()
192
284
  });
193
285
  var readAssetSchema = assetFileSchema.pick({
194
286
  id: true
195
287
  }).extend({
196
- projectId: uuidSchema.readonly()
288
+ projectId: uuidSchema.readonly(),
289
+ commitHash: z4.string().optional().readonly()
290
+ });
291
+ var saveAssetSchema = assetFileSchema.pick({
292
+ id: true
293
+ }).extend({
294
+ projectId: uuidSchema.readonly(),
295
+ filePath: z4.string().readonly(),
296
+ commitHash: z4.string().optional().readonly()
197
297
  });
198
298
  var updateAssetSchema = assetFileSchema.pick({
199
299
  id: true,
@@ -204,7 +304,7 @@ var updateAssetSchema = assetFileSchema.pick({
204
304
  /**
205
305
  * Path of the new file to update the Asset with
206
306
  */
207
- newFilePath: z3.string().readonly().optional()
307
+ newFilePath: z4.string().readonly().optional()
208
308
  });
209
309
  var deleteAssetSchema = assetFileSchema.pick({
210
310
  id: true,
@@ -212,92 +312,96 @@ var deleteAssetSchema = assetFileSchema.pick({
212
312
  }).extend({
213
313
  projectId: uuidSchema.readonly()
214
314
  });
215
- var countAssetsSchema = z3.object({ projectId: uuidSchema.readonly() });
315
+ var countAssetsSchema = z4.object({ projectId: uuidSchema.readonly() });
216
316
 
217
317
  // src/schema/collectionSchema.ts
218
- import z7 from "zod";
318
+ import z8 from "zod";
219
319
 
220
320
  // src/schema/entrySchema.ts
221
- import z5 from "zod";
321
+ import z6 from "zod";
222
322
 
223
323
  // src/schema/valueSchema.ts
224
- import z4 from "zod";
225
- var ValueTypeSchema = z4.enum([
324
+ import z5 from "zod";
325
+ var ValueTypeSchema = z5.enum([
226
326
  "string",
227
327
  "number",
228
328
  "boolean",
229
329
  "reference"
230
330
  ]);
231
- var valueContentReferenceBase = z4.object({
331
+ var valueContentReferenceBase = z5.object({
232
332
  id: uuidSchema
233
333
  });
234
334
  var valueContentReferenceWithLanguageBase = valueContentReferenceBase.extend({
235
335
  language: supportedLanguageSchema
236
336
  });
237
337
  var valueContentReferenceToAssetSchema = valueContentReferenceBase.extend({
238
- objectType: z4.literal(objectTypeSchema.Enum.asset)
338
+ objectType: z5.literal(objectTypeSchema.Enum.asset)
239
339
  });
240
340
  var valueContentReferenceToCollectionSchema = valueContentReferenceBase.extend({
241
- objectType: z4.literal(objectTypeSchema.Enum.collection)
341
+ objectType: z5.literal(objectTypeSchema.Enum.collection)
242
342
  });
243
343
  var valueContentReferenceToEntrySchema = valueContentReferenceBase.extend({
244
- objectType: z4.literal(objectTypeSchema.Enum.entry)
344
+ objectType: z5.literal(objectTypeSchema.Enum.entry)
245
345
  });
246
- var valueContentReferenceSchema = z4.union([
346
+ var valueContentReferenceSchema = z5.union([
247
347
  valueContentReferenceToAssetSchema,
248
348
  valueContentReferenceToCollectionSchema,
249
349
  valueContentReferenceToEntrySchema
250
350
  // valueContentReferenceToSharedValueSchema,
251
351
  ]);
252
- var resolvedValueContentReferenceSchema = z4.union([
352
+ var resolvedValueContentReferenceSchema = z5.union([
253
353
  assetSchema,
254
- z4.lazy(() => entrySchema)
354
+ z5.lazy(() => entrySchema)
255
355
  // Circular dependency / recursive type @see https://github.com/colinhacks/zod?tab=readme-ov-file#recursive-types
256
356
  // resolvedValueContentReferenceToSharedValueSchema,
257
357
  ]);
258
- var directValueBaseSchema = z4.object({
259
- objectType: z4.literal(objectTypeSchema.Enum.value).readonly(),
358
+ var directValueBaseSchema = z5.object({
359
+ objectType: z5.literal(objectTypeSchema.Enum.value).readonly(),
260
360
  fieldDefinitionId: uuidSchema.readonly()
261
361
  });
262
362
  var directStringValueSchema = directValueBaseSchema.extend({
263
- valueType: z4.literal(ValueTypeSchema.Enum.string).readonly(),
363
+ valueType: z5.literal(ValueTypeSchema.Enum.string).readonly(),
264
364
  content: translatableStringSchema
265
365
  });
266
366
  var directNumberValueSchema = directValueBaseSchema.extend({
267
- valueType: z4.literal(ValueTypeSchema.Enum.number).readonly(),
367
+ valueType: z5.literal(ValueTypeSchema.Enum.number).readonly(),
268
368
  content: translatableNumberSchema
269
369
  });
270
370
  var directBooleanValueSchema = directValueBaseSchema.extend({
271
- valueType: z4.literal(ValueTypeSchema.Enum.boolean).readonly(),
371
+ valueType: z5.literal(ValueTypeSchema.Enum.boolean).readonly(),
272
372
  content: translatableBooleanSchema
273
373
  });
274
- var directValueSchema = z4.union([
374
+ var directValueSchema = z5.union([
275
375
  directStringValueSchema,
276
376
  directNumberValueSchema,
277
377
  directBooleanValueSchema
278
378
  ]);
279
- var referencedValueSchema = z4.object({
280
- objectType: z4.literal(objectTypeSchema.Enum.value).readonly(),
379
+ var referencedValueSchema = z5.object({
380
+ objectType: z5.literal(objectTypeSchema.Enum.value).readonly(),
281
381
  fieldDefinitionId: uuidSchema.readonly(),
282
- valueType: z4.literal(ValueTypeSchema.Enum.reference).readonly(),
382
+ valueType: z5.literal(ValueTypeSchema.Enum.reference).readonly(),
283
383
  content: translatableArrayOf(valueContentReferenceSchema)
284
384
  });
285
- var valueSchema = z4.union([directValueSchema, referencedValueSchema]);
385
+ var valueSchema = z5.union([directValueSchema, referencedValueSchema]);
286
386
  var resolvedReferencedValueSchema = referencedValueSchema.extend({
287
387
  content: translatableArrayOf(resolvedValueContentReferenceSchema)
288
388
  });
289
- var resolvedValueSchema = z4.union([
389
+ var resolvedValueSchema = z5.union([
290
390
  directValueSchema,
291
391
  resolvedReferencedValueSchema
292
392
  ]);
293
393
 
294
394
  // src/schema/entrySchema.ts
295
395
  var entryFileSchema = baseFileSchema.extend({
296
- objectType: z5.literal(objectTypeSchema.Enum.entry).readonly(),
297
- values: z5.array(valueSchema)
396
+ objectType: z6.literal(objectTypeSchema.Enum.entry).readonly(),
397
+ values: z6.array(valueSchema)
298
398
  });
299
399
  var entrySchema = entryFileSchema.extend({
300
- values: z5.array(z5.lazy(() => resolvedValueSchema))
400
+ values: z6.array(z6.lazy(() => resolvedValueSchema)),
401
+ /**
402
+ * Commit history of this Entry
403
+ */
404
+ history: z6.array(gitCommitSchema)
301
405
  });
302
406
  var entryExportSchema = entrySchema.extend({});
303
407
  var createEntrySchema = entryFileSchema.omit({
@@ -308,14 +412,15 @@ var createEntrySchema = entryFileSchema.omit({
308
412
  }).extend({
309
413
  projectId: uuidSchema.readonly(),
310
414
  collectionId: uuidSchema.readonly(),
311
- values: z5.array(valueSchema)
415
+ values: z6.array(valueSchema)
312
416
  });
313
- var readEntrySchema = z5.object({
417
+ var readEntrySchema = z6.object({
314
418
  id: uuidSchema.readonly(),
315
419
  projectId: uuidSchema.readonly(),
316
- collectionId: uuidSchema.readonly()
420
+ collectionId: uuidSchema.readonly(),
421
+ commitHash: z6.string().optional().readonly()
317
422
  });
318
- var updateEntrySchema = entrySchema.omit({
423
+ var updateEntrySchema = entryFileSchema.omit({
319
424
  objectType: true,
320
425
  created: true,
321
426
  updated: true
@@ -324,14 +429,14 @@ var updateEntrySchema = entrySchema.omit({
324
429
  collectionId: uuidSchema.readonly()
325
430
  });
326
431
  var deleteEntrySchema = readEntrySchema.extend({});
327
- var countEntriesSchema = z5.object({
432
+ var countEntriesSchema = z6.object({
328
433
  projectId: uuidSchema.readonly(),
329
434
  collectionId: uuidSchema.readonly()
330
435
  });
331
436
 
332
437
  // src/schema/fieldSchema.ts
333
- import { z as z6 } from "zod";
334
- var FieldTypeSchema = z6.enum([
438
+ import { z as z7 } from "zod";
439
+ var FieldTypeSchema = z7.enum([
335
440
  // String Values
336
441
  "text",
337
442
  "textarea",
@@ -353,67 +458,67 @@ var FieldTypeSchema = z6.enum([
353
458
  "entry"
354
459
  // 'sharedValue', // @todo
355
460
  ]);
356
- var FieldWidthSchema = z6.enum(["12", "6", "4", "3"]);
357
- var FieldDefinitionBaseSchema = z6.object({
461
+ var FieldWidthSchema = z7.enum(["12", "6", "4", "3"]);
462
+ var FieldDefinitionBaseSchema = z7.object({
358
463
  id: uuidSchema.readonly(),
359
464
  label: translatableStringSchema,
360
465
  description: translatableStringSchema,
361
- isRequired: z6.boolean(),
362
- isDisabled: z6.boolean(),
363
- isUnique: z6.boolean(),
466
+ isRequired: z7.boolean(),
467
+ isDisabled: z7.boolean(),
468
+ isUnique: z7.boolean(),
364
469
  inputWidth: FieldWidthSchema
365
470
  });
366
471
  var StringFieldDefinitionBaseSchema = FieldDefinitionBaseSchema.extend(
367
472
  {
368
- valueType: z6.literal(ValueTypeSchema.Enum.string),
369
- defaultValue: z6.string().nullable()
473
+ valueType: z7.literal(ValueTypeSchema.Enum.string),
474
+ defaultValue: z7.string().nullable()
370
475
  }
371
476
  );
372
477
  var textFieldDefinitionSchema = StringFieldDefinitionBaseSchema.extend(
373
478
  {
374
- fieldType: z6.literal(FieldTypeSchema.Enum.text),
375
- min: z6.number().nullable(),
376
- max: z6.number().nullable()
479
+ fieldType: z7.literal(FieldTypeSchema.Enum.text),
480
+ min: z7.number().nullable(),
481
+ max: z7.number().nullable()
377
482
  }
378
483
  );
379
484
  var textareaFieldDefinitionSchema = StringFieldDefinitionBaseSchema.extend({
380
- fieldType: z6.literal(FieldTypeSchema.Enum.textarea),
381
- min: z6.number().nullable(),
382
- max: z6.number().nullable()
485
+ fieldType: z7.literal(FieldTypeSchema.Enum.textarea),
486
+ min: z7.number().nullable(),
487
+ max: z7.number().nullable()
383
488
  });
384
489
  var emailFieldDefinitionSchema = StringFieldDefinitionBaseSchema.extend({
385
- fieldType: z6.literal(FieldTypeSchema.Enum.email),
386
- defaultValue: z6.string().email().nullable()
490
+ fieldType: z7.literal(FieldTypeSchema.Enum.email),
491
+ defaultValue: z7.string().email().nullable()
387
492
  });
388
493
  var urlFieldDefinitionSchema = StringFieldDefinitionBaseSchema.extend({
389
- fieldType: z6.literal(FieldTypeSchema.Enum.url),
390
- defaultValue: z6.string().url().nullable()
494
+ fieldType: z7.literal(FieldTypeSchema.Enum.url),
495
+ defaultValue: z7.string().url().nullable()
391
496
  });
392
497
  var ipFieldDefinitionSchema = StringFieldDefinitionBaseSchema.extend({
393
- fieldType: z6.literal(FieldTypeSchema.Enum.ip),
394
- defaultValue: z6.string().ip().nullable()
498
+ fieldType: z7.literal(FieldTypeSchema.Enum.ip),
499
+ defaultValue: z7.string().ip().nullable()
395
500
  });
396
501
  var dateFieldDefinitionSchema = StringFieldDefinitionBaseSchema.extend(
397
502
  {
398
- fieldType: z6.literal(FieldTypeSchema.Enum.date),
399
- defaultValue: z6.string().date().nullable()
503
+ fieldType: z7.literal(FieldTypeSchema.Enum.date),
504
+ defaultValue: z7.string().date().nullable()
400
505
  }
401
506
  );
402
507
  var timeFieldDefinitionSchema = StringFieldDefinitionBaseSchema.extend(
403
508
  {
404
- fieldType: z6.literal(FieldTypeSchema.Enum.time),
405
- defaultValue: z6.string().time().nullable()
509
+ fieldType: z7.literal(FieldTypeSchema.Enum.time),
510
+ defaultValue: z7.string().time().nullable()
406
511
  }
407
512
  );
408
513
  var datetimeFieldDefinitionSchema = StringFieldDefinitionBaseSchema.extend({
409
- fieldType: z6.literal(FieldTypeSchema.Enum.datetime),
410
- defaultValue: z6.string().datetime().nullable()
514
+ fieldType: z7.literal(FieldTypeSchema.Enum.datetime),
515
+ defaultValue: z7.string().datetime().nullable()
411
516
  });
412
517
  var telephoneFieldDefinitionSchema = StringFieldDefinitionBaseSchema.extend({
413
- fieldType: z6.literal(FieldTypeSchema.Enum.telephone)
518
+ fieldType: z7.literal(FieldTypeSchema.Enum.telephone)
414
519
  // defaultValue: z.string().e164(), @todo when zod v4 releases @see https://github.com/colinhacks/zod/pull/3476
415
520
  });
416
- var stringFieldDefinitionSchema = z6.union([
521
+ var stringFieldDefinitionSchema = z7.union([
417
522
  textFieldDefinitionSchema,
418
523
  textareaFieldDefinitionSchema,
419
524
  emailFieldDefinitionSchema,
@@ -426,50 +531,50 @@ var stringFieldDefinitionSchema = z6.union([
426
531
  ]);
427
532
  var NumberFieldDefinitionBaseSchema = FieldDefinitionBaseSchema.extend(
428
533
  {
429
- valueType: z6.literal(ValueTypeSchema.Enum.number),
430
- min: z6.number().nullable(),
431
- max: z6.number().nullable(),
432
- isUnique: z6.literal(false),
433
- defaultValue: z6.number().nullable()
534
+ valueType: z7.literal(ValueTypeSchema.Enum.number),
535
+ min: z7.number().nullable(),
536
+ max: z7.number().nullable(),
537
+ isUnique: z7.literal(false),
538
+ defaultValue: z7.number().nullable()
434
539
  }
435
540
  );
436
541
  var numberFieldDefinitionSchema = NumberFieldDefinitionBaseSchema.extend({
437
- fieldType: z6.literal(FieldTypeSchema.Enum.number)
542
+ fieldType: z7.literal(FieldTypeSchema.Enum.number)
438
543
  });
439
544
  var rangeFieldDefinitionSchema = NumberFieldDefinitionBaseSchema.extend({
440
- fieldType: z6.literal(FieldTypeSchema.Enum.range),
545
+ fieldType: z7.literal(FieldTypeSchema.Enum.range),
441
546
  // Overwrite from nullable to required because a range needs min, max and default to work and is required, since it always returns a number
442
- isRequired: z6.literal(true),
443
- min: z6.number(),
444
- max: z6.number(),
445
- defaultValue: z6.number()
547
+ isRequired: z7.literal(true),
548
+ min: z7.number(),
549
+ max: z7.number(),
550
+ defaultValue: z7.number()
446
551
  });
447
552
  var BooleanFieldDefinitionBaseSchema = FieldDefinitionBaseSchema.extend({
448
- valueType: z6.literal(ValueTypeSchema.Enum.boolean),
553
+ valueType: z7.literal(ValueTypeSchema.Enum.boolean),
449
554
  // Overwrite from nullable to required because a boolean needs a default to work and is required, since it always is either true or false
450
- isRequired: z6.literal(true),
451
- defaultValue: z6.boolean(),
452
- isUnique: z6.literal(false)
555
+ isRequired: z7.literal(true),
556
+ defaultValue: z7.boolean(),
557
+ isUnique: z7.literal(false)
453
558
  });
454
559
  var toggleFieldDefinitionSchema = BooleanFieldDefinitionBaseSchema.extend({
455
- fieldType: z6.literal(FieldTypeSchema.Enum.toggle)
560
+ fieldType: z7.literal(FieldTypeSchema.Enum.toggle)
456
561
  });
457
562
  var ReferenceFieldDefinitionBaseSchema = FieldDefinitionBaseSchema.extend({
458
- valueType: z6.literal(ValueTypeSchema.Enum.reference)
563
+ valueType: z7.literal(ValueTypeSchema.Enum.reference)
459
564
  });
460
565
  var assetFieldDefinitionSchema = ReferenceFieldDefinitionBaseSchema.extend({
461
- fieldType: z6.literal(FieldTypeSchema.Enum.asset),
462
- allowedMimeTypes: z6.array(supportedAssetMimeTypeSchema).min(1),
463
- min: z6.number().nullable(),
464
- max: z6.number().nullable()
566
+ fieldType: z7.literal(FieldTypeSchema.Enum.asset),
567
+ allowedMimeTypes: z7.array(supportedAssetMimeTypeSchema).min(1),
568
+ min: z7.number().nullable(),
569
+ max: z7.number().nullable()
465
570
  });
466
571
  var entryFieldDefinitionSchema = ReferenceFieldDefinitionBaseSchema.extend({
467
- fieldType: z6.literal(FieldTypeSchema.Enum.entry),
468
- ofCollections: z6.array(uuidSchema),
469
- min: z6.number().nullable(),
470
- max: z6.number().nullable()
572
+ fieldType: z7.literal(FieldTypeSchema.Enum.entry),
573
+ ofCollections: z7.array(uuidSchema),
574
+ min: z7.number().nullable(),
575
+ max: z7.number().nullable()
471
576
  });
472
- var fieldDefinitionSchema = z6.union([
577
+ var fieldDefinitionSchema = z7.union([
473
578
  stringFieldDefinitionSchema,
474
579
  numberFieldDefinitionSchema,
475
580
  rangeFieldDefinitionSchema,
@@ -496,10 +601,10 @@ function getValueContentSchemaFromFieldDefinition(fieldDefinition) {
496
601
  }
497
602
  }
498
603
  function getBooleanValueContentSchema() {
499
- return z6.boolean();
604
+ return z7.boolean();
500
605
  }
501
606
  function getNumberValueContentSchema(definition) {
502
- let schema = z6.number();
607
+ let schema = z7.number();
503
608
  if (definition.min) {
504
609
  schema = schema.min(definition.min);
505
610
  }
@@ -512,7 +617,7 @@ function getNumberValueContentSchema(definition) {
512
617
  return schema;
513
618
  }
514
619
  function getStringValueContentSchema(definition) {
515
- let schema = z6.string().trim();
620
+ let schema = z7.string().trim();
516
621
  if ("min" in definition && definition.min) {
517
622
  schema = schema.min(definition.min);
518
623
  }
@@ -551,12 +656,12 @@ function getReferenceValueContentSchema(definition) {
551
656
  switch (definition.fieldType) {
552
657
  case FieldTypeSchema.Enum.asset:
553
658
  {
554
- schema = z6.array(valueContentReferenceToAssetSchema);
659
+ schema = z7.array(valueContentReferenceToAssetSchema);
555
660
  }
556
661
  break;
557
662
  case FieldTypeSchema.Enum.entry:
558
663
  {
559
- schema = z6.array(valueContentReferenceToEntrySchema);
664
+ schema = z7.array(valueContentReferenceToEntrySchema);
560
665
  }
561
666
  break;
562
667
  }
@@ -574,24 +679,29 @@ function getReferenceValueContentSchema(definition) {
574
679
 
575
680
  // src/schema/collectionSchema.ts
576
681
  var collectionFileSchema = baseFileSchema.extend({
577
- objectType: z7.literal(objectTypeSchema.Enum.collection).readonly(),
578
- name: z7.object({
682
+ objectType: z8.literal(objectTypeSchema.Enum.collection).readonly(),
683
+ name: z8.object({
579
684
  singular: translatableStringSchema,
580
685
  plural: translatableStringSchema
581
686
  }),
582
- slug: z7.object({
583
- singular: z7.string(),
584
- plural: z7.string()
687
+ slug: z8.object({
688
+ singular: z8.string(),
689
+ plural: z8.string()
585
690
  }),
586
691
  description: translatableStringSchema,
587
692
  icon: supportedIconSchema,
588
- fieldDefinitions: z7.array(fieldDefinitionSchema)
693
+ fieldDefinitions: z8.array(fieldDefinitionSchema)
694
+ });
695
+ var collectionSchema = collectionFileSchema.extend({
696
+ /**
697
+ * Commit history of this Collection
698
+ */
699
+ history: z8.array(gitCommitSchema)
589
700
  });
590
- var collectionSchema = collectionFileSchema.extend({});
591
701
  var collectionExportSchema = collectionSchema.extend({
592
- entries: z7.array(entryExportSchema)
702
+ entries: z8.array(entryExportSchema)
593
703
  });
594
- var createCollectionSchema = collectionSchema.omit({
704
+ var createCollectionSchema = collectionFileSchema.omit({
595
705
  id: true,
596
706
  objectType: true,
597
707
  created: true,
@@ -599,9 +709,10 @@ var createCollectionSchema = collectionSchema.omit({
599
709
  }).extend({
600
710
  projectId: uuidSchema.readonly()
601
711
  });
602
- var readCollectionSchema = z7.object({
712
+ var readCollectionSchema = z8.object({
603
713
  id: uuidSchema.readonly(),
604
- projectId: uuidSchema.readonly()
714
+ projectId: uuidSchema.readonly(),
715
+ commitHash: z8.string().optional().readonly()
605
716
  });
606
717
  var updateCollectionSchema = collectionFileSchema.pick({
607
718
  id: true,
@@ -614,115 +725,34 @@ var updateCollectionSchema = collectionFileSchema.pick({
614
725
  projectId: uuidSchema.readonly()
615
726
  });
616
727
  var deleteCollectionSchema = readCollectionSchema.extend({});
617
- var countCollectionsSchema = z7.object({
728
+ var countCollectionsSchema = z8.object({
618
729
  projectId: uuidSchema.readonly()
619
730
  });
620
731
 
621
732
  // src/schema/coreSchema.ts
622
- import { z as z8 } from "zod";
623
- var elekIoCoreOptionsSchema = z8.object({
624
- /**
625
- * The environment elek.io Core is currently running in
626
- */
627
- environment: environmentSchema,
628
- /**
629
- * The current version of elek.io Core
630
- */
631
- version: versionSchema,
632
- file: z8.object({
633
- json: z8.object({
634
- /**
635
- * If set, adds indentation with spaces (number) or escape character (string)
636
- * and line break characters to saved JSON files on disk, to make them easier to read.
637
- * Defaults to 2 spaces of indentation.
638
- */
639
- indentation: z8.union([z8.number(), z8.string()])
640
- })
641
- })
642
- });
643
- var constructorElekIoCoreSchema = elekIoCoreOptionsSchema.omit({
644
- version: true
645
- }).partial({
646
- environment: true,
647
- file: true
648
- }).optional();
649
-
650
- // src/schema/gitSchema.ts
651
733
  import { z as z9 } from "zod";
652
- var gitRepositoryPathSchema = z9.string();
653
- var gitSignatureSchema = z9.object({
654
- name: z9.string(),
655
- email: z9.string()
656
- });
657
- var gitCommitSchema = z9.object({
658
- /**
659
- * SHA-1 hash of the commit
660
- */
661
- hash: z9.string(),
662
- message: z9.string(),
663
- author: gitSignatureSchema,
664
- datetime: z9.string().datetime(),
665
- tag: z9.string().nullable()
666
- });
667
- var GitCommitIconNative = /* @__PURE__ */ ((GitCommitIconNative2) => {
668
- GitCommitIconNative2["INIT"] = ":tada:";
669
- GitCommitIconNative2["CREATE"] = ":heavy_plus_sign:";
670
- GitCommitIconNative2["UPDATE"] = ":wrench:";
671
- GitCommitIconNative2["DELETE"] = ":fire:";
672
- return GitCommitIconNative2;
673
- })(GitCommitIconNative || {});
674
- var gitCommitIconSchema = z9.nativeEnum(GitCommitIconNative);
675
- var gitInitOptionsSchema = z9.object({
676
- /**
677
- * Use the specified name for the initial branch in the newly created repository. If not specified, fall back to the default name (currently master, but this is subject to change in the future; the name can be customized via the init.defaultBranch configuration variable).
678
- */
679
- initialBranch: z9.string()
680
- });
681
- var gitCloneOptionsSchema = z9.object({
682
- /**
683
- * Create a shallow clone with a history truncated to the specified number of commits. Implies --single-branch unless --no-single-branch is given to fetch the histories near the tips of all branches. If you want to clone submodules shallowly, also pass --shallow-submodules.
684
- */
685
- depth: z9.number(),
686
- /**
687
- * Clone only the history leading to the tip of a single branch, either specified by the --branch option or the primary branch remote’s HEAD points at. Further fetches into the resulting repository will only update the remote-tracking branch for the branch this option was used for the initial cloning. If the HEAD at the remote did not point at any branch when --single-branch clone was made, no remote-tracking branch is created.
688
- */
689
- singleBranch: z9.boolean(),
690
- /**
691
- * Instead of pointing the newly created HEAD to the branch pointed to by the cloned repository’s HEAD, point to <name> branch instead. In a non-bare repository, this is the branch that will be checked out. --branch can also take tags and detaches the HEAD at that commit in the resulting repository.
692
- */
693
- branch: z9.string()
694
- });
695
- var gitSwitchOptionsSchema = z9.object({
696
- /**
697
- * If true, creates a new local branch and then switches to it
698
- *
699
- * @see https://git-scm.com/docs/git-switch#Documentation/git-switch.txt---createltnew-branchgt
700
- */
701
- isNew: z9.boolean().optional()
702
- });
703
- var gitLogOptionsSchema = z9.object({
704
- /**
705
- * Limit the result to given number of commits
706
- */
707
- limit: z9.number().optional(),
708
- /**
709
- * Only list commits that are between given SHAs or tag names
710
- *
711
- * Note that the commits of from and to are not included in the result
712
- */
713
- between: z9.object({
734
+ var elekIoCoreOptionsSchema = z9.object({
735
+ log: z9.object({
714
736
  /**
715
- * From the oldest commit
737
+ * The lowest level that should be logged
738
+ *
739
+ * @default 'info'
716
740
  */
717
- from: z9.string(),
741
+ level: logLevelSchema
742
+ }),
743
+ file: z9.object({
718
744
  /**
719
- * To the newest commit
745
+ * If set to true, caches files in memory to speed up access
720
746
  *
721
- * Defaults to the current HEAD
747
+ * @default true
722
748
  */
723
- to: z9.string().optional()
749
+ cache: z9.boolean()
724
750
  })
725
751
  });
752
+ var constructorElekIoCoreSchema = elekIoCoreOptionsSchema.partial({
753
+ log: true,
754
+ file: true
755
+ }).optional();
726
756
 
727
757
  // src/schema/gitTagSchema.ts
728
758
  import { z as z10 } from "zod";
@@ -774,7 +804,22 @@ var projectFileSchema = baseFileSchema.extend({
774
804
  status: projectStatusSchema,
775
805
  settings: projectSettingsSchema
776
806
  });
777
- var projectSchema = projectFileSchema.extend({});
807
+ var projectSchema = projectFileSchema.extend({
808
+ /**
809
+ * Commit history of this Project
810
+ */
811
+ history: z11.array(gitCommitSchema),
812
+ /**
813
+ * Full commit history of this Project
814
+ * including all Assets, Collections, Entries and other files
815
+ */
816
+ fullHistory: z11.array(gitCommitSchema)
817
+ });
818
+ var outdatedProjectSchema = projectFileSchema.pick({
819
+ id: true,
820
+ name: true,
821
+ coreVersion: true
822
+ });
778
823
  var projectExportSchema = projectSchema.extend({
779
824
  assets: z11.array(assetExportSchema),
780
825
  collections: z11.array(collectionExportSchema)
@@ -788,7 +833,8 @@ var createProjectSchema = projectSchema.pick({
788
833
  settings: true
789
834
  });
790
835
  var readProjectSchema = z11.object({
791
- id: uuidSchema.readonly()
836
+ id: uuidSchema.readonly(),
837
+ commitHash: z11.string().optional().readonly()
792
838
  });
793
839
  var updateProjectSchema = projectSchema.pick({
794
840
  id: true,
@@ -801,7 +847,11 @@ var updateProjectSchema = projectSchema.pick({
801
847
  settings: true
802
848
  });
803
849
  var upgradeProjectSchema = z11.object({
804
- id: uuidSchema.readonly()
850
+ id: uuidSchema.readonly(),
851
+ /**
852
+ * Force the upgrade even if the Project is up-to-date
853
+ */
854
+ force: z11.boolean().optional()
805
855
  });
806
856
  var deleteProjectSchema = readProjectSchema.extend({});
807
857
  var projectUpgradeSchema = z11.object({
@@ -958,7 +1008,7 @@ var workingDirectory = Path.join(Os.homedir(), "elek.io");
958
1008
  var pathTo = {
959
1009
  tmp: Path.join(workingDirectory, "tmp"),
960
1010
  userFile: Path.join(workingDirectory, "user.json"),
961
- // logs: Path.join(workingDirectory, 'logs'),
1011
+ logs: Path.join(workingDirectory, "logs"),
962
1012
  projects: Path.join(workingDirectory, "projects"),
963
1013
  project: (projectId) => {
964
1014
  return Path.join(pathTo.projects, projectId);
@@ -1010,6 +1060,9 @@ var pathTo = {
1010
1060
  },
1011
1061
  asset: (projectId, id, extension) => {
1012
1062
  return Path.join(pathTo.lfs(projectId), `${id}.${extension}`);
1063
+ },
1064
+ tmpAsset: (id, extension) => {
1065
+ return Path.join(pathTo.tmp, `${id}.${extension}`);
1013
1066
  }
1014
1067
  };
1015
1068
  function assignDefaultIfMissing(value, defaultsTo) {
@@ -1216,10 +1269,13 @@ function slug(string) {
1216
1269
 
1217
1270
  // src/service/AssetService.ts
1218
1271
  var AssetService = class extends AbstractCrudService {
1272
+ // @ts-ignore
1273
+ logService;
1219
1274
  jsonFileService;
1220
1275
  gitService;
1221
- constructor(options, jsonFileService, gitService) {
1276
+ constructor(options, logService, jsonFileService, gitService) {
1222
1277
  super(serviceTypeSchema.Enum.Asset, options);
1278
+ this.logService = logService;
1223
1279
  this.jsonFileService = jsonFileService;
1224
1280
  this.gitService = gitService;
1225
1281
  }
@@ -1236,6 +1292,7 @@ var AssetService = class extends AbstractCrudService {
1236
1292
  const assetFilePath = pathTo.assetFile(props.projectId, id);
1237
1293
  const assetFile = {
1238
1294
  ...props,
1295
+ name: slug(props.name),
1239
1296
  objectType: "asset",
1240
1297
  id,
1241
1298
  created: datetime(),
@@ -1261,14 +1318,48 @@ var AssetService = class extends AbstractCrudService {
1261
1318
  }
1262
1319
  /**
1263
1320
  * Returns an Asset by ID
1321
+ *
1322
+ * If a commit hash is provided, the Asset is read from history
1264
1323
  */
1265
1324
  async read(props) {
1266
1325
  readAssetSchema.parse(props);
1267
- const assetFile = await this.jsonFileService.read(
1268
- pathTo.assetFile(props.projectId, props.id),
1269
- assetFileSchema
1270
- );
1271
- return this.toAsset(props.projectId, assetFile);
1326
+ if (!props.commitHash) {
1327
+ const assetFile = await this.jsonFileService.read(
1328
+ pathTo.assetFile(props.projectId, props.id),
1329
+ assetFileSchema
1330
+ );
1331
+ return this.toAsset(props.projectId, assetFile);
1332
+ } else {
1333
+ const assetFile = this.migrate(
1334
+ JSON.parse(
1335
+ await this.gitService.getFileContentAtCommit(
1336
+ pathTo.project(props.projectId),
1337
+ pathTo.assetFile(props.projectId, props.id),
1338
+ props.commitHash
1339
+ )
1340
+ )
1341
+ );
1342
+ const assetBlob = await this.gitService.getFileContentAtCommit(
1343
+ pathTo.project(props.projectId),
1344
+ pathTo.asset(props.projectId, props.id, assetFile.extension),
1345
+ props.commitHash,
1346
+ "binary"
1347
+ );
1348
+ await Fs2.writeFile(
1349
+ pathTo.tmpAsset(assetFile.id, assetFile.extension),
1350
+ assetBlob,
1351
+ "binary"
1352
+ );
1353
+ return this.toAsset(props.projectId, assetFile, true);
1354
+ }
1355
+ }
1356
+ /**
1357
+ * Copies an Asset to given file path on disk
1358
+ */
1359
+ async save(props) {
1360
+ saveAssetSchema.parse(props);
1361
+ const asset = await this.read(props);
1362
+ await Fs2.copyFile(asset.absolutePath, props.filePath);
1272
1363
  }
1273
1364
  /**
1274
1365
  * Updates given Asset
@@ -1283,6 +1374,7 @@ var AssetService = class extends AbstractCrudService {
1283
1374
  const assetFile = {
1284
1375
  ...prevAssetFile,
1285
1376
  ...props,
1377
+ name: slug(props.name),
1286
1378
  updated: datetime()
1287
1379
  };
1288
1380
  if (props.newFilePath) {
@@ -1302,6 +1394,7 @@ var AssetService = class extends AbstractCrudService {
1302
1394
  );
1303
1395
  await Fs2.remove(prevAssetPath);
1304
1396
  await Fs2.copyFile(props.newFilePath, assetPath);
1397
+ await this.gitService.add(projectPath, [prevAssetPath, assetPath]);
1305
1398
  assetFile.extension = fileType.extension;
1306
1399
  assetFile.mimeType = fileType.mimeType;
1307
1400
  assetFile.size = size;
@@ -1377,15 +1470,15 @@ var AssetService = class extends AbstractCrudService {
1377
1470
  * @param projectId The project's ID
1378
1471
  * @param assetFile The AssetFile to convert
1379
1472
  */
1380
- async toAsset(projectId, assetFile) {
1381
- const assetPath = pathTo.asset(
1382
- projectId,
1383
- assetFile.id,
1384
- assetFile.extension
1385
- );
1473
+ async toAsset(projectId, assetFile, isFromHistory = false) {
1474
+ const assetPath = isFromHistory === false ? pathTo.asset(projectId, assetFile.id, assetFile.extension) : pathTo.tmpAsset(assetFile.id, assetFile.extension);
1475
+ const history = await this.gitService.log(pathTo.project(projectId), {
1476
+ filePath: pathTo.assetFile(projectId, assetFile.id)
1477
+ });
1386
1478
  const asset = {
1387
1479
  ...assetFile,
1388
- absolutePath: assetPath
1480
+ absolutePath: assetPath,
1481
+ history
1389
1482
  };
1390
1483
  return asset;
1391
1484
  }
@@ -1413,6 +1506,12 @@ var AssetService = class extends AbstractCrudService {
1413
1506
  });
1414
1507
  return result;
1415
1508
  }
1509
+ /**
1510
+ * Migrates an potentially outdated Asset file to the current schema
1511
+ */
1512
+ migrate(potentiallyOutdatedAssetFile) {
1513
+ return assetFileSchema.parse(potentiallyOutdatedAssetFile);
1514
+ }
1416
1515
  };
1417
1516
 
1418
1517
  // src/service/CollectionService.ts
@@ -1453,18 +1552,33 @@ var CollectionService = class extends AbstractCrudService {
1453
1552
  );
1454
1553
  await this.gitService.add(projectPath, [collectionFilePath]);
1455
1554
  await this.gitService.commit(projectPath, this.gitMessage.create);
1456
- return collectionFile;
1555
+ return this.toCollection(props.projectId, collectionFile);
1457
1556
  }
1458
1557
  /**
1459
1558
  * Returns a Collection by ID
1559
+ *
1560
+ * If a commit hash is provided, the Collection is read from history
1460
1561
  */
1461
1562
  async read(props) {
1462
1563
  readCollectionSchema.parse(props);
1463
- const collection = await this.jsonFileService.read(
1464
- pathTo.collectionFile(props.projectId, props.id),
1465
- collectionFileSchema
1466
- );
1467
- return collection;
1564
+ if (!props.commitHash) {
1565
+ const collectionFile = await this.jsonFileService.read(
1566
+ pathTo.collectionFile(props.projectId, props.id),
1567
+ collectionFileSchema
1568
+ );
1569
+ return this.toCollection(props.projectId, collectionFile);
1570
+ } else {
1571
+ const collectionFile = this.migrate(
1572
+ JSON.parse(
1573
+ await this.gitService.getFileContentAtCommit(
1574
+ pathTo.project(props.projectId),
1575
+ pathTo.collectionFile(props.projectId, props.id),
1576
+ props.commitHash
1577
+ )
1578
+ )
1579
+ );
1580
+ return this.toCollection(props.projectId, collectionFile);
1581
+ }
1468
1582
  }
1469
1583
  /**
1470
1584
  * Updates given Collection
@@ -1493,7 +1607,7 @@ var CollectionService = class extends AbstractCrudService {
1493
1607
  );
1494
1608
  await this.gitService.add(projectPath, [collectionFilePath]);
1495
1609
  await this.gitService.commit(projectPath, this.gitMessage.update);
1496
- return collectionFile;
1610
+ return this.toCollection(props.projectId, collectionFile);
1497
1611
  }
1498
1612
  /**
1499
1613
  * Deletes given Collection (folder), including it's items
@@ -1549,18 +1663,42 @@ var CollectionService = class extends AbstractCrudService {
1549
1663
  isCollection(obj) {
1550
1664
  return collectionFileSchema.safeParse(obj).success;
1551
1665
  }
1666
+ /**
1667
+ * Migrates an potentially outdated Collection file to the current schema
1668
+ */
1669
+ migrate(potentiallyOutdatedCollectionFile) {
1670
+ return collectionFileSchema.parse(potentiallyOutdatedCollectionFile);
1671
+ }
1672
+ /**
1673
+ * Creates an Collection from given CollectionFile
1674
+ *
1675
+ * @param projectId The project's ID
1676
+ * @param collectionFile The CollectionFile to convert
1677
+ */
1678
+ async toCollection(projectId, collectionFile) {
1679
+ const history = await this.gitService.log(pathTo.project(projectId), {
1680
+ filePath: pathTo.collectionFile(projectId, collectionFile.id)
1681
+ });
1682
+ const collection = {
1683
+ ...collectionFile,
1684
+ history
1685
+ };
1686
+ return collection;
1687
+ }
1552
1688
  };
1553
1689
 
1554
1690
  // src/service/EntryService.ts
1555
1691
  import Fs4 from "fs-extra";
1556
1692
  var EntryService = class extends AbstractCrudService {
1693
+ logService;
1557
1694
  jsonFileService;
1558
1695
  gitService;
1559
1696
  collectionService;
1560
1697
  assetService;
1561
1698
  // private sharedValueService: SharedValueService;
1562
- constructor(options, jsonFileService, gitService, collectionService, assetService) {
1699
+ constructor(options, logService, jsonFileService, gitService, collectionService, assetService) {
1563
1700
  super(serviceTypeSchema.Enum.Entry, options);
1701
+ this.logService = logService;
1564
1702
  this.jsonFileService = jsonFileService;
1565
1703
  this.gitService = gitService;
1566
1704
  this.collectionService = collectionService;
@@ -1589,11 +1727,11 @@ var EntryService = class extends AbstractCrudService {
1589
1727
  created: datetime(),
1590
1728
  updated: null
1591
1729
  };
1592
- const entry = await this.toEntry({
1593
- projectId: props.projectId,
1594
- collectionId: props.collectionId,
1730
+ const entry = await this.toEntry(
1731
+ props.projectId,
1732
+ props.collectionId,
1595
1733
  entryFile
1596
- });
1734
+ );
1597
1735
  this.validateValues({
1598
1736
  collectionId: props.collectionId,
1599
1737
  fieldDefinitions: collection.fieldDefinitions,
@@ -1609,19 +1747,30 @@ var EntryService = class extends AbstractCrudService {
1609
1747
  return entry;
1610
1748
  }
1611
1749
  /**
1612
- * Returns an Entry from given Collection by ID and language
1750
+ * Returns an Entry from given Collection by ID
1751
+ *
1752
+ * If a commit hash is provided, the Entry is read from history
1613
1753
  */
1614
1754
  async read(props) {
1615
1755
  readEntrySchema.parse(props);
1616
- const entryFile = await this.jsonFileService.read(
1617
- pathTo.entryFile(props.projectId, props.collectionId, props.id),
1618
- entryFileSchema
1619
- );
1620
- return await this.toEntry({
1621
- projectId: props.projectId,
1622
- collectionId: props.collectionId,
1623
- entryFile
1624
- });
1756
+ if (!props.commitHash) {
1757
+ const entryFile = await this.jsonFileService.read(
1758
+ pathTo.entryFile(props.projectId, props.collectionId, props.id),
1759
+ entryFileSchema
1760
+ );
1761
+ return this.toEntry(props.projectId, props.collectionId, entryFile);
1762
+ } else {
1763
+ const entryFile = this.migrate(
1764
+ JSON.parse(
1765
+ await this.gitService.getFileContentAtCommit(
1766
+ pathTo.project(props.projectId),
1767
+ pathTo.entryFile(props.projectId, props.collectionId, props.id),
1768
+ props.commitHash
1769
+ )
1770
+ )
1771
+ );
1772
+ return this.toEntry(props.projectId, props.collectionId, entryFile);
1773
+ }
1625
1774
  }
1626
1775
  /**
1627
1776
  * Updates an Entry of given Collection with new Values and shared Values
@@ -1648,11 +1797,11 @@ var EntryService = class extends AbstractCrudService {
1648
1797
  values: props.values,
1649
1798
  updated: datetime()
1650
1799
  };
1651
- const entry = await this.toEntry({
1652
- projectId: props.projectId,
1653
- collectionId: props.collectionId,
1800
+ const entry = await this.toEntry(
1801
+ props.projectId,
1802
+ props.collectionId,
1654
1803
  entryFile
1655
- });
1804
+ );
1656
1805
  this.validateValues({
1657
1806
  collectionId: props.collectionId,
1658
1807
  fieldDefinitions: collection.fieldDefinitions,
@@ -1722,6 +1871,12 @@ var EntryService = class extends AbstractCrudService {
1722
1871
  isEntry(obj) {
1723
1872
  return entrySchema.safeParse(obj).success;
1724
1873
  }
1874
+ /**
1875
+ * Migrates an potentially outdated Entry file to the current schema
1876
+ */
1877
+ migrate(potentiallyOutdatedEntryFile) {
1878
+ return entryFileSchema.parse(potentiallyOutdatedEntryFile);
1879
+ }
1725
1880
  /**
1726
1881
  * Returns a Field definition by ID
1727
1882
  */
@@ -1750,14 +1905,16 @@ var EntryService = class extends AbstractCrudService {
1750
1905
  id: value.fieldDefinitionId
1751
1906
  });
1752
1907
  const contentSchema = getValueContentSchemaFromFieldDefinition(fieldDefinition);
1753
- try {
1754
- for (const [_language, content] of Object.entries(value.content)) {
1755
- contentSchema.parse(content);
1908
+ this.logService.debug(
1909
+ "Validating Value against content schema generated from Field definition",
1910
+ {
1911
+ value,
1912
+ contentSchema,
1913
+ fieldDefinition
1756
1914
  }
1757
- } catch (error) {
1758
- console.log("Definition:", fieldDefinition);
1759
- console.log("Value:", value);
1760
- throw error;
1915
+ );
1916
+ for (const [_language, content] of Object.entries(value.content)) {
1917
+ contentSchema.parse(content);
1761
1918
  }
1762
1919
  });
1763
1920
  }
@@ -1827,16 +1984,20 @@ var EntryService = class extends AbstractCrudService {
1827
1984
  /**
1828
1985
  * Creates an Entry from given EntryFile by resolving it's Values
1829
1986
  */
1830
- async toEntry(props) {
1987
+ async toEntry(projectId, collectionId, entryFile) {
1988
+ const history = await this.gitService.log(pathTo.project(projectId), {
1989
+ filePath: pathTo.entryFile(projectId, collectionId, entryFile.id)
1990
+ });
1831
1991
  return {
1832
- ...props.entryFile,
1992
+ ...entryFile,
1993
+ history,
1833
1994
  // @ts-ignore @todo fixme - I have no idea why this happens. The types seem to be compatible to me and they work
1834
1995
  values: await Promise.all(
1835
- props.entryFile.values.map(async (value) => {
1996
+ entryFile.values.map(async (value) => {
1836
1997
  if (value.valueType === ValueTypeSchema.Enum.reference) {
1837
1998
  const resolvedContentReferences = await this.resolveValueContentReferences({
1838
- projectId: props.projectId,
1839
- collectionId: props.collectionId,
1999
+ projectId,
2000
+ collectionId,
1840
2001
  valueReference: value
1841
2002
  });
1842
2003
  return {
@@ -1853,11 +2014,10 @@ var EntryService = class extends AbstractCrudService {
1853
2014
 
1854
2015
  // src/service/GitService.ts
1855
2016
  import { GitProcess } from "dugite";
1856
- import { EOL as EOL2 } from "os";
1857
2017
  import PQueue from "p-queue";
2018
+ import Path2 from "path";
1858
2019
 
1859
2020
  // src/service/GitTagService.ts
1860
- import { EOL } from "os";
1861
2021
  var GitTagService = class extends AbstractCrudService {
1862
2022
  git;
1863
2023
  constructor(options, git) {
@@ -1940,7 +2100,7 @@ var GitTagService = class extends AbstractCrudService {
1940
2100
  "--format=%(refname:short)|%(subject)|%(*authorname)|%(*authoremail)|%(*authordate:iso-strict)"
1941
2101
  ];
1942
2102
  const result = await this.git(props.path, args);
1943
- const noEmptyLinesArr = result.stdout.split(EOL).filter((line) => {
2103
+ const noEmptyLinesArr = result.stdout.split("\n").filter((line) => {
1944
2104
  return line.trim() !== "";
1945
2105
  });
1946
2106
  const lineObjArr = noEmptyLinesArr.map((line) => {
@@ -1991,16 +2151,18 @@ var GitService = class {
1991
2151
  version;
1992
2152
  gitPath;
1993
2153
  queue;
2154
+ logService;
1994
2155
  gitTagService;
1995
2156
  userService;
1996
- constructor(options, userService) {
2157
+ constructor(options, logService, userService) {
1997
2158
  this.version = null;
1998
2159
  this.gitPath = null;
1999
2160
  this.queue = new PQueue({
2000
2161
  concurrency: 1
2001
2162
  // No concurrency because git operations are sequencial
2002
2163
  });
2003
- this.gitTagService = new GitTagService(options, this.git);
2164
+ this.gitTagService = new GitTagService(options, this.git.bind(this));
2165
+ this.logService = logService;
2004
2166
  this.userService = userService;
2005
2167
  this.updateVersion();
2006
2168
  this.updateGitPath();
@@ -2026,7 +2188,6 @@ var GitService = class {
2026
2188
  }
2027
2189
  await this.git(path, args);
2028
2190
  await this.setLocalConfig(path);
2029
- await this.installLfs(path);
2030
2191
  }
2031
2192
  /**
2032
2193
  * Clone a repository into a directory
@@ -2063,9 +2224,25 @@ var GitService = class {
2063
2224
  * @param files Files to add
2064
2225
  */
2065
2226
  async add(path, files2) {
2066
- const args = ["add", "--", ...files2];
2227
+ const relativePathsFromRepositoryRoot = files2.map((filePath) => {
2228
+ return filePath.replace(`${path}${Path2.sep}`, "");
2229
+ });
2230
+ const args = ["add", "--", ...relativePathsFromRepositoryRoot];
2067
2231
  await this.git(path, args);
2068
2232
  }
2233
+ async status(path) {
2234
+ const args = ["status", "--porcelain=2"];
2235
+ const result = await this.git(path, args);
2236
+ const normalizedLinesArr = result.stdout.split("\n").filter((line) => {
2237
+ return line.trim() !== "";
2238
+ }).map((line) => {
2239
+ const lineArr = line.trim().split(" ");
2240
+ return {
2241
+ filePath: lineArr[8]
2242
+ };
2243
+ });
2244
+ return normalizedLinesArr;
2245
+ }
2069
2246
  branches = {
2070
2247
  /**
2071
2248
  * List branches
@@ -2077,7 +2254,7 @@ var GitService = class {
2077
2254
  list: async (path) => {
2078
2255
  const args = ["branch", "--list", "--all"];
2079
2256
  const result = await this.git(path, args);
2080
- const normalizedLinesArr = result.stdout.split(EOL2).filter((line) => {
2257
+ const normalizedLinesArr = result.stdout.split("\n").filter((line) => {
2081
2258
  return line.trim() !== "";
2082
2259
  }).map((line) => {
2083
2260
  return line.trim().replace("* ", "");
@@ -2139,7 +2316,7 @@ var GitService = class {
2139
2316
  list: async (path) => {
2140
2317
  const args = ["remote"];
2141
2318
  const result = await this.git(path, args);
2142
- const normalizedLinesArr = result.stdout.split(EOL2).filter((line) => {
2319
+ const normalizedLinesArr = result.stdout.split("\n").filter((line) => {
2143
2320
  return line.trim() !== "";
2144
2321
  });
2145
2322
  return normalizedLinesArr;
@@ -2166,7 +2343,7 @@ var GitService = class {
2166
2343
  * @param path Path to the repository
2167
2344
  */
2168
2345
  addOrigin: async (path, url) => {
2169
- const args = ["remote", "add", "origin", url];
2346
+ const args = ["remote", "add", "origin", url.trim()];
2170
2347
  await this.git(path, args);
2171
2348
  },
2172
2349
  /**
@@ -2193,7 +2370,7 @@ var GitService = class {
2193
2370
  * @param path Path to the repository
2194
2371
  */
2195
2372
  setOriginUrl: async (path, url) => {
2196
- const args = ["remote", "set-url", "origin", url];
2373
+ const args = ["remote", "set-url", "origin", url.trim()];
2197
2374
  await this.git(path, args);
2198
2375
  }
2199
2376
  };
@@ -2314,11 +2491,12 @@ var GitService = class {
2314
2491
  if (options?.limit) {
2315
2492
  args = [...args, `--max-count=${options.limit}`];
2316
2493
  }
2317
- const result = await this.git(path, [
2318
- ...args,
2319
- "--format=%H|%s|%an|%ae|%aI|%D"
2320
- ]);
2321
- const noEmptyLinesArr = result.stdout.split(EOL2).filter((line) => {
2494
+ args = [...args, "--format=%H|%s|%an|%ae|%aI|%D"];
2495
+ if (options?.filePath) {
2496
+ args = [...args, "--", options.filePath];
2497
+ }
2498
+ const result = await this.git(path, args);
2499
+ const noEmptyLinesArr = result.stdout.split("\n").filter((line) => {
2322
2500
  return line.trim() !== "";
2323
2501
  });
2324
2502
  const lineObjArr = noEmptyLinesArr.map((line) => {
@@ -2336,6 +2514,27 @@ var GitService = class {
2336
2514
  });
2337
2515
  return lineObjArr.filter(this.isGitCommit.bind(this));
2338
2516
  }
2517
+ /**
2518
+ * Retrieves the content of a file at a specific commit
2519
+ *
2520
+ * @see https://git-scm.com/docs/git-show
2521
+ */
2522
+ async getFileContentAtCommit(path, filePath, commitHash, encoding = "utf8") {
2523
+ const relativePathFromRepositoryRoot = filePath.replace(
2524
+ `${path}${Path2.sep}`,
2525
+ ""
2526
+ );
2527
+ const normalizedPath = relativePathFromRepositoryRoot.split("\\").join("/");
2528
+ const args = ["show", `${commitHash}:${normalizedPath}`];
2529
+ const setEncoding = (cb) => {
2530
+ if (cb.stdout) {
2531
+ cb.stdout.setEncoding(encoding);
2532
+ }
2533
+ };
2534
+ return (await this.git(path, args, {
2535
+ processCallback: setEncoding
2536
+ })).stdout;
2537
+ }
2339
2538
  refNameToTagName(refName) {
2340
2539
  const tagName = refName.replace("tag: ", "").trim();
2341
2540
  if (tagName === "" || uuidSchema.safeParse(tagName).success === false) {
@@ -2375,16 +2574,6 @@ var GitService = class {
2375
2574
  async checkBranchOrTagName(path, name) {
2376
2575
  await this.git(path, ["check-ref-format", "--allow-onelevel", name]);
2377
2576
  }
2378
- /**
2379
- * Installs LFS support and starts tracking
2380
- * all files inside the lfs folder
2381
- *
2382
- * @param path Path to the repository
2383
- */
2384
- async installLfs(path) {
2385
- await this.git(path, ["lfs", "install"]);
2386
- await this.git(path, ["lfs", "track", "lfs/*"]);
2387
- }
2388
2577
  /**
2389
2578
  * Sets the git config of given local repository from ElekIoCoreOptions
2390
2579
  *
@@ -2422,31 +2611,37 @@ var GitService = class {
2422
2611
  * @param path Path to the repository
2423
2612
  * @param args Arguments to append after the `git` command
2424
2613
  */
2425
- async git(path, args) {
2426
- const result = await this.queue.add(
2427
- () => GitProcess.exec(args, path, {
2428
- env: {
2429
- // @todo Nasty stuff - remove after update to dugite with git > v2.45.2 once available
2430
- // @see https://github.com/git-lfs/git-lfs/issues/5749
2431
- GIT_CLONE_PROTECTION_ACTIVE: "false"
2432
- }
2433
- })
2434
- );
2614
+ async git(path, args, options) {
2615
+ const result = await this.queue.add(async () => {
2616
+ const start = Date.now();
2617
+ const gitResult = await GitProcess.exec(args, path, options);
2618
+ const durationMs = Date.now() - start;
2619
+ return {
2620
+ gitResult,
2621
+ durationMs
2622
+ };
2623
+ });
2435
2624
  if (!result) {
2436
2625
  throw new GitError(
2437
2626
  `Git ${this.version} (${this.gitPath}) command "git ${args.join(
2438
2627
  " "
2439
- )}" failed to return a result`
2628
+ )}" executed for "${path}" failed to return a result`
2440
2629
  );
2441
2630
  }
2442
- if (result.exitCode !== 0) {
2631
+ const gitLog = `Executed "git ${args.join(" ")}" in ${result.durationMs}ms`;
2632
+ if (result.durationMs >= 100) {
2633
+ this.logService.warn(gitLog);
2634
+ } else {
2635
+ this.logService.debug(gitLog);
2636
+ }
2637
+ if (result.gitResult.exitCode !== 0) {
2443
2638
  throw new GitError(
2444
2639
  `Git ${this.version} (${this.gitPath}) command "git ${args.join(
2445
2640
  " "
2446
- )}" failed with exit code "${result.exitCode}" and message "${result.stderr}"`
2641
+ )}" executed for "${path}" failed with exit code "${result.gitResult.exitCode}" and message "${result.gitResult.stderr.trim() || result.gitResult.stdout.trim()}"`
2447
2642
  );
2448
2643
  }
2449
- return result;
2644
+ return result.gitResult;
2450
2645
  }
2451
2646
  };
2452
2647
 
@@ -2454,8 +2649,10 @@ var GitService = class {
2454
2649
  import Fs5 from "fs-extra";
2455
2650
  var JsonFileService = class extends AbstractCrudService {
2456
2651
  cache = /* @__PURE__ */ new Map();
2457
- constructor(options) {
2652
+ logService;
2653
+ constructor(options, logService) {
2458
2654
  super(serviceTypeSchema.Enum.JsonFile, options);
2655
+ this.logService = logService;
2459
2656
  }
2460
2657
  /**
2461
2658
  * Creates a new file on disk. Fails if path already exists
@@ -2472,7 +2669,8 @@ var JsonFileService = class extends AbstractCrudService {
2472
2669
  flag: "wx",
2473
2670
  encoding: "utf8"
2474
2671
  });
2475
- this.cache.set(path, parsedData);
2672
+ this.options.file.cache === true && this.cache.set(path, parsedData);
2673
+ this.logService.debug(`Created file "${path}"`);
2476
2674
  return parsedData;
2477
2675
  }
2478
2676
  /**
@@ -2483,18 +2681,43 @@ var JsonFileService = class extends AbstractCrudService {
2483
2681
  * @returns Validated content of the file from disk
2484
2682
  */
2485
2683
  async read(path, schema) {
2486
- if (this.cache.has(path)) {
2487
- return this.cache.get(path);
2684
+ if (this.options.file.cache === true && this.cache.has(path)) {
2685
+ this.logService.debug(`Cache hit reading file "${path}"`);
2686
+ const json2 = this.cache.get(path);
2687
+ const parsedData2 = schema.parse(json2);
2688
+ return parsedData2;
2488
2689
  }
2690
+ this.logService.debug(`Cache miss reading file "${path}"`);
2489
2691
  const data = await Fs5.readFile(path, {
2490
2692
  flag: "r",
2491
2693
  encoding: "utf8"
2492
2694
  });
2493
2695
  const json = this.deserialize(data);
2494
2696
  const parsedData = schema.parse(json);
2495
- this.cache.set(path, parsedData);
2697
+ this.options.file.cache === true && this.cache.set(path, parsedData);
2496
2698
  return parsedData;
2497
2699
  }
2700
+ /**
2701
+ * Reads the content of a file on disk. Fails if path does not exist.
2702
+ * Does not validate the content of the file against a schema and
2703
+ * therefore is only to be used when retrieving data we do not have
2704
+ * a current schema for. E.g. reading from history or while upgrading
2705
+ * the old schema of a file to a new, current schema.
2706
+ *
2707
+ * Does not read from or write to cache.
2708
+ *
2709
+ * @param path Path to read the file from
2710
+ * @returns Unvalidated content of the file from disk
2711
+ */
2712
+ async unsafeRead(path) {
2713
+ this.logService.warn(`Unsafe reading of file "${path}"`);
2714
+ const data = await Fs5.readFile(path, {
2715
+ flag: "r",
2716
+ encoding: "utf8"
2717
+ });
2718
+ const json = this.deserialize(data);
2719
+ return json;
2720
+ }
2498
2721
  /**
2499
2722
  * Overwrites an existing file on disk
2500
2723
  *
@@ -2512,31 +2735,90 @@ var JsonFileService = class extends AbstractCrudService {
2512
2735
  flag: "w",
2513
2736
  encoding: "utf8"
2514
2737
  });
2515
- this.cache.set(path, parsedData);
2738
+ this.options.file.cache === true && this.cache.set(path, parsedData);
2739
+ this.logService.debug(`Updated file "${path}"`);
2516
2740
  return parsedData;
2517
2741
  }
2518
2742
  serialize(data) {
2519
- return JSON.stringify(data, null, this.options.file.json.indentation);
2743
+ return JSON.stringify(data, null, 2);
2520
2744
  }
2521
2745
  deserialize(data) {
2522
2746
  return JSON.parse(data);
2523
2747
  }
2524
2748
  };
2525
2749
 
2750
+ // src/service/LogService.ts
2751
+ import Path3 from "path";
2752
+ import {
2753
+ createLogger,
2754
+ format,
2755
+ transports
2756
+ } from "winston";
2757
+ import DailyRotateFile from "winston-daily-rotate-file";
2758
+ var LogService = class {
2759
+ logger;
2760
+ constructor(options) {
2761
+ const rotatingFileTransport = new DailyRotateFile({
2762
+ filename: Path3.join(pathTo.logs, "core-%DATE%.log"),
2763
+ datePattern: "YYYY-MM-DD",
2764
+ zippedArchive: true,
2765
+ maxFiles: "30d",
2766
+ handleExceptions: true,
2767
+ handleRejections: true,
2768
+ format: format.combine(format.timestamp(), format.json())
2769
+ });
2770
+ this.logger = createLogger({
2771
+ level: options.log.level,
2772
+ transports: [
2773
+ rotatingFileTransport,
2774
+ new transports.Console({
2775
+ handleExceptions: true,
2776
+ handleRejections: true,
2777
+ format: format.cli()
2778
+ })
2779
+ ]
2780
+ });
2781
+ rotatingFileTransport.on("rotate", (oldFilename, newFilename) => {
2782
+ this.logger.info(
2783
+ `Rotated log file from ${oldFilename} to ${newFilename}`
2784
+ );
2785
+ });
2786
+ }
2787
+ debug(message, ...meta) {
2788
+ return this.logger.debug(message, ...meta);
2789
+ }
2790
+ info(message, ...meta) {
2791
+ return this.logger.info(message, ...meta);
2792
+ }
2793
+ warn(message, ...meta) {
2794
+ return this.logger.warn(message, ...meta);
2795
+ }
2796
+ error(message, ...meta) {
2797
+ return this.logger.error(message, ...meta);
2798
+ }
2799
+ read(options) {
2800
+ return this.logger.query(options);
2801
+ }
2802
+ };
2803
+
2526
2804
  // src/service/ProjectService.ts
2527
2805
  import Fs6 from "fs-extra";
2528
2806
  import Os2 from "os";
2529
- import Path2 from "path";
2807
+ import Path4 from "path";
2530
2808
  import Semver from "semver";
2531
2809
  var ProjectService = class extends AbstractCrudService {
2810
+ coreVersion;
2811
+ logService;
2532
2812
  jsonFileService;
2533
2813
  userService;
2534
2814
  gitService;
2535
2815
  assetService;
2536
2816
  collectionService;
2537
2817
  entryService;
2538
- constructor(options, jsonFileService, userService, gitService, assetService, collectionService, entryService) {
2818
+ constructor(coreVersion, options, logService, jsonFileService, userService, gitService, assetService, collectionService, entryService) {
2539
2819
  super(serviceTypeSchema.Enum.Project, options);
2820
+ this.coreVersion = coreVersion;
2821
+ this.logService = logService;
2540
2822
  this.jsonFileService = jsonFileService;
2541
2823
  this.userService = userService;
2542
2824
  this.gitService = gitService;
@@ -2568,8 +2850,7 @@ var ProjectService = class extends AbstractCrudService {
2568
2850
  settings: Object.assign({}, defaultSettings, props.settings),
2569
2851
  created: datetime(),
2570
2852
  updated: null,
2571
- coreVersion: this.options.version,
2572
- // @todo should be read from package.json to avoid duplicates
2853
+ coreVersion: this.coreVersion,
2573
2854
  status: "todo",
2574
2855
  version: "0.0.1"
2575
2856
  };
@@ -2598,9 +2879,7 @@ var ProjectService = class extends AbstractCrudService {
2598
2879
  });
2599
2880
  throw error;
2600
2881
  }
2601
- return await this.toProject({
2602
- projectFile
2603
- });
2882
+ return await this.toProject(projectFile);
2604
2883
  }
2605
2884
  /**
2606
2885
  * Clones a Project by URL
@@ -2608,10 +2887,10 @@ var ProjectService = class extends AbstractCrudService {
2608
2887
  async clone(props) {
2609
2888
  cloneProjectSchema.parse(props);
2610
2889
  const tmpId = uuid();
2611
- const tmpProjectPath = Path2.join(pathTo.tmp, tmpId);
2890
+ const tmpProjectPath = Path4.join(pathTo.tmp, tmpId);
2612
2891
  await this.gitService.clone(props.url, tmpProjectPath);
2613
2892
  const projectFile = await this.jsonFileService.read(
2614
- Path2.join(tmpProjectPath, "project.json"),
2893
+ Path4.join(tmpProjectPath, "project.json"),
2615
2894
  projectFileSchema
2616
2895
  );
2617
2896
  const projectPath = pathTo.project(projectFile.id);
@@ -2623,22 +2902,33 @@ var ProjectService = class extends AbstractCrudService {
2623
2902
  }
2624
2903
  await Fs6.copy(tmpProjectPath, projectPath);
2625
2904
  await Fs6.remove(tmpProjectPath);
2626
- return await this.toProject({
2627
- projectFile
2628
- });
2905
+ return await this.toProject(projectFile);
2629
2906
  }
2630
2907
  /**
2631
2908
  * Returns a Project by ID
2909
+ *
2910
+ * If a commit hash is provided, the Project is read from history
2632
2911
  */
2633
2912
  async read(props) {
2634
2913
  readProjectSchema.parse(props);
2635
- const projectFile = await this.jsonFileService.read(
2636
- pathTo.projectFile(props.id),
2637
- projectFileSchema
2638
- );
2639
- return await this.toProject({
2640
- projectFile
2641
- });
2914
+ if (!props.commitHash) {
2915
+ const projectFile = await this.jsonFileService.read(
2916
+ pathTo.projectFile(props.id),
2917
+ projectFileSchema
2918
+ );
2919
+ return await this.toProject(projectFile);
2920
+ } else {
2921
+ const projectFile = this.migrate(
2922
+ JSON.parse(
2923
+ await this.gitService.getFileContentAtCommit(
2924
+ pathTo.project(props.id),
2925
+ pathTo.projectFile(props.id),
2926
+ props.commitHash
2927
+ )
2928
+ )
2929
+ );
2930
+ return await this.toProject(projectFile);
2931
+ }
2642
2932
  }
2643
2933
  /**
2644
2934
  * Updates given Project
@@ -2652,6 +2942,7 @@ var ProjectService = class extends AbstractCrudService {
2652
2942
  ...prevProjectFile,
2653
2943
  name: props.name || prevProjectFile.name,
2654
2944
  description: props.description || prevProjectFile.description,
2945
+ coreVersion: this.coreVersion,
2655
2946
  settings: {
2656
2947
  language: {
2657
2948
  supported: props.settings?.language.supported || prevProjectFile.settings.language.supported,
@@ -2663,82 +2954,97 @@ var ProjectService = class extends AbstractCrudService {
2663
2954
  await this.jsonFileService.update(projectFile, filePath, projectFileSchema);
2664
2955
  await this.gitService.add(projectPath, [filePath]);
2665
2956
  await this.gitService.commit(projectPath, this.gitMessage.update);
2666
- return await this.toProject({
2667
- projectFile
2668
- });
2957
+ return await this.toProject(projectFile);
2669
2958
  }
2670
2959
  /**
2671
- * Upgrades given Project to the latest version of this client
2672
- *
2673
- * Needed when a new core version is requiring changes to existing files or structure.
2960
+ * Upgrades given Project to the current version of Core
2674
2961
  *
2675
- * @todo Find out why using this.snapshotService is throwing isObjWithKeyAndValueOfString of undefined error in gitService (maybe binding issue)
2962
+ * Needed when a new Core version is requiring changes to existing files or structure.
2676
2963
  */
2677
2964
  async upgrade(props) {
2678
2965
  upgradeProjectSchema.parse(props);
2679
- const project = await this.read(props);
2680
- const projectPath = pathTo.project(project.id);
2681
- if (Semver.gt(project.coreVersion, this.options.version)) {
2682
- throw new Error(
2683
- `Failed upgrading project. The projects core version "${project.coreVersion}" is higher than the current core version "${this.options.version}" of this client. A client upgrade is needed beforehand.`
2966
+ const projectPath = pathTo.project(props.id);
2967
+ const projectFilePath = pathTo.projectFile(props.id);
2968
+ const prevProjectFile = outdatedProjectSchema.passthrough().parse(await this.jsonFileService.unsafeRead(projectFilePath));
2969
+ if (Semver.gt(prevProjectFile.coreVersion, this.coreVersion)) {
2970
+ throw new ProjectUpgradeError(
2971
+ `The Projects Core version "${prevProjectFile.coreVersion}" is higher than the current Core version "${this.coreVersion}".`
2684
2972
  );
2685
2973
  }
2686
- if (Semver.eq(project.coreVersion, this.options.version)) {
2687
- return;
2974
+ if (Semver.eq(prevProjectFile.coreVersion, this.coreVersion) && props.force !== true) {
2975
+ throw new ProjectUpgradeError(
2976
+ `The Projects Core version "${prevProjectFile.coreVersion}" is already up to date.`
2977
+ );
2688
2978
  }
2689
- const upgradeFiles = await files(
2690
- Path2.resolve(__dirname, "../upgrade"),
2691
- "ts"
2979
+ const assetReferences = await this.listReferences("asset", props.id);
2980
+ const collectionReferences = await this.listReferences(
2981
+ "collection",
2982
+ props.id
2692
2983
  );
2693
- const upgrades = (await Promise.all(
2694
- upgradeFiles.map((file) => {
2695
- return import(Path2.join("../upgrade", file.name));
2696
- })
2697
- )).map((upgradeImport) => {
2698
- return upgradeImport.default;
2699
- });
2700
- const sortedUpgrades = upgrades.sort((a, b) => {
2701
- return Semver.compare(a.to, b.to);
2702
- }).filter((upgrade) => {
2703
- if (upgrade.to !== "0.0.0") {
2704
- return upgrade;
2705
- }
2706
- return;
2984
+ this.logService.info(
2985
+ `Attempting to upgrade Project "${props.id}" from Core version ${prevProjectFile.coreVersion} to ${this.coreVersion}`
2986
+ );
2987
+ const failsafeTag = await this.gitService.tags.create({
2988
+ path: projectPath,
2989
+ message: `Attempting to upgrade Project from Core version ${prevProjectFile.coreVersion} to ${this.coreVersion}`
2707
2990
  });
2708
- for (let index = 0; index < sortedUpgrades.length; index++) {
2709
- const upgrade = sortedUpgrades[index];
2710
- if (!upgrade) {
2711
- throw new Error("Expected ProjectUpgrade but got undefined");
2712
- }
2713
- const failsafeTag = await this.gitService.tags.create({
2991
+ try {
2992
+ await Promise.all(
2993
+ assetReferences.map(async (reference) => {
2994
+ await this.upgradeObjectFile(props.id, "asset", reference);
2995
+ })
2996
+ );
2997
+ await Promise.all(
2998
+ collectionReferences.map(async (reference) => {
2999
+ await this.upgradeObjectFile(props.id, "collection", reference);
3000
+ })
3001
+ );
3002
+ await Promise.all(
3003
+ collectionReferences.map(async (collectionReference) => {
3004
+ const entryReferences = await this.listReferences(
3005
+ "entry",
3006
+ props.id,
3007
+ collectionReference.id
3008
+ );
3009
+ await Promise.all(
3010
+ entryReferences.map(async (reference) => {
3011
+ await this.upgradeObjectFile(
3012
+ props.id,
3013
+ "entry",
3014
+ reference,
3015
+ collectionReference.id
3016
+ );
3017
+ })
3018
+ );
3019
+ })
3020
+ );
3021
+ const migratedProjectFile = this.migrate(prevProjectFile);
3022
+ await this.update(migratedProjectFile);
3023
+ this.logService.info(`Upgraded project "${projectFilePath}"`, {
3024
+ previous: prevProjectFile,
3025
+ migrated: migratedProjectFile
3026
+ });
3027
+ await this.gitService.tags.create({
2714
3028
  path: projectPath,
2715
- message: `Attempting to upgrade Project to Core version "${upgrade.to}"`
3029
+ message: `Upgraded Project to Core version "${this.coreVersion}"`
2716
3030
  });
2717
- try {
2718
- await upgrade.run(project);
2719
- project.coreVersion = upgrade.to;
2720
- await this.update(project);
2721
- await this.gitService.tags.create({
2722
- path: projectPath,
2723
- message: `Upgraded Project to Core version "${upgrade.to}"`
2724
- });
2725
- await this.gitService.tags.delete({
2726
- path: projectPath,
2727
- id: failsafeTag.id
2728
- });
2729
- } catch (error) {
2730
- await this.gitService.reset(projectPath, "hard", failsafeTag.id);
2731
- throw new ProjectUpgradeError(
2732
- `Failed to upgrade Project to Core version "${upgrade.to}"`
2733
- );
2734
- }
3031
+ await this.gitService.tags.delete({
3032
+ path: projectPath,
3033
+ id: failsafeTag.id
3034
+ });
3035
+ } catch (error) {
3036
+ await this.gitService.reset(projectPath, "hard", failsafeTag.id);
3037
+ throw error;
2735
3038
  }
2736
3039
  }
2737
3040
  branches = {
2738
3041
  list: async (props) => {
2739
3042
  listBranchesProjectSchema.parse(props);
2740
3043
  const projectPath = pathTo.project(props.id);
2741
- await this.gitService.fetch(projectPath);
3044
+ const hasOrigin = await this.gitService.remotes.hasOrigin(projectPath);
3045
+ if (hasOrigin) {
3046
+ await this.gitService.fetch(projectPath);
3047
+ }
2742
3048
  return await this.gitService.branches.list(projectPath);
2743
3049
  },
2744
3050
  current: async (props) => {
@@ -2760,7 +3066,12 @@ var ProjectService = class extends AbstractCrudService {
2760
3066
  getOriginUrl: async (props) => {
2761
3067
  getRemoteOriginUrlProjectSchema.parse(props);
2762
3068
  const projectPath = pathTo.project(props.id);
2763
- return await this.gitService.remotes.getOriginUrl(projectPath);
3069
+ const hasOrigin = await this.gitService.remotes.hasOrigin(projectPath);
3070
+ if (!hasOrigin) {
3071
+ return null;
3072
+ } else {
3073
+ return await this.gitService.remotes.getOriginUrl(projectPath);
3074
+ }
2764
3075
  },
2765
3076
  setOriginUrl: async (props) => {
2766
3077
  setRemoteOriginUrlProjectSchema.parse(props);
@@ -2817,6 +3128,27 @@ var ProjectService = class extends AbstractCrudService {
2817
3128
  deleteProjectSchema.parse(props);
2818
3129
  await Fs6.remove(pathTo.project(props.id));
2819
3130
  }
3131
+ /**
3132
+ * Lists outdated Projects that need to be upgraded
3133
+ */
3134
+ async listOutdated() {
3135
+ const projectReferences = await this.listReferences(
3136
+ objectTypeSchema.Enum.project
3137
+ );
3138
+ const result = await Promise.all(
3139
+ projectReferences.map(async (reference) => {
3140
+ const json = await this.jsonFileService.unsafeRead(
3141
+ pathTo.projectFile(reference.id)
3142
+ );
3143
+ const projectFile = outdatedProjectSchema.parse(json);
3144
+ if (projectFile.coreVersion !== this.coreVersion) {
3145
+ return projectFile;
3146
+ }
3147
+ return null;
3148
+ })
3149
+ );
3150
+ return result.filter(notEmpty);
3151
+ }
2820
3152
  async list(props) {
2821
3153
  if (props) {
2822
3154
  listProjectsSchema.parse(props);
@@ -2879,11 +3211,25 @@ var ProjectService = class extends AbstractCrudService {
2879
3211
  };
2880
3212
  }
2881
3213
  /**
2882
- * Creates a Project from given ProjectFile by adding git information
3214
+ * Migrates an potentially outdated Project file to the current schema
3215
+ */
3216
+ migrate(potentiallyOutdatedProjectFile) {
3217
+ return projectFileSchema.parse(potentiallyOutdatedProjectFile);
3218
+ }
3219
+ /**
3220
+ * Creates a Project from given ProjectFile
2883
3221
  */
2884
- async toProject(props) {
3222
+ async toProject(projectFile) {
3223
+ const fullHistory = await this.gitService.log(
3224
+ pathTo.project(projectFile.id)
3225
+ );
3226
+ const history = await this.gitService.log(pathTo.project(projectFile.id), {
3227
+ filePath: pathTo.projectFile(projectFile.id)
3228
+ });
2885
3229
  return {
2886
- ...props.projectFile
3230
+ ...projectFile,
3231
+ history,
3232
+ fullHistory
2887
3233
  };
2888
3234
  }
2889
3235
  /**
@@ -2895,8 +3241,8 @@ var ProjectService = class extends AbstractCrudService {
2895
3241
  const folders2 = Object.values(projectFolderSchema.Values);
2896
3242
  await Promise.all(
2897
3243
  folders2.map(async (folder) => {
2898
- await Fs6.mkdirp(Path2.join(path, folder));
2899
- await Fs6.writeFile(Path2.join(path, folder, ".gitkeep"), "");
3244
+ await Fs6.mkdirp(Path4.join(path, folder));
3245
+ await Fs6.writeFile(Path4.join(path, folder, ".gitkeep"), "");
2900
3246
  })
2901
3247
  );
2902
3248
  }
@@ -2920,14 +3266,80 @@ var ProjectService = class extends AbstractCrudService {
2920
3266
  // projectFolderSchema.Enum.public + '/',
2921
3267
  // projectFolderSchema.Enum.logs + '/',
2922
3268
  ];
2923
- await Fs6.writeFile(Path2.join(path, ".gitignore"), lines.join(Os2.EOL));
3269
+ await Fs6.writeFile(Path4.join(path, ".gitignore"), lines.join(Os2.EOL));
3270
+ }
3271
+ async upgradeObjectFile(projectId, objectType, reference, collectionId) {
3272
+ switch (objectType) {
3273
+ case "asset": {
3274
+ const assetFilePath = pathTo.assetFile(projectId, reference.id);
3275
+ const prevAssetFile = await this.jsonFileService.unsafeRead(
3276
+ assetFilePath
3277
+ );
3278
+ const migratedAssetFile = this.assetService.migrate(prevAssetFile);
3279
+ await this.assetService.update({ projectId, ...migratedAssetFile });
3280
+ this.logService.info(`Upgraded ${objectType} "${assetFilePath}"`, {
3281
+ previous: prevAssetFile,
3282
+ migrated: migratedAssetFile
3283
+ });
3284
+ return;
3285
+ }
3286
+ case "collection": {
3287
+ const collectionFilePath = pathTo.collectionFile(
3288
+ projectId,
3289
+ reference.id
3290
+ );
3291
+ const prevCollectionFile = await this.jsonFileService.unsafeRead(
3292
+ collectionFilePath
3293
+ );
3294
+ const migratedCollectionFile = this.collectionService.migrate(prevCollectionFile);
3295
+ await this.collectionService.update({
3296
+ projectId,
3297
+ ...migratedCollectionFile
3298
+ });
3299
+ this.logService.info(`Upgraded ${objectType} "${collectionFilePath}"`, {
3300
+ previous: prevCollectionFile,
3301
+ migrated: migratedCollectionFile
3302
+ });
3303
+ return;
3304
+ }
3305
+ case "entry": {
3306
+ if (!collectionId) {
3307
+ throw new RequiredParameterMissingError("collectionId");
3308
+ }
3309
+ const entryFilePath = pathTo.entryFile(
3310
+ projectId,
3311
+ collectionId,
3312
+ reference.id
3313
+ );
3314
+ const prevEntryFile = await this.jsonFileService.unsafeRead(
3315
+ entryFilePath
3316
+ );
3317
+ const migratedEntryFile = this.entryService.migrate(prevEntryFile);
3318
+ await this.entryService.update({
3319
+ projectId,
3320
+ collectionId,
3321
+ ...migratedEntryFile
3322
+ });
3323
+ this.logService.info(`Upgraded ${objectType} "${entryFilePath}"`, {
3324
+ previous: prevEntryFile,
3325
+ migrated: migratedEntryFile
3326
+ });
3327
+ return;
3328
+ }
3329
+ default:
3330
+ throw new Error(
3331
+ `Trying to upgrade unsupported object file of type "${objectType}"`
3332
+ );
3333
+ }
2924
3334
  }
2925
3335
  };
2926
3336
 
2927
3337
  // src/service/UserService.ts
2928
3338
  var UserService = class {
3339
+ logService;
2929
3340
  jsonFileService;
2930
- constructor(jsonFileService) {
3341
+ constructor(logService, jsonFileService) {
3342
+ this.logService = logService;
2931
3343
  this.jsonFileService = jsonFileService;
2932
3344
  }
2933
3345
  /**
@@ -2937,6 +3349,7 @@ var UserService = class {
2937
3349
  try {
2938
3350
  return await this.jsonFileService.read(pathTo.userFile, userFileSchema);
2939
3351
  } catch (error) {
3352
+ this.logService.info("No User found");
2940
3353
  return void 0;
2941
3354
  }
2942
3355
  }
@@ -2954,13 +3367,16 @@ var UserService = class {
2954
3367
  if (userFile.userType === UserTypeSchema.Enum.cloud) {
2955
3368
  }
2956
3369
  await this.jsonFileService.update(userFile, userFilePath, userFileSchema);
3370
+ this.logService.debug("Updated User");
2957
3371
  return userFile;
2958
3372
  }
2959
3373
  };
2960
3374
 
2961
3375
  // src/index.node.ts
2962
3376
  var ElekIoCore = class {
3377
+ coreVersion;
2963
3378
  options;
3379
+ logService;
2964
3380
  userService;
2965
3381
  gitService;
2966
3382
  jsonFileService;
@@ -2968,24 +3384,29 @@ var ElekIoCore = class {
2968
3384
  projectService;
2969
3385
  collectionService;
2970
3386
  entryService;
2971
- // private readonly sharedValueService: SharedValueService;
2972
3387
  constructor(props) {
3388
+ this.coreVersion = version;
2973
3389
  const parsedProps = constructorElekIoCoreSchema.parse(props);
2974
3390
  const defaults = {
2975
- environment: "production",
2976
- version: "0.0.0",
3391
+ log: {
3392
+ level: "info"
3393
+ },
2977
3394
  file: {
2978
- json: {
2979
- indentation: 2
2980
- }
3395
+ cache: true
2981
3396
  }
2982
3397
  };
2983
3398
  this.options = Object.assign({}, defaults, parsedProps);
2984
- this.jsonFileService = new JsonFileService(this.options);
2985
- this.userService = new UserService(this.jsonFileService);
2986
- this.gitService = new GitService(this.options, this.userService);
3399
+ this.logService = new LogService(this.options);
3400
+ this.jsonFileService = new JsonFileService(this.options, this.logService);
3401
+ this.userService = new UserService(this.logService, this.jsonFileService);
3402
+ this.gitService = new GitService(
3403
+ this.options,
3404
+ this.logService,
3405
+ this.userService
3406
+ );
2987
3407
  this.assetService = new AssetService(
2988
3408
  this.options,
3409
+ this.logService,
2989
3410
  this.jsonFileService,
2990
3411
  this.gitService
2991
3412
  );
@@ -2996,14 +3417,16 @@ var ElekIoCore = class {
2996
3417
  );
2997
3418
  this.entryService = new EntryService(
2998
3419
  this.options,
3420
+ this.logService,
2999
3421
  this.jsonFileService,
3000
3422
  this.gitService,
3001
3423
  this.collectionService,
3002
3424
  this.assetService
3003
- // this.sharedValueService
3004
3425
  );
3005
3426
  this.projectService = new ProjectService(
3427
+ this.coreVersion,
3006
3428
  this.options,
3429
+ this.logService,
3007
3430
  this.jsonFileService,
3008
3431
  this.userService,
3009
3432
  this.gitService,
@@ -3011,18 +3434,19 @@ var ElekIoCore = class {
3011
3434
  this.collectionService,
3012
3435
  this.entryService
3013
3436
  );
3014
- if (this.options.environment !== "production") {
3015
- console.info(
3016
- `Initializing inside an "${this.options.environment}" environment`,
3017
- {
3018
- ...this.options
3019
- }
3020
- );
3021
- }
3437
+ this.logService.info(`Initializing elek.io Core ${this.coreVersion}`, {
3438
+ options: this.options
3439
+ });
3022
3440
  Fs7.mkdirpSync(pathTo.projects);
3023
3441
  Fs7.mkdirpSync(pathTo.tmp);
3024
3442
  Fs7.emptyDirSync(pathTo.tmp);
3025
3443
  }
3444
+ /**
3445
+ * Exposes the logger
3446
+ */
3447
+ get logger() {
3448
+ return this.logService;
3449
+ }
3026
3450
  /**
3027
3451
  * Utility / helper functions
3028
3452
  */
@@ -3065,12 +3489,6 @@ var ElekIoCore = class {
3065
3489
  get entries() {
3066
3490
  return this.entryService;
3067
3491
  }
3068
- /**
3069
- * CRUD methods to work with Values
3070
- */
3071
- // public get sharedValues(): SharedValueService {
3072
- // return this.sharedValueService;
3073
- // }
3074
3492
  };
3075
3493
  export {
3076
3494
  BooleanFieldDefinitionBaseSchema,
@@ -3125,7 +3543,6 @@ export {
3125
3543
  entryFieldDefinitionSchema,
3126
3544
  entryFileSchema,
3127
3545
  entrySchema,
3128
- environmentSchema,
3129
3546
  fieldDefinitionSchema,
3130
3547
  fileReferenceSchema,
3131
3548
  getChangesProjectSchema,
@@ -3148,8 +3565,10 @@ export {
3148
3565
  listGitTagsSchema,
3149
3566
  listProjectsSchema,
3150
3567
  localUserSchema,
3568
+ logLevelSchema,
3151
3569
  numberFieldDefinitionSchema,
3152
3570
  objectTypeSchema,
3571
+ outdatedProjectSchema,
3153
3572
  projectExportSchema,
3154
3573
  projectFileSchema,
3155
3574
  projectFolderSchema,
@@ -3167,6 +3586,7 @@ export {
3167
3586
  resolvedReferencedValueSchema,
3168
3587
  resolvedValueContentReferenceSchema,
3169
3588
  resolvedValueSchema,
3589
+ saveAssetSchema,
3170
3590
  searchProjectSchema,
3171
3591
  serviceTypeSchema,
3172
3592
  setRemoteOriginUrlProjectSchema,