@elek-io/core 0.9.0 → 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({
@@ -886,7 +936,6 @@ var baseUserSchema = gitSignatureSchema.extend({
886
936
  userType: UserTypeSchema,
887
937
  language: supportedLanguageSchema,
888
938
  window: z13.object({
889
- displayId: z13.number(),
890
939
  width: z13.number(),
891
940
  height: z13.number(),
892
941
  position: z13.object({
@@ -959,7 +1008,7 @@ var workingDirectory = Path.join(Os.homedir(), "elek.io");
959
1008
  var pathTo = {
960
1009
  tmp: Path.join(workingDirectory, "tmp"),
961
1010
  userFile: Path.join(workingDirectory, "user.json"),
962
- // logs: Path.join(workingDirectory, 'logs'),
1011
+ logs: Path.join(workingDirectory, "logs"),
963
1012
  projects: Path.join(workingDirectory, "projects"),
964
1013
  project: (projectId) => {
965
1014
  return Path.join(pathTo.projects, projectId);
@@ -1011,6 +1060,9 @@ var pathTo = {
1011
1060
  },
1012
1061
  asset: (projectId, id, extension) => {
1013
1062
  return Path.join(pathTo.lfs(projectId), `${id}.${extension}`);
1063
+ },
1064
+ tmpAsset: (id, extension) => {
1065
+ return Path.join(pathTo.tmp, `${id}.${extension}`);
1014
1066
  }
1015
1067
  };
1016
1068
  function assignDefaultIfMissing(value, defaultsTo) {
@@ -1217,10 +1269,13 @@ function slug(string) {
1217
1269
 
1218
1270
  // src/service/AssetService.ts
1219
1271
  var AssetService = class extends AbstractCrudService {
1272
+ // @ts-ignore
1273
+ logService;
1220
1274
  jsonFileService;
1221
1275
  gitService;
1222
- constructor(options, jsonFileService, gitService) {
1276
+ constructor(options, logService, jsonFileService, gitService) {
1223
1277
  super(serviceTypeSchema.Enum.Asset, options);
1278
+ this.logService = logService;
1224
1279
  this.jsonFileService = jsonFileService;
1225
1280
  this.gitService = gitService;
1226
1281
  }
@@ -1237,6 +1292,7 @@ var AssetService = class extends AbstractCrudService {
1237
1292
  const assetFilePath = pathTo.assetFile(props.projectId, id);
1238
1293
  const assetFile = {
1239
1294
  ...props,
1295
+ name: slug(props.name),
1240
1296
  objectType: "asset",
1241
1297
  id,
1242
1298
  created: datetime(),
@@ -1262,14 +1318,48 @@ var AssetService = class extends AbstractCrudService {
1262
1318
  }
1263
1319
  /**
1264
1320
  * Returns an Asset by ID
1321
+ *
1322
+ * If a commit hash is provided, the Asset is read from history
1265
1323
  */
1266
1324
  async read(props) {
1267
1325
  readAssetSchema.parse(props);
1268
- const assetFile = await this.jsonFileService.read(
1269
- pathTo.assetFile(props.projectId, props.id),
1270
- assetFileSchema
1271
- );
1272
- 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);
1273
1363
  }
1274
1364
  /**
1275
1365
  * Updates given Asset
@@ -1284,6 +1374,7 @@ var AssetService = class extends AbstractCrudService {
1284
1374
  const assetFile = {
1285
1375
  ...prevAssetFile,
1286
1376
  ...props,
1377
+ name: slug(props.name),
1287
1378
  updated: datetime()
1288
1379
  };
1289
1380
  if (props.newFilePath) {
@@ -1303,6 +1394,7 @@ var AssetService = class extends AbstractCrudService {
1303
1394
  );
1304
1395
  await Fs2.remove(prevAssetPath);
1305
1396
  await Fs2.copyFile(props.newFilePath, assetPath);
1397
+ await this.gitService.add(projectPath, [prevAssetPath, assetPath]);
1306
1398
  assetFile.extension = fileType.extension;
1307
1399
  assetFile.mimeType = fileType.mimeType;
1308
1400
  assetFile.size = size;
@@ -1378,15 +1470,15 @@ var AssetService = class extends AbstractCrudService {
1378
1470
  * @param projectId The project's ID
1379
1471
  * @param assetFile The AssetFile to convert
1380
1472
  */
1381
- async toAsset(projectId, assetFile) {
1382
- const assetPath = pathTo.asset(
1383
- projectId,
1384
- assetFile.id,
1385
- assetFile.extension
1386
- );
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
+ });
1387
1478
  const asset = {
1388
1479
  ...assetFile,
1389
- absolutePath: assetPath
1480
+ absolutePath: assetPath,
1481
+ history
1390
1482
  };
1391
1483
  return asset;
1392
1484
  }
@@ -1414,6 +1506,12 @@ var AssetService = class extends AbstractCrudService {
1414
1506
  });
1415
1507
  return result;
1416
1508
  }
1509
+ /**
1510
+ * Migrates an potentially outdated Asset file to the current schema
1511
+ */
1512
+ migrate(potentiallyOutdatedAssetFile) {
1513
+ return assetFileSchema.parse(potentiallyOutdatedAssetFile);
1514
+ }
1417
1515
  };
1418
1516
 
1419
1517
  // src/service/CollectionService.ts
@@ -1454,18 +1552,33 @@ var CollectionService = class extends AbstractCrudService {
1454
1552
  );
1455
1553
  await this.gitService.add(projectPath, [collectionFilePath]);
1456
1554
  await this.gitService.commit(projectPath, this.gitMessage.create);
1457
- return collectionFile;
1555
+ return this.toCollection(props.projectId, collectionFile);
1458
1556
  }
1459
1557
  /**
1460
1558
  * Returns a Collection by ID
1559
+ *
1560
+ * If a commit hash is provided, the Collection is read from history
1461
1561
  */
1462
1562
  async read(props) {
1463
1563
  readCollectionSchema.parse(props);
1464
- const collection = await this.jsonFileService.read(
1465
- pathTo.collectionFile(props.projectId, props.id),
1466
- collectionFileSchema
1467
- );
1468
- 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
+ }
1469
1582
  }
1470
1583
  /**
1471
1584
  * Updates given Collection
@@ -1494,7 +1607,7 @@ var CollectionService = class extends AbstractCrudService {
1494
1607
  );
1495
1608
  await this.gitService.add(projectPath, [collectionFilePath]);
1496
1609
  await this.gitService.commit(projectPath, this.gitMessage.update);
1497
- return collectionFile;
1610
+ return this.toCollection(props.projectId, collectionFile);
1498
1611
  }
1499
1612
  /**
1500
1613
  * Deletes given Collection (folder), including it's items
@@ -1550,18 +1663,42 @@ var CollectionService = class extends AbstractCrudService {
1550
1663
  isCollection(obj) {
1551
1664
  return collectionFileSchema.safeParse(obj).success;
1552
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
+ }
1553
1688
  };
1554
1689
 
1555
1690
  // src/service/EntryService.ts
1556
1691
  import Fs4 from "fs-extra";
1557
1692
  var EntryService = class extends AbstractCrudService {
1693
+ logService;
1558
1694
  jsonFileService;
1559
1695
  gitService;
1560
1696
  collectionService;
1561
1697
  assetService;
1562
1698
  // private sharedValueService: SharedValueService;
1563
- constructor(options, jsonFileService, gitService, collectionService, assetService) {
1699
+ constructor(options, logService, jsonFileService, gitService, collectionService, assetService) {
1564
1700
  super(serviceTypeSchema.Enum.Entry, options);
1701
+ this.logService = logService;
1565
1702
  this.jsonFileService = jsonFileService;
1566
1703
  this.gitService = gitService;
1567
1704
  this.collectionService = collectionService;
@@ -1590,11 +1727,11 @@ var EntryService = class extends AbstractCrudService {
1590
1727
  created: datetime(),
1591
1728
  updated: null
1592
1729
  };
1593
- const entry = await this.toEntry({
1594
- projectId: props.projectId,
1595
- collectionId: props.collectionId,
1730
+ const entry = await this.toEntry(
1731
+ props.projectId,
1732
+ props.collectionId,
1596
1733
  entryFile
1597
- });
1734
+ );
1598
1735
  this.validateValues({
1599
1736
  collectionId: props.collectionId,
1600
1737
  fieldDefinitions: collection.fieldDefinitions,
@@ -1610,19 +1747,30 @@ var EntryService = class extends AbstractCrudService {
1610
1747
  return entry;
1611
1748
  }
1612
1749
  /**
1613
- * 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
1614
1753
  */
1615
1754
  async read(props) {
1616
1755
  readEntrySchema.parse(props);
1617
- const entryFile = await this.jsonFileService.read(
1618
- pathTo.entryFile(props.projectId, props.collectionId, props.id),
1619
- entryFileSchema
1620
- );
1621
- return await this.toEntry({
1622
- projectId: props.projectId,
1623
- collectionId: props.collectionId,
1624
- entryFile
1625
- });
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
+ }
1626
1774
  }
1627
1775
  /**
1628
1776
  * Updates an Entry of given Collection with new Values and shared Values
@@ -1649,11 +1797,11 @@ var EntryService = class extends AbstractCrudService {
1649
1797
  values: props.values,
1650
1798
  updated: datetime()
1651
1799
  };
1652
- const entry = await this.toEntry({
1653
- projectId: props.projectId,
1654
- collectionId: props.collectionId,
1800
+ const entry = await this.toEntry(
1801
+ props.projectId,
1802
+ props.collectionId,
1655
1803
  entryFile
1656
- });
1804
+ );
1657
1805
  this.validateValues({
1658
1806
  collectionId: props.collectionId,
1659
1807
  fieldDefinitions: collection.fieldDefinitions,
@@ -1723,6 +1871,12 @@ var EntryService = class extends AbstractCrudService {
1723
1871
  isEntry(obj) {
1724
1872
  return entrySchema.safeParse(obj).success;
1725
1873
  }
1874
+ /**
1875
+ * Migrates an potentially outdated Entry file to the current schema
1876
+ */
1877
+ migrate(potentiallyOutdatedEntryFile) {
1878
+ return entryFileSchema.parse(potentiallyOutdatedEntryFile);
1879
+ }
1726
1880
  /**
1727
1881
  * Returns a Field definition by ID
1728
1882
  */
@@ -1751,14 +1905,16 @@ var EntryService = class extends AbstractCrudService {
1751
1905
  id: value.fieldDefinitionId
1752
1906
  });
1753
1907
  const contentSchema = getValueContentSchemaFromFieldDefinition(fieldDefinition);
1754
- try {
1755
- for (const [_language, content] of Object.entries(value.content)) {
1756
- contentSchema.parse(content);
1908
+ this.logService.debug(
1909
+ "Validating Value against content schema generated from Field definition",
1910
+ {
1911
+ value,
1912
+ contentSchema,
1913
+ fieldDefinition
1757
1914
  }
1758
- } catch (error) {
1759
- console.log("Definition:", fieldDefinition);
1760
- console.log("Value:", value);
1761
- throw error;
1915
+ );
1916
+ for (const [_language, content] of Object.entries(value.content)) {
1917
+ contentSchema.parse(content);
1762
1918
  }
1763
1919
  });
1764
1920
  }
@@ -1828,16 +1984,20 @@ var EntryService = class extends AbstractCrudService {
1828
1984
  /**
1829
1985
  * Creates an Entry from given EntryFile by resolving it's Values
1830
1986
  */
1831
- 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
+ });
1832
1991
  return {
1833
- ...props.entryFile,
1992
+ ...entryFile,
1993
+ history,
1834
1994
  // @ts-ignore @todo fixme - I have no idea why this happens. The types seem to be compatible to me and they work
1835
1995
  values: await Promise.all(
1836
- props.entryFile.values.map(async (value) => {
1996
+ entryFile.values.map(async (value) => {
1837
1997
  if (value.valueType === ValueTypeSchema.Enum.reference) {
1838
1998
  const resolvedContentReferences = await this.resolveValueContentReferences({
1839
- projectId: props.projectId,
1840
- collectionId: props.collectionId,
1999
+ projectId,
2000
+ collectionId,
1841
2001
  valueReference: value
1842
2002
  });
1843
2003
  return {
@@ -1854,11 +2014,10 @@ var EntryService = class extends AbstractCrudService {
1854
2014
 
1855
2015
  // src/service/GitService.ts
1856
2016
  import { GitProcess } from "dugite";
1857
- import { EOL as EOL2 } from "os";
1858
2017
  import PQueue from "p-queue";
2018
+ import Path2 from "path";
1859
2019
 
1860
2020
  // src/service/GitTagService.ts
1861
- import { EOL } from "os";
1862
2021
  var GitTagService = class extends AbstractCrudService {
1863
2022
  git;
1864
2023
  constructor(options, git) {
@@ -1941,7 +2100,7 @@ var GitTagService = class extends AbstractCrudService {
1941
2100
  "--format=%(refname:short)|%(subject)|%(*authorname)|%(*authoremail)|%(*authordate:iso-strict)"
1942
2101
  ];
1943
2102
  const result = await this.git(props.path, args);
1944
- const noEmptyLinesArr = result.stdout.split(EOL).filter((line) => {
2103
+ const noEmptyLinesArr = result.stdout.split("\n").filter((line) => {
1945
2104
  return line.trim() !== "";
1946
2105
  });
1947
2106
  const lineObjArr = noEmptyLinesArr.map((line) => {
@@ -1992,16 +2151,18 @@ var GitService = class {
1992
2151
  version;
1993
2152
  gitPath;
1994
2153
  queue;
2154
+ logService;
1995
2155
  gitTagService;
1996
2156
  userService;
1997
- constructor(options, userService) {
2157
+ constructor(options, logService, userService) {
1998
2158
  this.version = null;
1999
2159
  this.gitPath = null;
2000
2160
  this.queue = new PQueue({
2001
2161
  concurrency: 1
2002
2162
  // No concurrency because git operations are sequencial
2003
2163
  });
2004
- this.gitTagService = new GitTagService(options, this.git);
2164
+ this.gitTagService = new GitTagService(options, this.git.bind(this));
2165
+ this.logService = logService;
2005
2166
  this.userService = userService;
2006
2167
  this.updateVersion();
2007
2168
  this.updateGitPath();
@@ -2027,7 +2188,6 @@ var GitService = class {
2027
2188
  }
2028
2189
  await this.git(path, args);
2029
2190
  await this.setLocalConfig(path);
2030
- await this.installLfs(path);
2031
2191
  }
2032
2192
  /**
2033
2193
  * Clone a repository into a directory
@@ -2064,9 +2224,25 @@ var GitService = class {
2064
2224
  * @param files Files to add
2065
2225
  */
2066
2226
  async add(path, files2) {
2067
- const args = ["add", "--", ...files2];
2227
+ const relativePathsFromRepositoryRoot = files2.map((filePath) => {
2228
+ return filePath.replace(`${path}${Path2.sep}`, "");
2229
+ });
2230
+ const args = ["add", "--", ...relativePathsFromRepositoryRoot];
2068
2231
  await this.git(path, args);
2069
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
+ }
2070
2246
  branches = {
2071
2247
  /**
2072
2248
  * List branches
@@ -2078,7 +2254,7 @@ var GitService = class {
2078
2254
  list: async (path) => {
2079
2255
  const args = ["branch", "--list", "--all"];
2080
2256
  const result = await this.git(path, args);
2081
- const normalizedLinesArr = result.stdout.split(EOL2).filter((line) => {
2257
+ const normalizedLinesArr = result.stdout.split("\n").filter((line) => {
2082
2258
  return line.trim() !== "";
2083
2259
  }).map((line) => {
2084
2260
  return line.trim().replace("* ", "");
@@ -2140,7 +2316,7 @@ var GitService = class {
2140
2316
  list: async (path) => {
2141
2317
  const args = ["remote"];
2142
2318
  const result = await this.git(path, args);
2143
- const normalizedLinesArr = result.stdout.split(EOL2).filter((line) => {
2319
+ const normalizedLinesArr = result.stdout.split("\n").filter((line) => {
2144
2320
  return line.trim() !== "";
2145
2321
  });
2146
2322
  return normalizedLinesArr;
@@ -2167,7 +2343,7 @@ var GitService = class {
2167
2343
  * @param path Path to the repository
2168
2344
  */
2169
2345
  addOrigin: async (path, url) => {
2170
- const args = ["remote", "add", "origin", url];
2346
+ const args = ["remote", "add", "origin", url.trim()];
2171
2347
  await this.git(path, args);
2172
2348
  },
2173
2349
  /**
@@ -2194,7 +2370,7 @@ var GitService = class {
2194
2370
  * @param path Path to the repository
2195
2371
  */
2196
2372
  setOriginUrl: async (path, url) => {
2197
- const args = ["remote", "set-url", "origin", url];
2373
+ const args = ["remote", "set-url", "origin", url.trim()];
2198
2374
  await this.git(path, args);
2199
2375
  }
2200
2376
  };
@@ -2315,11 +2491,12 @@ var GitService = class {
2315
2491
  if (options?.limit) {
2316
2492
  args = [...args, `--max-count=${options.limit}`];
2317
2493
  }
2318
- const result = await this.git(path, [
2319
- ...args,
2320
- "--format=%H|%s|%an|%ae|%aI|%D"
2321
- ]);
2322
- 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) => {
2323
2500
  return line.trim() !== "";
2324
2501
  });
2325
2502
  const lineObjArr = noEmptyLinesArr.map((line) => {
@@ -2337,6 +2514,27 @@ var GitService = class {
2337
2514
  });
2338
2515
  return lineObjArr.filter(this.isGitCommit.bind(this));
2339
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
+ }
2340
2538
  refNameToTagName(refName) {
2341
2539
  const tagName = refName.replace("tag: ", "").trim();
2342
2540
  if (tagName === "" || uuidSchema.safeParse(tagName).success === false) {
@@ -2376,16 +2574,6 @@ var GitService = class {
2376
2574
  async checkBranchOrTagName(path, name) {
2377
2575
  await this.git(path, ["check-ref-format", "--allow-onelevel", name]);
2378
2576
  }
2379
- /**
2380
- * Installs LFS support and starts tracking
2381
- * all files inside the lfs folder
2382
- *
2383
- * @param path Path to the repository
2384
- */
2385
- async installLfs(path) {
2386
- await this.git(path, ["lfs", "install"]);
2387
- await this.git(path, ["lfs", "track", "lfs/*"]);
2388
- }
2389
2577
  /**
2390
2578
  * Sets the git config of given local repository from ElekIoCoreOptions
2391
2579
  *
@@ -2423,31 +2611,37 @@ var GitService = class {
2423
2611
  * @param path Path to the repository
2424
2612
  * @param args Arguments to append after the `git` command
2425
2613
  */
2426
- async git(path, args) {
2427
- const result = await this.queue.add(
2428
- () => GitProcess.exec(args, path, {
2429
- env: {
2430
- // @todo Nasty stuff - remove after update to dugite with git > v2.45.2 once available
2431
- // @see https://github.com/git-lfs/git-lfs/issues/5749
2432
- GIT_CLONE_PROTECTION_ACTIVE: "false"
2433
- }
2434
- })
2435
- );
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
+ });
2436
2624
  if (!result) {
2437
2625
  throw new GitError(
2438
2626
  `Git ${this.version} (${this.gitPath}) command "git ${args.join(
2439
2627
  " "
2440
- )}" failed to return a result`
2628
+ )}" executed for "${path}" failed to return a result`
2441
2629
  );
2442
2630
  }
2443
- 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) {
2444
2638
  throw new GitError(
2445
2639
  `Git ${this.version} (${this.gitPath}) command "git ${args.join(
2446
2640
  " "
2447
- )}" 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()}"`
2448
2642
  );
2449
2643
  }
2450
- return result;
2644
+ return result.gitResult;
2451
2645
  }
2452
2646
  };
2453
2647
 
@@ -2455,8 +2649,10 @@ var GitService = class {
2455
2649
  import Fs5 from "fs-extra";
2456
2650
  var JsonFileService = class extends AbstractCrudService {
2457
2651
  cache = /* @__PURE__ */ new Map();
2458
- constructor(options) {
2652
+ logService;
2653
+ constructor(options, logService) {
2459
2654
  super(serviceTypeSchema.Enum.JsonFile, options);
2655
+ this.logService = logService;
2460
2656
  }
2461
2657
  /**
2462
2658
  * Creates a new file on disk. Fails if path already exists
@@ -2473,7 +2669,8 @@ var JsonFileService = class extends AbstractCrudService {
2473
2669
  flag: "wx",
2474
2670
  encoding: "utf8"
2475
2671
  });
2476
- this.cache.set(path, parsedData);
2672
+ this.options.file.cache === true && this.cache.set(path, parsedData);
2673
+ this.logService.debug(`Created file "${path}"`);
2477
2674
  return parsedData;
2478
2675
  }
2479
2676
  /**
@@ -2484,18 +2681,43 @@ var JsonFileService = class extends AbstractCrudService {
2484
2681
  * @returns Validated content of the file from disk
2485
2682
  */
2486
2683
  async read(path, schema) {
2487
- if (this.cache.has(path)) {
2488
- 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;
2489
2689
  }
2690
+ this.logService.debug(`Cache miss reading file "${path}"`);
2490
2691
  const data = await Fs5.readFile(path, {
2491
2692
  flag: "r",
2492
2693
  encoding: "utf8"
2493
2694
  });
2494
2695
  const json = this.deserialize(data);
2495
2696
  const parsedData = schema.parse(json);
2496
- this.cache.set(path, parsedData);
2697
+ this.options.file.cache === true && this.cache.set(path, parsedData);
2497
2698
  return parsedData;
2498
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
+ }
2499
2721
  /**
2500
2722
  * Overwrites an existing file on disk
2501
2723
  *
@@ -2513,31 +2735,90 @@ var JsonFileService = class extends AbstractCrudService {
2513
2735
  flag: "w",
2514
2736
  encoding: "utf8"
2515
2737
  });
2516
- this.cache.set(path, parsedData);
2738
+ this.options.file.cache === true && this.cache.set(path, parsedData);
2739
+ this.logService.debug(`Updated file "${path}"`);
2517
2740
  return parsedData;
2518
2741
  }
2519
2742
  serialize(data) {
2520
- return JSON.stringify(data, null, this.options.file.json.indentation);
2743
+ return JSON.stringify(data, null, 2);
2521
2744
  }
2522
2745
  deserialize(data) {
2523
2746
  return JSON.parse(data);
2524
2747
  }
2525
2748
  };
2526
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
+
2527
2804
  // src/service/ProjectService.ts
2528
2805
  import Fs6 from "fs-extra";
2529
2806
  import Os2 from "os";
2530
- import Path2 from "path";
2807
+ import Path4 from "path";
2531
2808
  import Semver from "semver";
2532
2809
  var ProjectService = class extends AbstractCrudService {
2810
+ coreVersion;
2811
+ logService;
2533
2812
  jsonFileService;
2534
2813
  userService;
2535
2814
  gitService;
2536
2815
  assetService;
2537
2816
  collectionService;
2538
2817
  entryService;
2539
- constructor(options, jsonFileService, userService, gitService, assetService, collectionService, entryService) {
2818
+ constructor(coreVersion, options, logService, jsonFileService, userService, gitService, assetService, collectionService, entryService) {
2540
2819
  super(serviceTypeSchema.Enum.Project, options);
2820
+ this.coreVersion = coreVersion;
2821
+ this.logService = logService;
2541
2822
  this.jsonFileService = jsonFileService;
2542
2823
  this.userService = userService;
2543
2824
  this.gitService = gitService;
@@ -2569,8 +2850,7 @@ var ProjectService = class extends AbstractCrudService {
2569
2850
  settings: Object.assign({}, defaultSettings, props.settings),
2570
2851
  created: datetime(),
2571
2852
  updated: null,
2572
- coreVersion: this.options.version,
2573
- // @todo should be read from package.json to avoid duplicates
2853
+ coreVersion: this.coreVersion,
2574
2854
  status: "todo",
2575
2855
  version: "0.0.1"
2576
2856
  };
@@ -2599,9 +2879,7 @@ var ProjectService = class extends AbstractCrudService {
2599
2879
  });
2600
2880
  throw error;
2601
2881
  }
2602
- return await this.toProject({
2603
- projectFile
2604
- });
2882
+ return await this.toProject(projectFile);
2605
2883
  }
2606
2884
  /**
2607
2885
  * Clones a Project by URL
@@ -2609,10 +2887,10 @@ var ProjectService = class extends AbstractCrudService {
2609
2887
  async clone(props) {
2610
2888
  cloneProjectSchema.parse(props);
2611
2889
  const tmpId = uuid();
2612
- const tmpProjectPath = Path2.join(pathTo.tmp, tmpId);
2890
+ const tmpProjectPath = Path4.join(pathTo.tmp, tmpId);
2613
2891
  await this.gitService.clone(props.url, tmpProjectPath);
2614
2892
  const projectFile = await this.jsonFileService.read(
2615
- Path2.join(tmpProjectPath, "project.json"),
2893
+ Path4.join(tmpProjectPath, "project.json"),
2616
2894
  projectFileSchema
2617
2895
  );
2618
2896
  const projectPath = pathTo.project(projectFile.id);
@@ -2624,22 +2902,33 @@ var ProjectService = class extends AbstractCrudService {
2624
2902
  }
2625
2903
  await Fs6.copy(tmpProjectPath, projectPath);
2626
2904
  await Fs6.remove(tmpProjectPath);
2627
- return await this.toProject({
2628
- projectFile
2629
- });
2905
+ return await this.toProject(projectFile);
2630
2906
  }
2631
2907
  /**
2632
2908
  * Returns a Project by ID
2909
+ *
2910
+ * If a commit hash is provided, the Project is read from history
2633
2911
  */
2634
2912
  async read(props) {
2635
2913
  readProjectSchema.parse(props);
2636
- const projectFile = await this.jsonFileService.read(
2637
- pathTo.projectFile(props.id),
2638
- projectFileSchema
2639
- );
2640
- return await this.toProject({
2641
- projectFile
2642
- });
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
+ }
2643
2932
  }
2644
2933
  /**
2645
2934
  * Updates given Project
@@ -2653,6 +2942,7 @@ var ProjectService = class extends AbstractCrudService {
2653
2942
  ...prevProjectFile,
2654
2943
  name: props.name || prevProjectFile.name,
2655
2944
  description: props.description || prevProjectFile.description,
2945
+ coreVersion: this.coreVersion,
2656
2946
  settings: {
2657
2947
  language: {
2658
2948
  supported: props.settings?.language.supported || prevProjectFile.settings.language.supported,
@@ -2664,82 +2954,97 @@ var ProjectService = class extends AbstractCrudService {
2664
2954
  await this.jsonFileService.update(projectFile, filePath, projectFileSchema);
2665
2955
  await this.gitService.add(projectPath, [filePath]);
2666
2956
  await this.gitService.commit(projectPath, this.gitMessage.update);
2667
- return await this.toProject({
2668
- projectFile
2669
- });
2957
+ return await this.toProject(projectFile);
2670
2958
  }
2671
2959
  /**
2672
- * Upgrades given Project to the latest version of this client
2673
- *
2674
- * Needed when a new core version is requiring changes to existing files or structure.
2960
+ * Upgrades given Project to the current version of Core
2675
2961
  *
2676
- * @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.
2677
2963
  */
2678
2964
  async upgrade(props) {
2679
2965
  upgradeProjectSchema.parse(props);
2680
- const project = await this.read(props);
2681
- const projectPath = pathTo.project(project.id);
2682
- if (Semver.gt(project.coreVersion, this.options.version)) {
2683
- throw new Error(
2684
- `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}".`
2685
2972
  );
2686
2973
  }
2687
- if (Semver.eq(project.coreVersion, this.options.version)) {
2688
- 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
+ );
2689
2978
  }
2690
- const upgradeFiles = await files(
2691
- Path2.resolve(__dirname, "../upgrade"),
2692
- "ts"
2979
+ const assetReferences = await this.listReferences("asset", props.id);
2980
+ const collectionReferences = await this.listReferences(
2981
+ "collection",
2982
+ props.id
2693
2983
  );
2694
- const upgrades = (await Promise.all(
2695
- upgradeFiles.map((file) => {
2696
- return import(Path2.join("../upgrade", file.name));
2697
- })
2698
- )).map((upgradeImport) => {
2699
- return upgradeImport.default;
2700
- });
2701
- const sortedUpgrades = upgrades.sort((a, b) => {
2702
- return Semver.compare(a.to, b.to);
2703
- }).filter((upgrade) => {
2704
- if (upgrade.to !== "0.0.0") {
2705
- return upgrade;
2706
- }
2707
- 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}`
2708
2990
  });
2709
- for (let index = 0; index < sortedUpgrades.length; index++) {
2710
- const upgrade = sortedUpgrades[index];
2711
- if (!upgrade) {
2712
- throw new Error("Expected ProjectUpgrade but got undefined");
2713
- }
2714
- 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({
2715
3028
  path: projectPath,
2716
- message: `Attempting to upgrade Project to Core version "${upgrade.to}"`
3029
+ message: `Upgraded Project to Core version "${this.coreVersion}"`
2717
3030
  });
2718
- try {
2719
- await upgrade.run(project);
2720
- project.coreVersion = upgrade.to;
2721
- await this.update(project);
2722
- await this.gitService.tags.create({
2723
- path: projectPath,
2724
- message: `Upgraded Project to Core version "${upgrade.to}"`
2725
- });
2726
- await this.gitService.tags.delete({
2727
- path: projectPath,
2728
- id: failsafeTag.id
2729
- });
2730
- } catch (error) {
2731
- await this.gitService.reset(projectPath, "hard", failsafeTag.id);
2732
- throw new ProjectUpgradeError(
2733
- `Failed to upgrade Project to Core version "${upgrade.to}"`
2734
- );
2735
- }
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;
2736
3038
  }
2737
3039
  }
2738
3040
  branches = {
2739
3041
  list: async (props) => {
2740
3042
  listBranchesProjectSchema.parse(props);
2741
3043
  const projectPath = pathTo.project(props.id);
2742
- await this.gitService.fetch(projectPath);
3044
+ const hasOrigin = await this.gitService.remotes.hasOrigin(projectPath);
3045
+ if (hasOrigin) {
3046
+ await this.gitService.fetch(projectPath);
3047
+ }
2743
3048
  return await this.gitService.branches.list(projectPath);
2744
3049
  },
2745
3050
  current: async (props) => {
@@ -2761,7 +3066,12 @@ var ProjectService = class extends AbstractCrudService {
2761
3066
  getOriginUrl: async (props) => {
2762
3067
  getRemoteOriginUrlProjectSchema.parse(props);
2763
3068
  const projectPath = pathTo.project(props.id);
2764
- 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
+ }
2765
3075
  },
2766
3076
  setOriginUrl: async (props) => {
2767
3077
  setRemoteOriginUrlProjectSchema.parse(props);
@@ -2818,6 +3128,27 @@ var ProjectService = class extends AbstractCrudService {
2818
3128
  deleteProjectSchema.parse(props);
2819
3129
  await Fs6.remove(pathTo.project(props.id));
2820
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
+ }
2821
3152
  async list(props) {
2822
3153
  if (props) {
2823
3154
  listProjectsSchema.parse(props);
@@ -2880,11 +3211,25 @@ var ProjectService = class extends AbstractCrudService {
2880
3211
  };
2881
3212
  }
2882
3213
  /**
2883
- * 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
2884
3221
  */
2885
- 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
+ });
2886
3229
  return {
2887
- ...props.projectFile
3230
+ ...projectFile,
3231
+ history,
3232
+ fullHistory
2888
3233
  };
2889
3234
  }
2890
3235
  /**
@@ -2896,8 +3241,8 @@ var ProjectService = class extends AbstractCrudService {
2896
3241
  const folders2 = Object.values(projectFolderSchema.Values);
2897
3242
  await Promise.all(
2898
3243
  folders2.map(async (folder) => {
2899
- await Fs6.mkdirp(Path2.join(path, folder));
2900
- 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"), "");
2901
3246
  })
2902
3247
  );
2903
3248
  }
@@ -2921,14 +3266,80 @@ var ProjectService = class extends AbstractCrudService {
2921
3266
  // projectFolderSchema.Enum.public + '/',
2922
3267
  // projectFolderSchema.Enum.logs + '/',
2923
3268
  ];
2924
- 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
+ }
2925
3334
  }
2926
3335
  };
2927
3336
 
2928
3337
  // src/service/UserService.ts
2929
3338
  var UserService = class {
3339
+ logService;
2930
3340
  jsonFileService;
2931
- constructor(jsonFileService) {
3341
+ constructor(logService, jsonFileService) {
3342
+ this.logService = logService;
2932
3343
  this.jsonFileService = jsonFileService;
2933
3344
  }
2934
3345
  /**
@@ -2938,6 +3349,7 @@ var UserService = class {
2938
3349
  try {
2939
3350
  return await this.jsonFileService.read(pathTo.userFile, userFileSchema);
2940
3351
  } catch (error) {
3352
+ this.logService.info("No User found");
2941
3353
  return void 0;
2942
3354
  }
2943
3355
  }
@@ -2955,13 +3367,16 @@ var UserService = class {
2955
3367
  if (userFile.userType === UserTypeSchema.Enum.cloud) {
2956
3368
  }
2957
3369
  await this.jsonFileService.update(userFile, userFilePath, userFileSchema);
3370
+ this.logService.debug("Updated User");
2958
3371
  return userFile;
2959
3372
  }
2960
3373
  };
2961
3374
 
2962
3375
  // src/index.node.ts
2963
3376
  var ElekIoCore = class {
3377
+ coreVersion;
2964
3378
  options;
3379
+ logService;
2965
3380
  userService;
2966
3381
  gitService;
2967
3382
  jsonFileService;
@@ -2969,24 +3384,29 @@ var ElekIoCore = class {
2969
3384
  projectService;
2970
3385
  collectionService;
2971
3386
  entryService;
2972
- // private readonly sharedValueService: SharedValueService;
2973
3387
  constructor(props) {
3388
+ this.coreVersion = version;
2974
3389
  const parsedProps = constructorElekIoCoreSchema.parse(props);
2975
3390
  const defaults = {
2976
- environment: "production",
2977
- version: "0.0.0",
3391
+ log: {
3392
+ level: "info"
3393
+ },
2978
3394
  file: {
2979
- json: {
2980
- indentation: 2
2981
- }
3395
+ cache: true
2982
3396
  }
2983
3397
  };
2984
3398
  this.options = Object.assign({}, defaults, parsedProps);
2985
- this.jsonFileService = new JsonFileService(this.options);
2986
- this.userService = new UserService(this.jsonFileService);
2987
- 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
+ );
2988
3407
  this.assetService = new AssetService(
2989
3408
  this.options,
3409
+ this.logService,
2990
3410
  this.jsonFileService,
2991
3411
  this.gitService
2992
3412
  );
@@ -2997,14 +3417,16 @@ var ElekIoCore = class {
2997
3417
  );
2998
3418
  this.entryService = new EntryService(
2999
3419
  this.options,
3420
+ this.logService,
3000
3421
  this.jsonFileService,
3001
3422
  this.gitService,
3002
3423
  this.collectionService,
3003
3424
  this.assetService
3004
- // this.sharedValueService
3005
3425
  );
3006
3426
  this.projectService = new ProjectService(
3427
+ this.coreVersion,
3007
3428
  this.options,
3429
+ this.logService,
3008
3430
  this.jsonFileService,
3009
3431
  this.userService,
3010
3432
  this.gitService,
@@ -3012,18 +3434,19 @@ var ElekIoCore = class {
3012
3434
  this.collectionService,
3013
3435
  this.entryService
3014
3436
  );
3015
- if (this.options.environment !== "production") {
3016
- console.info(
3017
- `Initializing inside an "${this.options.environment}" environment`,
3018
- {
3019
- ...this.options
3020
- }
3021
- );
3022
- }
3437
+ this.logService.info(`Initializing elek.io Core ${this.coreVersion}`, {
3438
+ options: this.options
3439
+ });
3023
3440
  Fs7.mkdirpSync(pathTo.projects);
3024
3441
  Fs7.mkdirpSync(pathTo.tmp);
3025
3442
  Fs7.emptyDirSync(pathTo.tmp);
3026
3443
  }
3444
+ /**
3445
+ * Exposes the logger
3446
+ */
3447
+ get logger() {
3448
+ return this.logService;
3449
+ }
3027
3450
  /**
3028
3451
  * Utility / helper functions
3029
3452
  */
@@ -3066,12 +3489,6 @@ var ElekIoCore = class {
3066
3489
  get entries() {
3067
3490
  return this.entryService;
3068
3491
  }
3069
- /**
3070
- * CRUD methods to work with Values
3071
- */
3072
- // public get sharedValues(): SharedValueService {
3073
- // return this.sharedValueService;
3074
- // }
3075
3492
  };
3076
3493
  export {
3077
3494
  BooleanFieldDefinitionBaseSchema,
@@ -3126,7 +3543,6 @@ export {
3126
3543
  entryFieldDefinitionSchema,
3127
3544
  entryFileSchema,
3128
3545
  entrySchema,
3129
- environmentSchema,
3130
3546
  fieldDefinitionSchema,
3131
3547
  fileReferenceSchema,
3132
3548
  getChangesProjectSchema,
@@ -3149,8 +3565,10 @@ export {
3149
3565
  listGitTagsSchema,
3150
3566
  listProjectsSchema,
3151
3567
  localUserSchema,
3568
+ logLevelSchema,
3152
3569
  numberFieldDefinitionSchema,
3153
3570
  objectTypeSchema,
3571
+ outdatedProjectSchema,
3154
3572
  projectExportSchema,
3155
3573
  projectFileSchema,
3156
3574
  projectFolderSchema,
@@ -3168,6 +3586,7 @@ export {
3168
3586
  resolvedReferencedValueSchema,
3169
3587
  resolvedValueContentReferenceSchema,
3170
3588
  resolvedValueSchema,
3589
+ saveAssetSchema,
3171
3590
  searchProjectSchema,
3172
3591
  serviceTypeSchema,
3173
3592
  setRemoteOriginUrlProjectSchema,