@elek-io/core 0.15.2 → 0.16.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.
@@ -1,10 +1,10 @@
1
1
  #! /usr/bin/env node
2
2
  import { Command } from "@commander-js/extra-typings";
3
- import Path from "path";
3
+ import Path from "node:path";
4
4
  import Fs from "fs-extra";
5
5
  import { build } from "tsdown";
6
6
  import CodeBlockWriter from "code-block-writer";
7
- import assert from "assert";
7
+ import assert from "node:assert";
8
8
  import chokidar from "chokidar";
9
9
  import { serve } from "@hono/node-server";
10
10
  import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
@@ -14,25 +14,30 @@ import { cors } from "hono/cors";
14
14
  import { trimTrailingSlash } from "hono/trailing-slash";
15
15
  import { z as z$1 } from "zod";
16
16
  import { Scalar } from "@scalar/hono-api-reference";
17
- import Os from "os";
18
- import { execFile } from "child_process";
17
+ import Os from "node:os";
18
+ import { execFile } from "node:child_process";
19
19
  import mime from "mime";
20
20
  import slugify from "@sindresorhus/slugify";
21
21
  import { v4 } from "uuid";
22
- import { GitProcess } from "dugite";
22
+ import { exec } from "dugite";
23
23
  import PQueue from "p-queue";
24
24
  import { createLogger, format, transports } from "winston";
25
25
  import DailyRotateFile from "winston-daily-rotate-file";
26
26
  import Semver from "semver";
27
27
 
28
- //#region rolldown:runtime
28
+ //#region \0rolldown/runtime.js
29
29
  var __defProp = Object.defineProperty;
30
- var __export = (all) => {
30
+ var __exportAll = (all, no_symbols) => {
31
31
  let target = {};
32
- for (var name$1 in all) __defProp(target, name$1, {
33
- get: all[name$1],
34
- enumerable: true
35
- });
32
+ for (var name in all) {
33
+ __defProp(target, name, {
34
+ get: all[name],
35
+ enumerable: true
36
+ });
37
+ }
38
+ if (!no_symbols) {
39
+ __defProp(target, Symbol.toStringTag, { value: "Module" });
40
+ }
36
41
  return target;
37
42
  };
38
43
 
@@ -40,24 +45,34 @@ var __export = (all) => {
40
45
  //#region package.json
41
46
  var package_default = {
42
47
  name: "@elek-io/core",
43
- version: "0.15.2",
48
+ version: "0.16.0",
44
49
  description: "Handles core functionality of elek.io Projects like file IO and version control.",
45
50
  homepage: "https://elek.io",
46
51
  repository: "https://github.com/elek-io/core",
47
52
  bugs: { "url": "https://github.com/elek-io/core/issues" },
48
53
  type: "module",
49
- bin: { "elek": "./dist/cli/index.cli.js" },
50
- files: ["dist/node", "dist/browser"],
51
- exports: { ".": {
52
- "node": { "import": {
53
- "types": "./dist/node/index.node.d.ts",
54
- "default": "./dist/node/index.node.js"
55
- } },
56
- "import": {
57
- "types": "./dist/browser/index.browser.d.ts",
58
- "default": "./dist/browser/index.browser.js"
59
- }
60
- } },
54
+ bin: { "elek": "./dist/cli/index.cli.mjs" },
55
+ files: [
56
+ "dist/node",
57
+ "dist/browser",
58
+ "dist/astro"
59
+ ],
60
+ exports: {
61
+ ".": {
62
+ "node": { "import": {
63
+ "types": "./dist/node/index.node.d.mts",
64
+ "default": "./dist/node/index.node.mjs"
65
+ } },
66
+ "import": {
67
+ "types": "./dist/browser/index.browser.d.ts",
68
+ "default": "./dist/browser/index.browser.js"
69
+ }
70
+ },
71
+ "./astro": { "import": {
72
+ "types": "./dist/astro/index.astro.d.mts",
73
+ "default": "./dist/astro/index.astro.mjs"
74
+ } }
75
+ },
61
76
  pnpm: { "overrides": {} },
62
77
  scripts: {
63
78
  "lint": "eslint",
@@ -72,105 +87,222 @@ var package_default = {
72
87
  },
73
88
  dependencies: {
74
89
  "@commander-js/extra-typings": "14.0.0",
75
- "@hono/node-server": "1.19.5",
76
- "@hono/zod-openapi": "1.1.4",
77
- "@scalar/hono-api-reference": "0.9.22",
90
+ "@hono/node-server": "1.19.11",
91
+ "@hono/zod-openapi": "1.2.2",
92
+ "@scalar/hono-api-reference": "0.10.0",
78
93
  "@sindresorhus/slugify": "3.0.0",
79
- "chokidar": "4.0.3",
94
+ "chokidar": "5.0.0",
80
95
  "code-block-writer": "13.0.3",
81
- "commander": "14.0.2",
82
- "fs-extra": "11.3.2",
83
- "hono": "4.10.4",
96
+ "commander": "14.0.3",
97
+ "fs-extra": "11.3.4",
98
+ "hono": "4.12.5",
84
99
  "mime": "4.1.0",
85
- "p-queue": "9.0.0",
86
- "semver": "7.7.3",
87
- "tsdown": "0.15.12",
100
+ "p-queue": "9.1.0",
101
+ "semver": "7.7.4",
102
+ "tsdown": "0.21.0",
88
103
  "uuid": "13.0.0",
89
- "winston": "3.18.3",
104
+ "winston": "3.19.0",
90
105
  "winston-daily-rotate-file": "5.0.0",
91
- "zod": "4.1.12"
106
+ "zod": "4.3.6"
92
107
  },
93
108
  devDependencies: {
94
- "@changesets/cli": "2.29.7",
95
- "@eslint/js": "9.38.0",
96
- "@faker-js/faker": "10.1.0",
97
- "@tsconfig/node22": "22.0.2",
98
- "@tsconfig/strictest": "2.0.7",
109
+ "@changesets/cli": "2.30.0",
110
+ "@eslint/js": "10.0.1",
111
+ "@faker-js/faker": "10.3.0",
112
+ "@tsconfig/node24": "24.0.4",
113
+ "@tsconfig/strictest": "2.0.8",
99
114
  "@types/fs-extra": "11.0.4",
100
- "@types/node": "22.18.13",
115
+ "@types/node": "24.12.0",
101
116
  "@types/semver": "7.7.1",
102
- "@vitest/coverage-v8": "4.0.5",
103
- "eslint": "9.38.0",
117
+ "@vitest/coverage-v8": "4.0.18",
118
+ "astro": "5.18.0",
119
+ "eslint": "10.0.3",
104
120
  "eslint-config-prettier": "10.1.8",
105
- "globals": "16.4.0",
121
+ "globals": "17.4.0",
106
122
  "jiti": "2.6.1",
107
- "prettier": "3.6.2",
123
+ "prettier": "3.8.1",
108
124
  "typescript": "5.9.3",
109
- "typescript-eslint": "8.46.2",
110
- "vitest": "4.0.5"
125
+ "typescript-eslint": "8.56.1",
126
+ "vitest": "4.0.18"
127
+ },
128
+ peerDependencies: {
129
+ "astro": ">=5.0.0",
130
+ "dugite": "3.2.0"
111
131
  },
112
- peerDependencies: { "dugite": "2.7.1" }
132
+ peerDependenciesMeta: { "astro": { "optional": true } }
113
133
  };
114
134
 
115
135
  //#endregion
116
136
  //#region src/cli/exportAction.ts
117
- async function exportProjects({ outDir, projects, options }) {
118
- const projectsToExport = [];
119
- const resolvedOutDir = Path.resolve(outDir);
120
- await Fs.ensureDir(resolvedOutDir);
121
- let content = {};
122
- if (projects === "all") projectsToExport.push(...(await core.projects.list({ limit: 0 })).list);
123
- else for (const projectId of projects) projectsToExport.push(await core.projects.read({ id: projectId }));
137
+ async function exportFile({ resolvedOutDir, name, content }) {
138
+ await Fs.writeFile(Path.join(resolvedOutDir, `${name}.json`), JSON.stringify(content, null, 2));
139
+ }
140
+ async function exportProjectNested({ projectToExport }) {
141
+ const assets = (await core.assets.list({
142
+ projectId: projectToExport.id,
143
+ limit: 0
144
+ })).list;
145
+ let assetContent = {};
146
+ for (const asset of assets) assetContent = {
147
+ ...assetContent,
148
+ [asset.id]: { ...asset }
149
+ };
150
+ let collectionContent = {};
151
+ const collections = (await core.collections.list({
152
+ projectId: projectToExport.id,
153
+ limit: 0
154
+ })).list;
155
+ for (const collection of collections) {
156
+ let entryContent = {};
157
+ const entries = (await core.entries.list({
158
+ projectId: projectToExport.id,
159
+ collectionId: collection.id,
160
+ limit: 0
161
+ })).list;
162
+ for (const entry of entries) entryContent = {
163
+ ...entryContent,
164
+ [entry.id]: { ...entry }
165
+ };
166
+ collectionContent = {
167
+ ...collectionContent,
168
+ [collection.id]: {
169
+ ...collection,
170
+ entries: entryContent
171
+ }
172
+ };
173
+ }
174
+ return {
175
+ ...projectToExport,
176
+ assets: assetContent,
177
+ collections: collectionContent
178
+ };
179
+ }
180
+ async function exportProjectsNested({ resolvedOutDir, projectsToExport, options }) {
181
+ if (projectsToExport.length === 1) {
182
+ const projectToExport = projectsToExport[0];
183
+ const project = await exportProjectNested({
184
+ resolvedOutDir,
185
+ projectToExport,
186
+ options
187
+ });
188
+ await exportFile({
189
+ resolvedOutDir,
190
+ options,
191
+ name: `project-${projectToExport.id}`,
192
+ content: project
193
+ });
194
+ } else {
195
+ let projects = {};
196
+ for (const project of projectsToExport) projects = {
197
+ ...projects,
198
+ [project.id]: await exportProjectNested({
199
+ resolvedOutDir,
200
+ projectToExport: project,
201
+ options
202
+ })
203
+ };
204
+ await exportFile({
205
+ resolvedOutDir,
206
+ options,
207
+ name: "projects",
208
+ content: projects
209
+ });
210
+ }
211
+ }
212
+ async function exportProjectsSeparate({ resolvedOutDir, projectsToExport, options }) {
124
213
  for (const project of projectsToExport) {
125
- const assets = (await core.assets.list({
214
+ const projectOutDir = Path.join(resolvedOutDir, `project-${project.id}`);
215
+ await Fs.ensureDir(projectOutDir);
216
+ await exportFile({
217
+ resolvedOutDir: projectOutDir,
218
+ options,
219
+ name: `project`,
220
+ content: project
221
+ });
222
+ const tmpAssets = (await core.assets.list({
126
223
  projectId: project.id,
127
224
  limit: 0
128
225
  })).list;
129
- let assetContent = {};
130
- for (const asset of assets) assetContent = {
131
- ...assetContent,
132
- [asset.id]: { ...asset }
133
- };
134
- let collectionContent = {};
226
+ const assets = [];
227
+ const assetOutDir = Path.join(projectOutDir, "assets");
228
+ await Fs.ensureDir(assetOutDir);
229
+ for (const asset of tmpAssets) {
230
+ const assetDestination = Path.join(assetOutDir, `${asset.id}.${asset.extension}`);
231
+ await Fs.copyFile(asset.absolutePath, assetDestination);
232
+ assets.push({
233
+ ...asset,
234
+ absolutePath: assetDestination
235
+ });
236
+ }
237
+ await exportFile({
238
+ resolvedOutDir: assetOutDir,
239
+ options,
240
+ name: `assets`,
241
+ content: assets
242
+ });
135
243
  const collections = (await core.collections.list({
136
244
  projectId: project.id,
137
245
  limit: 0
138
246
  })).list;
247
+ const collectionsOutDir = Path.join(projectOutDir, "collections");
248
+ await Fs.ensureDir(collectionsOutDir);
139
249
  for (const collection of collections) {
140
- let entryContent = {};
250
+ const collectionOutDir = Path.join(collectionsOutDir, collection.slug.plural);
251
+ await Fs.ensureDir(collectionOutDir);
252
+ await exportFile({
253
+ resolvedOutDir: collectionOutDir,
254
+ options,
255
+ name: `collection`,
256
+ content: collection
257
+ });
141
258
  const entries = (await core.entries.list({
142
259
  projectId: project.id,
143
260
  collectionId: collection.id,
144
261
  limit: 0
145
262
  })).list;
146
- for (const entry of entries) entryContent = {
147
- ...entryContent,
148
- [entry.id]: { ...entry }
149
- };
150
- collectionContent = {
151
- ...collectionContent,
152
- [collection.id]: {
153
- ...collection,
154
- entries: entryContent
155
- }
156
- };
263
+ await exportFile({
264
+ resolvedOutDir: collectionOutDir,
265
+ options,
266
+ name: `entries`,
267
+ content: entries
268
+ });
157
269
  }
158
- content = {
159
- ...content,
160
- [project.id]: {
161
- ...project,
162
- assets: assetContent,
163
- collections: collectionContent
164
- }
165
- };
270
+ await exportFile({
271
+ resolvedOutDir: collectionsOutDir,
272
+ options,
273
+ name: `collections`,
274
+ content: collections
275
+ });
276
+ }
277
+ }
278
+ async function exportProjects({ outDir, projects, template, options }) {
279
+ const projectsToExport = [];
280
+ const resolvedOutDir = Path.resolve(outDir);
281
+ await Fs.ensureDir(resolvedOutDir);
282
+ if (projects === "all") projectsToExport.push(...(await core.projects.list({ limit: 0 })).list);
283
+ else for (const projectId of projects) projectsToExport.push(await core.projects.read({ id: projectId }));
284
+ switch (template) {
285
+ case "nested":
286
+ await exportProjectsNested({
287
+ resolvedOutDir,
288
+ projectsToExport,
289
+ options
290
+ });
291
+ break;
292
+ case "separate":
293
+ await exportProjectsSeparate({
294
+ resolvedOutDir,
295
+ projectsToExport,
296
+ options
297
+ });
298
+ break;
166
299
  }
167
- if (options.separate === true) for (const project of projectsToExport) await Fs.writeFile(Path.join(resolvedOutDir, `project-${project.id}.json`), JSON.stringify(content[project.id], null, 2));
168
- else await Fs.writeFile(Path.join(resolvedOutDir, "projects.json"), JSON.stringify(content, null, 2));
169
300
  }
170
- const exportAction = async ({ outDir, projects, options }) => {
301
+ const exportAction = async ({ outDir, projects, template, options }) => {
171
302
  await exportProjects({
172
303
  outDir,
173
304
  projects,
305
+ template,
174
306
  options
175
307
  });
176
308
  if (options.watch === true) {
@@ -186,6 +318,7 @@ const exportAction = async ({ outDir, projects, options }) => {
186
318
  exportProjects({
187
319
  outDir,
188
320
  projects,
321
+ template,
189
322
  options
190
323
  });
191
324
  });
@@ -326,7 +459,7 @@ function writeFetch(writer, to, method = "GET") {
326
459
  }).write(`);`).newLine();
327
460
  writer.writeLine(`const entries = await response.json();`);
328
461
  }
329
- async function generateApiClientAs({ outDir, language, format: format$1, target }) {
462
+ async function generateApiClientAs({ outDir, language, format, target }) {
330
463
  const resolvedOutDir = Path.resolve(outDir);
331
464
  await Fs.ensureDir(resolvedOutDir);
332
465
  const outFileTs = Path.join(resolvedOutDir, "client.ts");
@@ -337,8 +470,9 @@ async function generateApiClientAs({ outDir, language, format: format$1, target
337
470
  external: ["@elek-io/core", "zod"],
338
471
  entry: outFileTs.split(Path.sep).join(Path.posix.sep),
339
472
  outDir: resolvedOutDir,
340
- format: format$1,
473
+ format,
341
474
  target,
475
+ platform: "neutral",
342
476
  sourcemap: true,
343
477
  clean: false,
344
478
  dts: true,
@@ -347,11 +481,11 @@ async function generateApiClientAs({ outDir, language, format: format$1, target
347
481
  await Fs.remove(outFileTs);
348
482
  }
349
483
  }
350
- const generateApiClientAction = async ({ outDir, language, format: format$1, target, options }) => {
484
+ const generateApiClientAction = async ({ outDir, language, format, target, options }) => {
351
485
  await generateApiClientAs({
352
486
  outDir,
353
487
  language,
354
- format: format$1,
488
+ format,
355
489
  target,
356
490
  options
357
491
  });
@@ -368,7 +502,7 @@ const generateApiClientAction = async ({ outDir, language, format: format$1, tar
368
502
  generateApiClientAs({
369
503
  outDir,
370
504
  language,
371
- format: format$1,
505
+ format,
372
506
  target,
373
507
  options
374
508
  });
@@ -383,10 +517,10 @@ const generateApiClientAction = async ({ outDir, language, format: format$1, tar
383
517
  */
384
518
  const requestResponseLogger = createMiddleware(async (c, next) => {
385
519
  const { method, url } = c.req;
386
- const requestId$1 = c.get("requestId");
520
+ const requestId = c.get("requestId");
387
521
  c.var.logService.info({
388
522
  source: "core",
389
- message: `Recieved API request "${method} ${url}" with requestId ${requestId$1}`
523
+ message: `Recieved API request "${method} ${url}" with requestId ${requestId}`
390
524
  });
391
525
  const start = Date.now();
392
526
  await next();
@@ -394,7 +528,7 @@ const requestResponseLogger = createMiddleware(async (c, next) => {
394
528
  const statusCode = c.res.status.toString();
395
529
  const resultLog = {
396
530
  source: "core",
397
- message: `Response for API request "${method} ${url}" with requestId ${requestId$1} and status code ${statusCode} in ${durationMs}ms`
531
+ message: `Response for API request "${method} ${url}" with requestId ${requestId} and status code ${statusCode} in ${durationMs}ms`
398
532
  };
399
533
  if (statusCode.startsWith("2")) c.var.logService.info(resultLog);
400
534
  else if (statusCode.startsWith("3")) c.var.logService.warn(resultLog);
@@ -504,19 +638,19 @@ const logLevelSchema = z.enum([
504
638
  "debug"
505
639
  ]);
506
640
  const versionSchema = z.string();
507
- const uuidSchema = z.uuid("shared.invalidUuid");
641
+ const uuidSchema = z.uuid();
508
642
  /**
509
643
  * A record that can be used to translate a string value into all supported languages
510
644
  */
511
- const translatableStringSchema = z.partialRecord(supportedLanguageSchema, z.string().trim().min(1, "shared.translatableStringRequired"));
645
+ const translatableStringSchema = z.partialRecord(supportedLanguageSchema, z.string().trim().min(1));
512
646
  /**
513
647
  * A record that can be used to translate a number value into all supported languages
514
648
  */
515
- const translatableNumberSchema = z.partialRecord(supportedLanguageSchema, z.number({ error: (error) => error.input === void 0 ? "shared.translatableNumberRequired" : "shared.translatableNumberNotANumber" }));
649
+ const translatableNumberSchema = z.partialRecord(supportedLanguageSchema, z.number());
516
650
  /**
517
651
  * A record that can be used to translate a boolean value into all supported languages
518
652
  */
519
- const translatableBooleanSchema = z.partialRecord(supportedLanguageSchema, z.boolean({ error: (error) => error.input === void 0 ? "shared.translatableBooleanRequired" : "shared.translatableBooleanNotABoolean" }));
653
+ const translatableBooleanSchema = z.partialRecord(supportedLanguageSchema, z.boolean());
520
654
  function translatableArrayOf(schema) {
521
655
  return z.partialRecord(supportedLanguageSchema, z.array(schema));
522
656
  }
@@ -951,8 +1085,8 @@ const projectBranchSchema = z.enum(["production", "work"]);
951
1085
  const projectFileSchema = baseFileSchema.extend({
952
1086
  objectType: z.literal(objectTypeSchema.enum.project).readonly(),
953
1087
  coreVersion: versionSchema,
954
- name: z.string().trim().min(1, "shared.projectNameRequired"),
955
- description: z.string().trim().min(1, "shared.projectDescriptionRequired"),
1088
+ name: z.string().trim().min(1),
1089
+ description: z.string().trim().min(1),
956
1090
  version: versionSchema,
957
1091
  status: projectStatusSchema,
958
1092
  settings: projectSettingsSchema
@@ -962,11 +1096,10 @@ const projectSchema = projectFileSchema.extend({
962
1096
  history: z.array(gitCommitSchema).openapi({ description: "Commit history of this Project" }),
963
1097
  fullHistory: z.array(gitCommitSchema).openapi({ description: "Full commit history of this Project including all Assets, Collections, Entries and other files" })
964
1098
  }).openapi("Project");
965
- const outdatedProjectSchema = projectFileSchema.pick({
1099
+ const migrateProjectSchema = projectFileSchema.pick({
966
1100
  id: true,
967
- name: true,
968
1101
  coreVersion: true
969
- });
1102
+ }).loose();
970
1103
  const projectExportSchema = projectSchema.extend({
971
1104
  assets: z.array(assetExportSchema),
972
1105
  collections: z.array(collectionExportSchema)
@@ -975,9 +1108,6 @@ const createProjectSchema = projectSchema.pick({
975
1108
  name: true,
976
1109
  description: true,
977
1110
  settings: true
978
- }).partial({
979
- description: true,
980
- settings: true
981
1111
  });
982
1112
  const readProjectSchema = z.object({
983
1113
  id: uuidSchema.readonly(),
@@ -988,10 +1118,6 @@ const updateProjectSchema = projectSchema.pick({
988
1118
  name: true,
989
1119
  description: true,
990
1120
  settings: true
991
- }).partial({
992
- name: true,
993
- description: true,
994
- settings: true
995
1121
  });
996
1122
  const upgradeProjectSchema = z.object({
997
1123
  id: uuidSchema.readonly(),
@@ -1030,6 +1156,12 @@ const searchProjectSchema = z.object({
1030
1156
  //#endregion
1031
1157
  //#region src/schema/schemaFromFieldDefinition.ts
1032
1158
  /**
1159
+ * Dynamic zod schema generation
1160
+ *
1161
+ * Altough everything is already strictly typed, a type of string might not be an email or text of a certain length.
1162
+ * To validate this, we need to generate zod schemas based on Field definitions the user created.
1163
+ */
1164
+ /**
1033
1165
  * Boolean Values are always either true or false, so we don't need the Field definition here
1034
1166
  */
1035
1167
  function getBooleanValueContentSchemaFromFieldDefinition() {
@@ -1081,7 +1213,7 @@ function getStringValueContentSchemaFromFieldDefinition(fieldDefinition) {
1081
1213
  if ("min" in fieldDefinition && fieldDefinition.min) schema = schema.min(fieldDefinition.min);
1082
1214
  if ("max" in fieldDefinition && fieldDefinition.max) schema = schema.max(fieldDefinition.max);
1083
1215
  if (fieldDefinition.isRequired === false) return schema.nullable();
1084
- return schema.min(1, "shared.stringValueRequired");
1216
+ return schema.min(1);
1085
1217
  }
1086
1218
  /**
1087
1219
  * Reference Values can reference either Assets or Entries (or Shared Values in the future)
@@ -1097,7 +1229,7 @@ function getReferenceValueContentSchemaFromFieldDefinition(fieldDefinition) {
1097
1229
  schema = z.array(valueContentReferenceToEntrySchema);
1098
1230
  break;
1099
1231
  }
1100
- if (fieldDefinition.isRequired) schema = schema.min(1, "shared.referenceRequired");
1232
+ if (fieldDefinition.isRequired) schema = schema.min(1);
1101
1233
  if (fieldDefinition.min) schema = schema.min(fieldDefinition.min);
1102
1234
  if (fieldDefinition.max) schema = schema.max(fieldDefinition.max);
1103
1235
  return schema;
@@ -1230,7 +1362,7 @@ const projectsSchema = z$1.string().default("all").transform((value) => {
1230
1362
  return value.split(",").map((v) => uuidSchema.parse(v.trim()));
1231
1363
  });
1232
1364
  const generateApiClientOptionsSchema = z$1.object({ watch: z$1.boolean().default(false) });
1233
- const exportProjectsOptionsSchema = generateApiClientOptionsSchema.extend({ separate: z$1.boolean().default(false) });
1365
+ const exportProjectsOptionsSchema = generateApiClientOptionsSchema.extend({ watch: z$1.boolean().default(false) });
1234
1366
  const generateApiClientSchema = z$1.object({
1235
1367
  outDir: outDirSchema,
1236
1368
  language: languageSchema,
@@ -1254,6 +1386,7 @@ const apiStartSchema = z$1.object({ port: portSchema });
1254
1386
  const exportSchema = z$1.object({
1255
1387
  outDir: outDirSchema,
1256
1388
  projects: projectsSchema,
1389
+ template: z$1.enum(["nested", "separate"]).default("nested"),
1257
1390
  options: exportProjectsOptionsSchema
1258
1391
  });
1259
1392
 
@@ -1332,7 +1465,6 @@ const router$6 = createRouter().openapi(createRoute({
1332
1465
  const project = await c.var.projectService.read({ id: projectId });
1333
1466
  return c.json(project, 200);
1334
1467
  });
1335
- var projects_default = router$6;
1336
1468
 
1337
1469
  //#endregion
1338
1470
  //#region src/api/routes/content/v1/collections.ts
@@ -1418,7 +1550,6 @@ const router$5 = createRouter().openapi(createRoute({
1418
1550
  });
1419
1551
  return c.json(collection, 200);
1420
1552
  });
1421
- var collections_default = router$5;
1422
1553
 
1423
1554
  //#endregion
1424
1555
  //#region src/api/routes/content/v1/entries.ts
@@ -1525,7 +1656,6 @@ const router$4 = createRouter().openapi(createRoute({
1525
1656
  });
1526
1657
  return c.json(entry, 200);
1527
1658
  });
1528
- var entries_default = router$4;
1529
1659
 
1530
1660
  //#endregion
1531
1661
  //#region src/api/routes/content/v1/assets.ts
@@ -1611,22 +1741,18 @@ const router$3 = createRouter().openapi(createRoute({
1611
1741
  });
1612
1742
  return c.json(asset, 200);
1613
1743
  });
1614
- var assets_default = router$3;
1615
1744
 
1616
1745
  //#endregion
1617
1746
  //#region src/api/routes/content/v1/index.ts
1618
- const router$2 = createRouter().route("/projects", projects_default).route("/projects", collections_default).route("/projects", entries_default).route("/projects", assets_default);
1619
- var v1_default = router$2;
1747
+ const router$2 = createRouter().route("/projects", router$6).route("/projects", router$5).route("/projects", router$4).route("/projects", router$3);
1620
1748
 
1621
1749
  //#endregion
1622
1750
  //#region src/api/routes/content/index.ts
1623
- const router$1 = createRouter().route("/v1", v1_default);
1624
- var content_default = router$1;
1751
+ const router$1 = createRouter().route("/v1", router$2);
1625
1752
 
1626
1753
  //#endregion
1627
1754
  //#region src/api/routes/index.ts
1628
- const router = createRouter().route("/content", content_default);
1629
- var routes_default = router;
1755
+ const router = createRouter().route("/content", router$1);
1630
1756
 
1631
1757
  //#endregion
1632
1758
  //#region src/api/index.ts
@@ -1644,7 +1770,7 @@ var LocalApi = class {
1644
1770
  this.collectionService = collectionService;
1645
1771
  this.entryService = entryService;
1646
1772
  this.assetService = assetService;
1647
- this.api = createApi(this.logService, this.projectService, this.collectionService, this.entryService, this.assetService).route("/", routes_default).doc("/openapi.json", {
1773
+ this.api = createApi(this.logService, this.projectService, this.collectionService, this.entryService, this.assetService).route("/", router).doc("/openapi.json", {
1648
1774
  openapi: "3.0.0",
1649
1775
  externalDocs: { url: "https://elek.io/docs" },
1650
1776
  info: {
@@ -1748,7 +1874,7 @@ var RequiredParameterMissingError = class extends Error {
1748
1874
 
1749
1875
  //#endregion
1750
1876
  //#region src/util/node.ts
1751
- var node_exports = /* @__PURE__ */ __export({
1877
+ var node_exports = /* @__PURE__ */ __exportAll({
1752
1878
  execCommand: () => execCommand,
1753
1879
  files: () => files,
1754
1880
  folders: () => folders,
@@ -1915,8 +2041,8 @@ var AbstractCrudService = class {
1915
2041
  /**
1916
2042
  * Do not instantiate directly as this is an abstract class
1917
2043
  */
1918
- constructor(type$1, options, logService) {
1919
- this.type = type$1;
2044
+ constructor(type, options, logService) {
2045
+ this.type = type;
1920
2046
  this.options = options;
1921
2047
  this.logService = logService;
1922
2048
  }
@@ -1952,8 +2078,8 @@ var AbstractCrudService = class {
1952
2078
  * @param projectId Project to get all asset references from
1953
2079
  * @param collectionId Only needed when requesting files of type "Entry"
1954
2080
  */
1955
- async listReferences(type$1, projectId, collectionId) {
1956
- switch (type$1) {
2081
+ async listReferences(type, projectId, collectionId) {
2082
+ switch (type) {
1957
2083
  case objectTypeSchema.enum.asset:
1958
2084
  if (!projectId) throw new RequiredParameterMissingError("projectId");
1959
2085
  return this.getFileReferences(pathTo.lfs(projectId));
@@ -1968,7 +2094,7 @@ var AbstractCrudService = class {
1968
2094
  case objectTypeSchema.enum.sharedValue:
1969
2095
  if (!projectId) throw new RequiredParameterMissingError("projectId");
1970
2096
  return this.getFileReferences(pathTo.sharedValues(projectId));
1971
- default: throw new Error(`Trying to list files of unsupported type "${type$1}"`);
2097
+ default: throw new Error(`Trying to list files of unsupported type "${type}"`);
1972
2098
  }
1973
2099
  }
1974
2100
  async getFolderReferences(path) {
@@ -2188,9 +2314,9 @@ var AssetService = class extends AbstractCrudService {
2188
2314
  async list(props) {
2189
2315
  listAssetsSchema.parse(props);
2190
2316
  const offset = props.offset || 0;
2191
- const limit = props.limit || 15;
2317
+ const limit = props.limit ?? 15;
2192
2318
  const assetReferences = await this.listReferences(objectTypeSchema.enum.asset, props.projectId);
2193
- const partialAssetReferences = assetReferences.slice(offset, limit);
2319
+ const partialAssetReferences = limit === 0 ? assetReferences.slice(offset) : assetReferences.slice(offset, offset + limit);
2194
2320
  const assets = await this.returnResolved(partialAssetReferences.map((assetReference) => {
2195
2321
  return this.read({
2196
2322
  projectId: props.projectId,
@@ -2373,9 +2499,9 @@ var CollectionService = class extends AbstractCrudService {
2373
2499
  async list(props) {
2374
2500
  listCollectionsSchema.parse(props);
2375
2501
  const offset = props.offset || 0;
2376
- const limit = props.limit || 15;
2502
+ const limit = props.limit ?? 15;
2377
2503
  const collectionReferences = await this.listReferences(objectTypeSchema.enum.collection, props.projectId);
2378
- const partialCollectionReferences = collectionReferences.slice(offset, limit);
2504
+ const partialCollectionReferences = limit === 0 ? collectionReferences.slice(offset) : collectionReferences.slice(offset, offset + limit);
2379
2505
  const collections = await this.returnResolved(partialCollectionReferences.map((reference) => {
2380
2506
  return this.read({
2381
2507
  projectId: props.projectId,
@@ -2538,9 +2664,9 @@ var EntryService = class extends AbstractCrudService {
2538
2664
  async list(props) {
2539
2665
  listEntriesSchema.parse(props);
2540
2666
  const offset = props.offset || 0;
2541
- const limit = props.limit || 15;
2667
+ const limit = props.limit ?? 15;
2542
2668
  const entryReferences = await this.listReferences(objectTypeSchema.enum.entry, props.projectId, props.collectionId);
2543
- const partialEntryReferences = entryReferences.slice(offset, limit);
2669
+ const partialEntryReferences = limit === 0 ? entryReferences.slice(offset) : entryReferences.slice(offset, offset + limit);
2544
2670
  const entries = await this.returnResolved(partialEntryReferences.map((reference) => {
2545
2671
  return this.read({
2546
2672
  projectId: props.projectId,
@@ -2626,8 +2752,8 @@ var GitTagService = class extends AbstractCrudService {
2626
2752
  */
2627
2753
  async read(props) {
2628
2754
  readGitTagSchema.parse(props);
2629
- const tag = (await this.list({ path: props.path })).list.find((tag$1) => {
2630
- return tag$1.id === props.id;
2755
+ const tag = (await this.list({ path: props.path })).list.find((tag) => {
2756
+ return tag.id === props.id;
2631
2757
  });
2632
2758
  if (!tag) throw new GitError(`Provided tag with UUID "${props.id}" did not match any known tags`);
2633
2759
  return tag;
@@ -2818,11 +2944,11 @@ var GitService = class {
2818
2944
  * @param path Path to the repository
2819
2945
  * @param files Files to add
2820
2946
  */
2821
- async add(path, files$2) {
2947
+ async add(path, files) {
2822
2948
  const args = [
2823
2949
  "add",
2824
2950
  "--",
2825
- ...files$2.map((filePath) => {
2951
+ ...files.map((filePath) => {
2826
2952
  return filePath.replace(`${path}${Path.sep}`, "");
2827
2953
  })
2828
2954
  ];
@@ -3096,11 +3222,11 @@ var GitService = class {
3096
3222
  * @param path Path to the repository
3097
3223
  * @param name Name to check
3098
3224
  */
3099
- async checkBranchOrTagName(path, name$1) {
3225
+ async checkBranchOrTagName(path, name) {
3100
3226
  await this.git(path, [
3101
3227
  "check-ref-format",
3102
3228
  "--allow-onelevel",
3103
- name$1
3229
+ name
3104
3230
  ]);
3105
3231
  }
3106
3232
  /**
@@ -3159,7 +3285,7 @@ var GitService = class {
3159
3285
  const result = await this.queue.add(async () => {
3160
3286
  const start = Date.now();
3161
3287
  return {
3162
- gitResult: await GitProcess.exec(args, path, options),
3288
+ gitResult: await exec(args, path, options),
3163
3289
  durationMs: Date.now() - start
3164
3290
  };
3165
3291
  });
@@ -3171,8 +3297,12 @@ var GitService = class {
3171
3297
  };
3172
3298
  if (result.durationMs >= 100) this.logService.warn(gitLog);
3173
3299
  else this.logService.debug(gitLog);
3174
- if (result.gitResult.exitCode !== 0) throw new GitError(`Git ${this.version} (${this.gitPath}) command "git ${args.join(" ")}" executed for "${path}" failed with exit code "${result.gitResult.exitCode}" and message "${result.gitResult.stderr.trim() || result.gitResult.stdout.trim()}"`);
3175
- return result.gitResult;
3300
+ if (result.gitResult.exitCode !== 0) throw new GitError(`Git ${this.version} (${this.gitPath}) command "git ${args.join(" ")}" executed for "${path}" failed with exit code "${result.gitResult.exitCode}" and message "${result.gitResult.stderr.toString().trim() || result.gitResult.stdout.toString().trim()}"`);
3301
+ return {
3302
+ ...result.gitResult,
3303
+ stdout: result.gitResult.stdout.toString(),
3304
+ stderr: result.gitResult.stderr.toString()
3305
+ };
3176
3306
  }
3177
3307
  };
3178
3308
 
@@ -3221,8 +3351,8 @@ var JsonFileService = class extends AbstractCrudService {
3221
3351
  source: "core",
3222
3352
  message: `Cache hit reading file "${path}"`
3223
3353
  });
3224
- const json$1 = this.cache.get(path);
3225
- return schema.parse(json$1);
3354
+ const json = this.cache.get(path);
3355
+ return schema.parse(json);
3226
3356
  }
3227
3357
  this.logService.debug({
3228
3358
  source: "core",
@@ -3327,13 +3457,18 @@ var LogService = class {
3327
3457
  handleExceptions: true,
3328
3458
  handleRejections: true,
3329
3459
  format: format.combine(format.colorize(), format.timestamp({ format: "HH:mm:ss" }), format.printf((props) => {
3330
- const { timestamp, level, source, message } = logConsoleTransportSchema.parse({
3331
- ...props[Symbol.for("splat")][0],
3460
+ const splatArgs = props[Symbol.for("splat")];
3461
+ const result = logConsoleTransportSchema.safeParse({
3462
+ ...splatArgs?.[0] ?? {},
3332
3463
  timestamp: props["timestamp"],
3333
3464
  level: props.level,
3334
3465
  message: props.message
3335
3466
  });
3336
- return `${timestamp} [${source}] ${level}: ${message}`;
3467
+ if (result.success) {
3468
+ const { timestamp, level, source, message } = result.data;
3469
+ return `${timestamp} [${source}] ${level}: ${message}`;
3470
+ }
3471
+ return `${String(props["timestamp"])} ${props.level}: ${String(props.message)}`;
3337
3472
  }))
3338
3473
  });
3339
3474
  this.logger = createLogger({
@@ -3397,16 +3532,14 @@ var SynchronizeLocalChangesError = class extends Error {
3397
3532
  var ProjectService = class extends AbstractCrudService {
3398
3533
  coreVersion;
3399
3534
  jsonFileService;
3400
- userService;
3401
3535
  gitService;
3402
3536
  assetService;
3403
3537
  collectionService;
3404
3538
  entryService;
3405
- constructor(coreVersion, options, logService, jsonFileService, userService, gitService, assetService, collectionService, entryService) {
3539
+ constructor(coreVersion, options, logService, jsonFileService, gitService, assetService, collectionService, entryService) {
3406
3540
  super(serviceTypeSchema.enum.Project, options, logService);
3407
3541
  this.coreVersion = coreVersion;
3408
3542
  this.jsonFileService = jsonFileService;
3409
- this.userService = userService;
3410
3543
  this.gitService = gitService;
3411
3544
  this.assetService = assetService;
3412
3545
  this.collectionService = collectionService;
@@ -3417,19 +3550,11 @@ var ProjectService = class extends AbstractCrudService {
3417
3550
  */
3418
3551
  async create(props) {
3419
3552
  createProjectSchema.parse(props);
3420
- const user = await this.userService.get();
3421
- if (!user) throw new NoCurrentUserError();
3422
3553
  const id = uuid();
3423
- const defaultSettings = { language: {
3424
- default: user.language,
3425
- supported: [user.language]
3426
- } };
3427
3554
  const projectFile = {
3428
3555
  ...props,
3429
3556
  objectType: "project",
3430
3557
  id,
3431
- description: props.description || "",
3432
- settings: Object.assign({}, defaultSettings, props.settings),
3433
3558
  created: datetime(),
3434
3559
  updated: null,
3435
3560
  coreVersion: this.coreVersion,
@@ -3487,7 +3612,7 @@ var ProjectService = class extends AbstractCrudService {
3487
3612
  const projectFile = await this.jsonFileService.read(pathTo.projectFile(props.id), projectFileSchema);
3488
3613
  return await this.toProject(projectFile);
3489
3614
  } else {
3490
- const projectFile = this.migrate(JSON.parse(await this.gitService.getFileContentAtCommit(pathTo.project(props.id), pathTo.projectFile(props.id), props.commitHash)));
3615
+ const projectFile = this.migrate(migrateProjectSchema.parse(JSON.parse(await this.gitService.getFileContentAtCommit(pathTo.project(props.id), pathTo.projectFile(props.id), props.commitHash))));
3491
3616
  return await this.toProject(projectFile);
3492
3617
  }
3493
3618
  }
@@ -3498,16 +3623,9 @@ var ProjectService = class extends AbstractCrudService {
3498
3623
  updateProjectSchema.parse(props);
3499
3624
  const projectPath = pathTo.project(props.id);
3500
3625
  const filePath = pathTo.projectFile(props.id);
3501
- const prevProjectFile = await this.read(props);
3502
3626
  const projectFile = {
3503
- ...prevProjectFile,
3504
- name: props.name || prevProjectFile.name,
3505
- description: props.description || prevProjectFile.description,
3506
- coreVersion: this.coreVersion,
3507
- settings: { language: {
3508
- supported: props.settings?.language.supported || prevProjectFile.settings.language.supported,
3509
- default: props.settings?.language.default || prevProjectFile.settings.language.default
3510
- } },
3627
+ ...await this.read(props),
3628
+ ...props,
3511
3629
  updated: datetime()
3512
3630
  };
3513
3631
  await this.jsonFileService.update(projectFile, filePath, projectFileSchema);
@@ -3531,7 +3649,7 @@ var ProjectService = class extends AbstractCrudService {
3531
3649
  const projectPath = pathTo.project(props.id);
3532
3650
  const projectFilePath = pathTo.projectFile(props.id);
3533
3651
  if (await this.gitService.branches.current(projectPath) !== projectBranchSchema.enum.work) await this.gitService.branches.switch(projectPath, projectBranchSchema.enum.work);
3534
- const currentProjectFile = outdatedProjectSchema.passthrough().parse(await this.jsonFileService.unsafeRead(projectFilePath));
3652
+ const currentProjectFile = migrateProjectSchema.parse(await this.jsonFileService.unsafeRead(projectFilePath));
3535
3653
  if (Semver.gt(currentProjectFile.coreVersion, this.coreVersion)) throw new ProjectUpgradeError(`The Projects Core version "${currentProjectFile.coreVersion}" is higher than the current Core version "${this.coreVersion}".`);
3536
3654
  if (Semver.eq(currentProjectFile.coreVersion, this.coreVersion) && props.force !== true) throw new ProjectUpgradeError(`The Projects Core version "${currentProjectFile.coreVersion}" is already up to date.`);
3537
3655
  const assetReferences = await this.listReferences("asset", props.id);
@@ -3673,7 +3791,7 @@ var ProjectService = class extends AbstractCrudService {
3673
3791
  const projectReferences = await this.listReferences(objectTypeSchema.enum.project);
3674
3792
  return (await Promise.all(projectReferences.map(async (reference) => {
3675
3793
  const json = await this.jsonFileService.unsafeRead(pathTo.projectFile(reference.id));
3676
- const projectFile = outdatedProjectSchema.parse(json);
3794
+ const projectFile = migrateProjectSchema.parse(json);
3677
3795
  if (projectFile.coreVersion !== this.coreVersion) return projectFile;
3678
3796
  return null;
3679
3797
  }))).filter(isNotEmpty);
@@ -3681,9 +3799,9 @@ var ProjectService = class extends AbstractCrudService {
3681
3799
  async list(props) {
3682
3800
  if (props) listProjectsSchema.parse(props);
3683
3801
  const offset = props?.offset || 0;
3684
- const limit = props?.limit || 15;
3802
+ const limit = props?.limit ?? 15;
3685
3803
  const projectReferences = await this.listReferences(objectTypeSchema.enum.project);
3686
- const partialProjectReferences = projectReferences.slice(offset, limit);
3804
+ const partialProjectReferences = limit === 0 ? projectReferences.slice(offset) : projectReferences.slice(offset, offset + limit);
3687
3805
  const projects = await this.returnResolved(partialProjectReferences.map((reference) => {
3688
3806
  return this.read({ id: reference.id });
3689
3807
  }));
@@ -3706,8 +3824,9 @@ var ProjectService = class extends AbstractCrudService {
3706
3824
  /**
3707
3825
  * Migrates an potentially outdated Project file to the current schema
3708
3826
  */
3709
- migrate(potentiallyOutdatedProjectFile) {
3710
- return projectFileSchema.parse(potentiallyOutdatedProjectFile);
3827
+ migrate(props) {
3828
+ props.coreVersion = this.coreVersion;
3829
+ return projectFileSchema.parse(props);
3711
3830
  }
3712
3831
  /**
3713
3832
  * Creates a Project from given ProjectFile
@@ -3731,8 +3850,8 @@ var ProjectService = class extends AbstractCrudService {
3731
3850
  * committed
3732
3851
  */
3733
3852
  async createFolderStructure(path) {
3734
- const folders$1 = Object.values(projectFolderSchema.enum);
3735
- await Promise.all(folders$1.map(async (folder) => {
3853
+ const folders = Object.values(projectFolderSchema.enum);
3854
+ await Promise.all(folders.map(async (folder) => {
3736
3855
  await Fs.mkdirp(Path.join(path, folder));
3737
3856
  await Fs.writeFile(Path.join(path, folder, ".gitkeep"), "");
3738
3857
  }));
@@ -3896,7 +4015,7 @@ var ElekIoCore = class {
3896
4015
  this.assetService = new AssetService(this.options, this.logService, this.jsonFileService, this.gitService);
3897
4016
  this.collectionService = new CollectionService(this.options, this.logService, this.jsonFileService, this.gitService);
3898
4017
  this.entryService = new EntryService(this.options, this.logService, this.jsonFileService, this.gitService, this.collectionService);
3899
- this.projectService = new ProjectService(this.coreVersion, this.options, this.logService, this.jsonFileService, this.userService, this.gitService, this.assetService, this.collectionService, this.entryService);
4018
+ this.projectService = new ProjectService(this.coreVersion, this.options, this.logService, this.jsonFileService, this.gitService, this.assetService, this.collectionService, this.entryService);
3900
4019
  this.localApi = new LocalApi(this.logService, this.projectService, this.collectionService, this.entryService, this.assetService);
3901
4020
  this.logService.info({
3902
4021
  source: "core",
@@ -3978,11 +4097,11 @@ function watchProjects() {
3978
4097
  //#region src/index.cli.ts
3979
4098
  const program = new Command();
3980
4099
  program.name("elek").description("CLI for elek.io").version(package_default.version);
3981
- program.command("generate:client").description("Generates a JS/TS API Client").argument("[outDir]", "The directory to generate the API Client in", "./.elek.io").argument("[language]", "The programming language of the generated API Client. Choose \"ts\" if you bundle it yourself in your TypeScript project, or \"js\" if you want a ready-to-use JavaScript API Client.", "ts").argument("[format]", "The output format of the generated API Client. Choose \"esm\" for ES Modules, or \"cjs\" for CommonJS. This option is only relevant if you choose \"js\" as the language.", "esm").argument("[target]", "The target environment of the generated API Client. Choose this depending on the JavaScript runtime you want to support. This option is only relevant if you choose \"js\" as the language.", "es2020").option("-w, --watch", "Watches for changes in your Projects and regenerates the API Client automatically.").action(async (outDir, language, format$1, target, options) => {
4100
+ program.command("generate:client").description("Generates a JS/TS API Client").argument("[outDir]", "The directory to generate the API Client in", "./.elek.io").argument("[language]", "The programming language of the generated API Client. Choose \"ts\" if you bundle it yourself in your TypeScript project, or \"js\" if you want a ready-to-use JavaScript API Client.", "ts").argument("[format]", "The output format of the generated API Client. Choose \"esm\" for ES Modules, or \"cjs\" for CommonJS. This option is only relevant if you choose \"js\" as the language.", "esm").argument("[target]", "The target environment of the generated API Client. Choose this depending on the JavaScript runtime you want to support. This option is only relevant if you choose \"js\" as the language.", "es2020").option("-w, --watch", "Watches for changes in your Projects and regenerates the API Client automatically.").action(async (outDir, language, format, target, options) => {
3982
4101
  await generateApiClientAction(generateApiClientSchema.parse({
3983
4102
  outDir,
3984
4103
  language,
3985
- format: format$1,
4104
+ format,
3986
4105
  target,
3987
4106
  options
3988
4107
  }));
@@ -3990,10 +4109,11 @@ program.command("generate:client").description("Generates a JS/TS API Client").a
3990
4109
  program.command("api:start").description("Starts the local API").argument("[port]", "The port to run the local API on", "31310").action((port) => {
3991
4110
  startApiAction(apiStartSchema.parse({ port }));
3992
4111
  });
3993
- program.command("export").description("Exports all locally available Projects into a JSON file").argument("[outDir]", "The directory to write the JSON file to", "./.elek.io").argument("[projects]", "One or more Project IDs, separated by commas to export. If not provided, all Projects will be exported.", "all").option("-s, --separate", "Separates the exported Projects into individual files.").option("-w, --watch", "Watches for changes in your Projects and updates the JSON file automatically.").action(async (outDir, projects, options) => {
4112
+ program.command("export").description("Exports locally available Projects to JSON").argument("[outDir]", "The directory to write the JSON to", "./.elek.io").argument("[projects]", "One or more Project IDs, separated by commas to export. If not provided, all Projects will be exported.", "all").argument("[template]", "The template to use for exporting Projects. Choose \"nested\" to export all Projects in a single, nested file or \"separate\" to export each Project in a separate folder with individual files.", "nested").option("-w, --watch", "Watches for changes in your Projects and updates the JSON file automatically.").action(async (outDir, projects, template, options) => {
3994
4113
  await exportAction(exportSchema.parse({
3995
4114
  outDir,
3996
4115
  projects,
4116
+ template,
3997
4117
  options
3998
4118
  }));
3999
4119
  });
@@ -4001,4 +4121,4 @@ await program.parseAsync();
4001
4121
 
4002
4122
  //#endregion
4003
4123
  export { };
4004
- //# sourceMappingURL=index.cli.js.map
4124
+ //# sourceMappingURL=index.cli.mjs.map