@isardsat/editorial-server 6.13.0 → 6.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/schema.d.ts +4 -1
- package/dist/lib/storage.d.ts +4 -1
- package/dist/routes/data.js +128 -6
- package/dist/routes/files.js +109 -30
- package/package.json +3 -3
package/dist/lib/schema.d.ts
CHANGED
|
@@ -2,12 +2,15 @@ export declare function createSchema(configDirectory: string): Promise<Record<st
|
|
|
2
2
|
displayName: string;
|
|
3
3
|
fields: Record<string, {
|
|
4
4
|
[x: string]: unknown;
|
|
5
|
-
type: "string" | "number" | "boolean" | "url" | "date" | "datetime" | "markdown" | "color" | "select";
|
|
5
|
+
type: "string" | "number" | "boolean" | "url" | "date" | "datetime" | "markdown" | "color" | "select" | "multiselect";
|
|
6
6
|
displayName: string;
|
|
7
7
|
optional: boolean;
|
|
8
8
|
displayExtra?: string | undefined;
|
|
9
9
|
placeholder?: string | undefined;
|
|
10
10
|
showInSummary?: boolean | undefined;
|
|
11
|
+
options?: string[] | undefined;
|
|
12
|
+
maxSelectedOptions?: number | undefined;
|
|
13
|
+
minSelectedOptions?: number | undefined;
|
|
11
14
|
}>;
|
|
12
15
|
filterBy?: string | undefined;
|
|
13
16
|
singleton?: boolean | undefined;
|
package/dist/lib/storage.d.ts
CHANGED
|
@@ -4,12 +4,15 @@ export declare function createStorage(dataDirectory: string): {
|
|
|
4
4
|
displayName: string;
|
|
5
5
|
fields: Record<string, {
|
|
6
6
|
[x: string]: unknown;
|
|
7
|
-
type: "string" | "number" | "boolean" | "url" | "date" | "datetime" | "markdown" | "color" | "select";
|
|
7
|
+
type: "string" | "number" | "boolean" | "url" | "date" | "datetime" | "markdown" | "color" | "select" | "multiselect";
|
|
8
8
|
displayName: string;
|
|
9
9
|
optional: boolean;
|
|
10
10
|
displayExtra?: string | undefined;
|
|
11
11
|
placeholder?: string | undefined;
|
|
12
12
|
showInSummary?: boolean | undefined;
|
|
13
|
+
options?: string[] | undefined;
|
|
14
|
+
maxSelectedOptions?: number | undefined;
|
|
15
|
+
minSelectedOptions?: number | undefined;
|
|
13
16
|
}>;
|
|
14
17
|
filterBy?: string | undefined;
|
|
15
18
|
singleton?: boolean | undefined;
|
package/dist/routes/data.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
|
2
|
-
import { EditorialDataItemSchema, EditorialDataSchema, EditorialSchemaSchema, } from "@isardsat/editorial-common";
|
|
2
|
+
import { EditorialDataItemSchema, EditorialDataSchema, EditorialSchemaSchema, getOptionsReference, } from "@isardsat/editorial-common";
|
|
3
3
|
function createCache() {
|
|
4
4
|
let schemaCache = null;
|
|
5
5
|
const contentCache = new Map();
|
|
@@ -36,6 +36,86 @@ function createCache() {
|
|
|
36
36
|
},
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolves uploaded file paths to full URLs for an item.
|
|
41
|
+
*/
|
|
42
|
+
function resolveFileUrls(item, schema, itemType, origin) {
|
|
43
|
+
const resolvedItem = { ...item };
|
|
44
|
+
const itemSchema = schema[itemType];
|
|
45
|
+
if (!itemSchema)
|
|
46
|
+
return resolvedItem;
|
|
47
|
+
for (const [key, value] of Object.entries(resolvedItem)) {
|
|
48
|
+
if (!itemSchema.fields[key]?.isUploadedFile)
|
|
49
|
+
continue;
|
|
50
|
+
if (typeof value !== "string")
|
|
51
|
+
continue;
|
|
52
|
+
if (value.startsWith("http"))
|
|
53
|
+
continue;
|
|
54
|
+
resolvedItem[key] = `${origin}/${value}`;
|
|
55
|
+
}
|
|
56
|
+
return resolvedItem;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Resolves referenced fields in an item, replacing IDs with full objects.
|
|
60
|
+
* Recursively resolves nested references.
|
|
61
|
+
*/
|
|
62
|
+
function resolveReferences(item, schema, itemType, content, origin, resolvedIds = new Set()) {
|
|
63
|
+
const itemIdentifier = `${itemType}:${item.id}`;
|
|
64
|
+
// Prevent circular references
|
|
65
|
+
if (resolvedIds.has(itemIdentifier)) {
|
|
66
|
+
return resolveFileUrls(item, schema, itemType, origin);
|
|
67
|
+
}
|
|
68
|
+
resolvedIds.add(itemIdentifier);
|
|
69
|
+
// First resolve file URLs for the current item
|
|
70
|
+
let resolvedItem = resolveFileUrls(item, schema, itemType, origin);
|
|
71
|
+
const itemSchema = schema[itemType];
|
|
72
|
+
if (!itemSchema)
|
|
73
|
+
return resolvedItem;
|
|
74
|
+
for (const [fieldKey, fieldConfig] of Object.entries(itemSchema.fields)) {
|
|
75
|
+
if (fieldConfig.type !== "select" && fieldConfig.type !== "multiselect") {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const referencedType = getOptionsReference(fieldConfig.options);
|
|
79
|
+
if (!referencedType)
|
|
80
|
+
continue;
|
|
81
|
+
const referencedCollection = content[referencedType];
|
|
82
|
+
if (!referencedCollection)
|
|
83
|
+
continue;
|
|
84
|
+
const fieldValue = item[fieldKey];
|
|
85
|
+
if (fieldConfig.type === "select" && typeof fieldValue === "string") {
|
|
86
|
+
// Single reference - replace ID with full object
|
|
87
|
+
const referencedItem = referencedCollection[fieldValue];
|
|
88
|
+
if (referencedItem) {
|
|
89
|
+
// Recursively resolve nested references
|
|
90
|
+
resolvedItem[fieldKey] = resolveReferences(referencedItem, schema, referencedType, content, origin, new Set(resolvedIds));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (fieldConfig.type === "multiselect" &&
|
|
94
|
+
Array.isArray(fieldValue)) {
|
|
95
|
+
// Multiple references - replace IDs with full objects
|
|
96
|
+
resolvedItem[fieldKey] = fieldValue
|
|
97
|
+
.map((id) => {
|
|
98
|
+
const referencedItem = referencedCollection[id];
|
|
99
|
+
if (!referencedItem)
|
|
100
|
+
return null;
|
|
101
|
+
// Recursively resolve nested references
|
|
102
|
+
return resolveReferences(referencedItem, schema, referencedType, content, origin, new Set(resolvedIds));
|
|
103
|
+
})
|
|
104
|
+
.filter(Boolean);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return resolvedItem;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Resolves all references in a collection.
|
|
111
|
+
*/
|
|
112
|
+
function resolveCollectionReferences(collection, schema, itemType, content, origin) {
|
|
113
|
+
const resolvedCollection = {};
|
|
114
|
+
for (const [itemKey, item] of Object.entries(collection)) {
|
|
115
|
+
resolvedCollection[itemKey] = resolveReferences(item, schema, itemType, content, origin);
|
|
116
|
+
}
|
|
117
|
+
return resolvedCollection;
|
|
118
|
+
}
|
|
39
119
|
export function createDataRoutes(config, storage) {
|
|
40
120
|
const app = new OpenAPIHono();
|
|
41
121
|
const cache = createCache();
|
|
@@ -70,6 +150,13 @@ export function createDataRoutes(config, storage) {
|
|
|
70
150
|
example: "es_ES",
|
|
71
151
|
}),
|
|
72
152
|
preview: z.string().optional(),
|
|
153
|
+
resolve: z
|
|
154
|
+
.string()
|
|
155
|
+
.optional()
|
|
156
|
+
.openapi({
|
|
157
|
+
param: { name: "resolve", in: "query" },
|
|
158
|
+
description: "Resolve referenced fields to full objects",
|
|
159
|
+
}),
|
|
73
160
|
}),
|
|
74
161
|
},
|
|
75
162
|
responses: {
|
|
@@ -83,9 +170,19 @@ export function createDataRoutes(config, storage) {
|
|
|
83
170
|
},
|
|
84
171
|
},
|
|
85
172
|
}), async (c) => {
|
|
86
|
-
const { preview } = c.req.valid("query");
|
|
173
|
+
const { preview, resolve } = c.req.valid("query");
|
|
87
174
|
const content = await cache.getContent(storage, { production: !preview });
|
|
88
|
-
|
|
175
|
+
if (!resolve) {
|
|
176
|
+
return c.json(content);
|
|
177
|
+
}
|
|
178
|
+
const origin = preview ? new URL(c.req.url).origin : publicFilesUrl;
|
|
179
|
+
// Resolve references for all collections
|
|
180
|
+
const schema = await cache.getSchema(storage);
|
|
181
|
+
const resolvedContent = {};
|
|
182
|
+
for (const [itemType, collection] of Object.entries(content)) {
|
|
183
|
+
resolvedContent[itemType] = resolveCollectionReferences(collection, schema, itemType, content, origin);
|
|
184
|
+
}
|
|
185
|
+
return c.json(resolvedContent);
|
|
89
186
|
});
|
|
90
187
|
app.openapi(createRoute({
|
|
91
188
|
method: "get",
|
|
@@ -106,6 +203,13 @@ export function createDataRoutes(config, storage) {
|
|
|
106
203
|
example: "es_ES",
|
|
107
204
|
}),
|
|
108
205
|
preview: z.string().optional(),
|
|
206
|
+
resolve: z
|
|
207
|
+
.string()
|
|
208
|
+
.optional()
|
|
209
|
+
.openapi({
|
|
210
|
+
param: { name: "resolve", in: "query" },
|
|
211
|
+
description: "Resolve referenced fields to full objects",
|
|
212
|
+
}),
|
|
109
213
|
}),
|
|
110
214
|
},
|
|
111
215
|
responses: {
|
|
@@ -123,7 +227,7 @@ export function createDataRoutes(config, storage) {
|
|
|
123
227
|
},
|
|
124
228
|
}), async (c) => {
|
|
125
229
|
const { itemType } = c.req.valid("param");
|
|
126
|
-
const { lang, preview } = c.req.valid("query");
|
|
230
|
+
const { lang, preview, resolve } = c.req.valid("query");
|
|
127
231
|
const origin = preview ? new URL(c.req.url).origin : publicFilesUrl;
|
|
128
232
|
const content = await cache.getContent(storage, {
|
|
129
233
|
production: !preview,
|
|
@@ -149,6 +253,11 @@ export function createDataRoutes(config, storage) {
|
|
|
149
253
|
}
|
|
150
254
|
}
|
|
151
255
|
}
|
|
256
|
+
// Resolve references if requested (includes file URL resolution)
|
|
257
|
+
if (resolve) {
|
|
258
|
+
return c.json(resolveCollectionReferences(collection, schema, itemType, content, origin));
|
|
259
|
+
}
|
|
260
|
+
// Apply file URL resolution for non-resolved requests
|
|
152
261
|
for (const [itemKey, itemValue] of Object.entries(collection)) {
|
|
153
262
|
for (const [key, value] of Object.entries(itemValue)) {
|
|
154
263
|
if (!schema[itemType].fields[key]?.isUploadedFile)
|
|
@@ -219,6 +328,13 @@ export function createDataRoutes(config, storage) {
|
|
|
219
328
|
example: "es_ES",
|
|
220
329
|
}),
|
|
221
330
|
preview: z.string().optional(),
|
|
331
|
+
resolve: z
|
|
332
|
+
.string()
|
|
333
|
+
.optional()
|
|
334
|
+
.openapi({
|
|
335
|
+
param: { name: "resolve", in: "query" },
|
|
336
|
+
description: "Resolve referenced fields to full objects",
|
|
337
|
+
}),
|
|
222
338
|
}),
|
|
223
339
|
},
|
|
224
340
|
responses: {
|
|
@@ -236,7 +352,7 @@ export function createDataRoutes(config, storage) {
|
|
|
236
352
|
},
|
|
237
353
|
}), async (c) => {
|
|
238
354
|
const { itemType, id } = c.req.valid("param");
|
|
239
|
-
const { lang, preview } = c.req.valid("query");
|
|
355
|
+
const { lang, preview, resolve } = c.req.valid("query");
|
|
240
356
|
const origin = preview ? new URL(c.req.url).origin : publicFilesUrl;
|
|
241
357
|
const content = await cache.getContent(storage, {
|
|
242
358
|
production: !preview,
|
|
@@ -247,7 +363,7 @@ export function createDataRoutes(config, storage) {
|
|
|
247
363
|
if (!collection) {
|
|
248
364
|
return c.notFound();
|
|
249
365
|
}
|
|
250
|
-
|
|
366
|
+
let item = collection[id];
|
|
251
367
|
if (!item) {
|
|
252
368
|
return c.notFound();
|
|
253
369
|
}
|
|
@@ -261,6 +377,12 @@ export function createDataRoutes(config, storage) {
|
|
|
261
377
|
}
|
|
262
378
|
}
|
|
263
379
|
}
|
|
380
|
+
// Resolve references if requested (includes file URL resolution)
|
|
381
|
+
if (resolve) {
|
|
382
|
+
item = resolveReferences(item, schema, itemType, content, origin);
|
|
383
|
+
return c.json(item);
|
|
384
|
+
}
|
|
385
|
+
// Apply file URL resolution for non-resolved requests
|
|
264
386
|
for (const [key, value] of Object.entries(item)) {
|
|
265
387
|
if (!schema[itemType].fields[key]?.isUploadedFile)
|
|
266
388
|
continue;
|
package/dist/routes/files.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
|
|
2
2
|
import { EditorialFilesResponseSchema, } from "@isardsat/editorial-common";
|
|
3
3
|
import { readdirSync, statSync } from "node:fs";
|
|
4
4
|
import { access, constants, mkdir, rename, writeFile } from "node:fs/promises";
|
|
5
|
-
import { basename, join, normalize, relative } from "node:path";
|
|
5
|
+
import { basename, dirname, join, normalize, relative } from "node:path";
|
|
6
6
|
export async function createFilesRoutes(config) {
|
|
7
7
|
const app = new OpenAPIHono();
|
|
8
8
|
// Dynamically import the ES module and call its init function
|
|
@@ -114,6 +114,7 @@ export async function createFilesRoutes(config) {
|
|
|
114
114
|
content: {
|
|
115
115
|
"application/json": {
|
|
116
116
|
schema: z.object({
|
|
117
|
+
// relative to publicDirPath, e.g. "images/2026/a.png" or "images/2026"
|
|
117
118
|
path: z.string(),
|
|
118
119
|
}),
|
|
119
120
|
},
|
|
@@ -123,60 +124,46 @@ export async function createFilesRoutes(config) {
|
|
|
123
124
|
},
|
|
124
125
|
responses: {
|
|
125
126
|
200: {
|
|
126
|
-
content: {
|
|
127
|
-
|
|
128
|
-
schema: z.boolean(),
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
description: "",
|
|
127
|
+
content: { "application/json": { schema: z.boolean() } },
|
|
128
|
+
description: "Deleted (moved to deleted folder)",
|
|
132
129
|
},
|
|
133
130
|
400: {
|
|
134
131
|
content: {
|
|
135
|
-
"application/json": {
|
|
136
|
-
schema: z.object({
|
|
137
|
-
error: z.string(),
|
|
138
|
-
}),
|
|
139
|
-
},
|
|
132
|
+
"application/json": { schema: z.object({ error: z.string() }) },
|
|
140
133
|
},
|
|
141
134
|
description: "Invalid file path",
|
|
142
135
|
},
|
|
143
136
|
404: {
|
|
144
137
|
content: {
|
|
145
|
-
"application/json": {
|
|
146
|
-
schema: z.object({
|
|
147
|
-
error: z.string(),
|
|
148
|
-
}),
|
|
149
|
-
},
|
|
138
|
+
"application/json": { schema: z.object({ error: z.string() }) },
|
|
150
139
|
},
|
|
151
140
|
description: "File not found",
|
|
152
141
|
},
|
|
153
142
|
500: {
|
|
154
143
|
content: {
|
|
155
|
-
"application/json": {
|
|
156
|
-
schema: z.object({
|
|
157
|
-
error: z.string(),
|
|
158
|
-
}),
|
|
159
|
-
},
|
|
144
|
+
"application/json": { schema: z.object({ error: z.string() }) },
|
|
160
145
|
},
|
|
161
146
|
description: "Server error",
|
|
162
147
|
},
|
|
163
148
|
},
|
|
164
149
|
}), async (c) => {
|
|
165
|
-
const { path:
|
|
150
|
+
const { path: relativePathInput } = c.req.valid("json");
|
|
166
151
|
try {
|
|
167
|
-
const
|
|
168
|
-
|
|
152
|
+
const rel = normalize(relativePathInput).replace(/^([/\\])+/, "");
|
|
153
|
+
const absoluteSource = normalize(join(publicDirPath, rel));
|
|
154
|
+
const publicRoot = normalize(publicDirPath + (publicDirPath.endsWith("/") ? "" : "/"));
|
|
155
|
+
if (!absoluteSource.startsWith(publicRoot)) {
|
|
169
156
|
return c.json({ error: "Invalid file path" }, 400);
|
|
170
157
|
}
|
|
171
|
-
const exists = await access(
|
|
158
|
+
const exists = await access(absoluteSource, constants.F_OK)
|
|
172
159
|
.then(() => true)
|
|
173
160
|
.catch(() => false);
|
|
174
161
|
if (!exists) {
|
|
175
162
|
return c.json({ error: "File not found" }, 404);
|
|
176
163
|
}
|
|
177
|
-
|
|
178
|
-
await mkdir(
|
|
179
|
-
await rename(
|
|
164
|
+
const absoluteTarget = normalize(join(deletedDirPath, rel));
|
|
165
|
+
await mkdir(dirname(absoluteTarget), { recursive: true });
|
|
166
|
+
await rename(absoluteSource, absoluteTarget);
|
|
180
167
|
return c.json(true, 200);
|
|
181
168
|
}
|
|
182
169
|
catch (err) {
|
|
@@ -249,7 +236,9 @@ export async function createFilesRoutes(config) {
|
|
|
249
236
|
for (const file of fileArray) {
|
|
250
237
|
if (file instanceof File) {
|
|
251
238
|
if (file.size >= 1e6) {
|
|
252
|
-
|
|
239
|
+
await largeFilesHandler.upload(file, { path: targetPath });
|
|
240
|
+
uploadedFiles.push(file.name);
|
|
241
|
+
continue;
|
|
253
242
|
}
|
|
254
243
|
const fileName = file.name;
|
|
255
244
|
const filePath = join(normalizedTargetDir, fileName);
|
|
@@ -265,5 +254,95 @@ export async function createFilesRoutes(config) {
|
|
|
265
254
|
return c.json({ error: "Server error" }, 500);
|
|
266
255
|
}
|
|
267
256
|
});
|
|
257
|
+
app.openapi(createRoute({
|
|
258
|
+
method: "post",
|
|
259
|
+
path: "/files/directory",
|
|
260
|
+
request: {
|
|
261
|
+
body: {
|
|
262
|
+
content: {
|
|
263
|
+
"application/json": {
|
|
264
|
+
schema: z.object({
|
|
265
|
+
// path relative to publicDirPath, e.g. "images/2026"
|
|
266
|
+
path: z.string().optional().default(""),
|
|
267
|
+
// directory name to create, e.g. "new-folder"
|
|
268
|
+
name: z.string().min(1),
|
|
269
|
+
}),
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
required: true,
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
responses: {
|
|
276
|
+
201: {
|
|
277
|
+
content: {
|
|
278
|
+
"application/json": {
|
|
279
|
+
schema: z.object({
|
|
280
|
+
relativePath: z.string(),
|
|
281
|
+
}),
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
description: "Directory created",
|
|
285
|
+
},
|
|
286
|
+
400: {
|
|
287
|
+
content: {
|
|
288
|
+
"application/json": {
|
|
289
|
+
schema: z.object({
|
|
290
|
+
error: z.string(),
|
|
291
|
+
}),
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
description: "Invalid directory path/name",
|
|
295
|
+
},
|
|
296
|
+
409: {
|
|
297
|
+
content: {
|
|
298
|
+
"application/json": {
|
|
299
|
+
schema: z.object({
|
|
300
|
+
error: z.string(),
|
|
301
|
+
}),
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
description: "Directory already exists",
|
|
305
|
+
},
|
|
306
|
+
500: {
|
|
307
|
+
content: {
|
|
308
|
+
"application/json": {
|
|
309
|
+
schema: z.object({
|
|
310
|
+
error: z.string(),
|
|
311
|
+
}),
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
description: "Server error",
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
}), async (c) => {
|
|
318
|
+
try {
|
|
319
|
+
const { path, name } = c.req.valid("json");
|
|
320
|
+
// Avoid path traversal and invalid names
|
|
321
|
+
if (name.includes("/") ||
|
|
322
|
+
name.includes("\\") ||
|
|
323
|
+
name === "." ||
|
|
324
|
+
name === "..") {
|
|
325
|
+
return c.json({ error: "Invalid directory name" }, 400);
|
|
326
|
+
}
|
|
327
|
+
const targetDir = join(publicDirPath, path ?? "", name);
|
|
328
|
+
const normalizedTargetDir = normalize(targetDir);
|
|
329
|
+
if (!normalizedTargetDir.startsWith(publicDirPath)) {
|
|
330
|
+
return c.json({ error: "Invalid directory path" }, 400);
|
|
331
|
+
}
|
|
332
|
+
const exists = await access(normalizedTargetDir, constants.F_OK)
|
|
333
|
+
.then(() => true)
|
|
334
|
+
.catch(() => false);
|
|
335
|
+
if (exists) {
|
|
336
|
+
return c.json({ error: "Directory already exists" }, 409);
|
|
337
|
+
}
|
|
338
|
+
await mkdir(normalizedTargetDir, { recursive: true });
|
|
339
|
+
const relativePath = relative(publicDirPath, normalizedTargetDir);
|
|
340
|
+
return c.json({ relativePath }, 201);
|
|
341
|
+
}
|
|
342
|
+
catch (err) {
|
|
343
|
+
console.error("Create directory failed:", err);
|
|
344
|
+
return c.json({ error: "Server error" }, 500);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
268
347
|
return app;
|
|
269
348
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@isardsat/editorial-server",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.14.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"hono": "^4.9.8",
|
|
15
15
|
"yaml": "^2.8.1",
|
|
16
16
|
"zod": "^4.1.11",
|
|
17
|
-
"@isardsat/editorial-
|
|
18
|
-
"@isardsat/editorial-
|
|
17
|
+
"@isardsat/editorial-common": "^6.14.0",
|
|
18
|
+
"@isardsat/editorial-admin": "^6.14.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@tsconfig/node22": "^22.0.0",
|