@manablox/api-rpc 0.3.0 → 0.4.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/index.d.ts +3272 -1233
- package/dist/index.js +244 -11
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { MIN_PASSWORD_LENGTH, actorRoles, allowedTypeIds, assertCan, effectiveGrants, normaliseGrants } from "@manablox/auth";
|
|
3
|
-
import { ManabloxError, TRANSPORT_CODE, WORKFLOW_CONDITION_OPERATORS, WORKFLOW_EVENTS } from "@manablox/core";
|
|
2
|
+
import { MIN_PASSWORD_LENGTH, actorRoles, allowedTypeIds, assertCan, can, effectiveGrants, normaliseGrants } from "@manablox/auth";
|
|
3
|
+
import { AUDIT_ACTIONS, ManabloxError, NOTIFICATION_CHANNELS, NOTIFICATION_KINDS, TRANSPORT_CODE, WORKFLOW_CONDITION_OPERATORS, WORKFLOW_EVENTS } from "@manablox/core";
|
|
4
4
|
import { ORPCError, os } from "@orpc/server";
|
|
5
|
-
import { renderContentTypeConfig } from "@manablox/services";
|
|
5
|
+
import { SPACE_EXPORT_SECTIONS, applySpaceStarter, renderContentTypeConfig } from "@manablox/services";
|
|
6
6
|
//#region src/base.ts
|
|
7
7
|
/**
|
|
8
8
|
* Base procedure builder. Every management procedure runs through it, so the
|
|
@@ -136,7 +136,7 @@ const assetRouter = {
|
|
|
136
136
|
title: z.string().max(500).nullable().optional()
|
|
137
137
|
})).handler(async ({ input, context }) => {
|
|
138
138
|
const { spaceId: _spaceId, id, ...data } = input;
|
|
139
|
-
return context.
|
|
139
|
+
return context.media.present(await context.media.update(id, data));
|
|
140
140
|
}),
|
|
141
141
|
/**
|
|
142
142
|
* An image's crop and focal point. `null` clears one; omitting it also clears it, so
|
|
@@ -163,6 +163,105 @@ const assetRouter = {
|
|
|
163
163
|
})
|
|
164
164
|
};
|
|
165
165
|
//#endregion
|
|
166
|
+
//#region src/routers/audit.ts
|
|
167
|
+
const actorKind = z.enum([
|
|
168
|
+
"user",
|
|
169
|
+
"apikey",
|
|
170
|
+
"workflow",
|
|
171
|
+
"system"
|
|
172
|
+
]);
|
|
173
|
+
const targetKind = z.enum([
|
|
174
|
+
"content",
|
|
175
|
+
"contentType",
|
|
176
|
+
"space",
|
|
177
|
+
"member",
|
|
178
|
+
"user",
|
|
179
|
+
"apiKey",
|
|
180
|
+
"menu",
|
|
181
|
+
"role",
|
|
182
|
+
"workflow",
|
|
183
|
+
"workflowRun",
|
|
184
|
+
"asset",
|
|
185
|
+
"session"
|
|
186
|
+
]);
|
|
187
|
+
const filterSchema$1 = z.object({
|
|
188
|
+
actorKind: actorKind.optional(),
|
|
189
|
+
actorId: z.string().max(200).optional(),
|
|
190
|
+
actions: z.array(z.enum(AUDIT_ACTIONS)).max(60).optional(),
|
|
191
|
+
targetKind: targetKind.optional(),
|
|
192
|
+
targetId: z.string().max(200).optional(),
|
|
193
|
+
from: z.coerce.date().optional(),
|
|
194
|
+
to: z.coerce.date().optional(),
|
|
195
|
+
search: searchTerm.optional()
|
|
196
|
+
});
|
|
197
|
+
const sortSchema$1 = z.object({
|
|
198
|
+
by: z.enum([
|
|
199
|
+
"at",
|
|
200
|
+
"action",
|
|
201
|
+
"actorLabel",
|
|
202
|
+
"targetKind",
|
|
203
|
+
"targetLabel"
|
|
204
|
+
]).default("at"),
|
|
205
|
+
direction: z.enum(["asc", "desc"]).default("desc")
|
|
206
|
+
});
|
|
207
|
+
const paginationSchema$1 = pagination({
|
|
208
|
+
limit: 50,
|
|
209
|
+
max: 200
|
|
210
|
+
});
|
|
211
|
+
/**
|
|
212
|
+
* The activity log. Reading a space's log takes `audit:read` there; the instance-wide
|
|
213
|
+
* view, which includes actions outside any space (accounts, keys, creating spaces), is a
|
|
214
|
+
* superadmin's. Nothing here writes: the log is appended where the actions happen.
|
|
215
|
+
*/
|
|
216
|
+
const auditRouter = {
|
|
217
|
+
/** The actions, actor kinds and target kinds the filter can name. */
|
|
218
|
+
catalog: authed.handler(async ({ context }) => context.audit.catalog()),
|
|
219
|
+
list: scoped("audit:read").input(z.object({
|
|
220
|
+
spaceId: uuid,
|
|
221
|
+
filter: filterSchema$1.default({}),
|
|
222
|
+
sort: sortSchema$1.default({
|
|
223
|
+
by: "at",
|
|
224
|
+
direction: "desc"
|
|
225
|
+
}),
|
|
226
|
+
pagination: paginationSchema$1.default({
|
|
227
|
+
limit: 50,
|
|
228
|
+
offset: 0
|
|
229
|
+
})
|
|
230
|
+
})).handler(async ({ input, context }) => context.audit.list(input.spaceId, input.filter, input.sort, input.pagination)),
|
|
231
|
+
/**
|
|
232
|
+
* Every entry on the instance, or with `instanceOnly` just those outside any space.
|
|
233
|
+
* Superadmin, because it crosses spaces.
|
|
234
|
+
*/
|
|
235
|
+
listInstance: superadmin.input(z.object({
|
|
236
|
+
instanceOnly: z.boolean().default(false),
|
|
237
|
+
filter: filterSchema$1.default({}),
|
|
238
|
+
sort: sortSchema$1.default({
|
|
239
|
+
by: "at",
|
|
240
|
+
direction: "desc"
|
|
241
|
+
}),
|
|
242
|
+
pagination: paginationSchema$1.default({
|
|
243
|
+
limit: 50,
|
|
244
|
+
offset: 0
|
|
245
|
+
})
|
|
246
|
+
})).handler(async ({ input, context }) => context.audit.listInstance({
|
|
247
|
+
...input.filter,
|
|
248
|
+
...input.instanceOnly ? { spaceId: null } : {}
|
|
249
|
+
}, input.sort, input.pagination)),
|
|
250
|
+
get: scoped("audit:read").input(z.object({
|
|
251
|
+
spaceId: uuid,
|
|
252
|
+
id: uuid
|
|
253
|
+
})).handler(async ({ input, context }) => context.audit.get(input.spaceId, input.id)),
|
|
254
|
+
/** What has been recorded about one thing in the space: a document's history. */
|
|
255
|
+
forTarget: scoped("audit:read").input(z.object({
|
|
256
|
+
spaceId: uuid,
|
|
257
|
+
targetKind,
|
|
258
|
+
targetId: z.string().max(200),
|
|
259
|
+
limit: z.number().int().min(1).max(200).default(100)
|
|
260
|
+
})).handler(async ({ input, context }) => context.audit.forTarget(input.spaceId, input.targetKind, input.targetId, input.limit)),
|
|
261
|
+
/** Walks the whole chain and says whether every entry still hashes to what it claims. */
|
|
262
|
+
verify: superadmin.handler(async ({ context }) => context.audit.verify())
|
|
263
|
+
};
|
|
264
|
+
//#endregion
|
|
166
265
|
//#region src/routers/content.ts
|
|
167
266
|
const filterSchema = z.object({
|
|
168
267
|
spaceId: uuid,
|
|
@@ -202,6 +301,8 @@ const paginationSchema = pagination({
|
|
|
202
301
|
limit: 25,
|
|
203
302
|
max: 200
|
|
204
303
|
});
|
|
304
|
+
/** What an author says when asking, or a reviewer when answering. */
|
|
305
|
+
const note = z.string().max(2e3);
|
|
205
306
|
const sortSchema = z.array(z.object({
|
|
206
307
|
by: z.enum([
|
|
207
308
|
"position",
|
|
@@ -270,7 +371,10 @@ const contentRouter = {
|
|
|
270
371
|
}),
|
|
271
372
|
create: scoped("content:write").input(saveSchema).handler(async ({ input, context }) => {
|
|
272
373
|
assertOnType(context, input.spaceId, "content:write", input.typeId);
|
|
273
|
-
|
|
374
|
+
const actor = toActor(context, input.spaceId);
|
|
375
|
+
const row = await context.content.create(input, actor);
|
|
376
|
+
if (context.manablox.contentTypes.get(row.typeId).requiresApproval && !input.localizationId && !can(context.principal, input.spaceId, "content:publish", row.typeId)) await context.approvals.request(input.spaceId, row.id, actor);
|
|
377
|
+
return row;
|
|
274
378
|
}),
|
|
275
379
|
update: scoped("content:write").input(saveSchema.extend({ id: uuid })).handler(async ({ input, context }) => {
|
|
276
380
|
assertOnType(context, input.spaceId, "content:write", input.typeId);
|
|
@@ -322,6 +426,49 @@ const contentRouter = {
|
|
|
322
426
|
await assertOnDocument(context, input.spaceId, "content:write", input.id);
|
|
323
427
|
return context.content.createTranslation(input.spaceId, input.id, input.locale, toActor(context, input.spaceId));
|
|
324
428
|
}),
|
|
429
|
+
/** Where a document stands in review: the open request, the last decision, the history. */
|
|
430
|
+
approval: scoped("content:read").input(z.object({
|
|
431
|
+
spaceId: uuid,
|
|
432
|
+
id: uuid
|
|
433
|
+
})).handler(async ({ input, context }) => {
|
|
434
|
+
await assertOnDocument(context, input.spaceId, "content:read", input.id);
|
|
435
|
+
return context.approvals.state(input.spaceId, input.id);
|
|
436
|
+
}),
|
|
437
|
+
/** The open requests the caller may decide on: those of the types they can publish. */
|
|
438
|
+
pendingApprovals: scoped("content:publish").input(z.object({ spaceId: uuid })).handler(async ({ input, context }) => context.approvals.pending(input.spaceId, allowedTypeIds(context.principal, input.spaceId, "content:publish"))),
|
|
439
|
+
requestApproval: scoped("content:write").input(z.object({
|
|
440
|
+
spaceId: uuid,
|
|
441
|
+
id: uuid,
|
|
442
|
+
note: note.optional()
|
|
443
|
+
})).handler(async ({ input, context }) => {
|
|
444
|
+
await assertOnDocument(context, input.spaceId, "content:write", input.id);
|
|
445
|
+
return context.approvals.request(input.spaceId, input.id, toActor(context, input.spaceId), input.note?.trim() || null);
|
|
446
|
+
}),
|
|
447
|
+
withdrawApproval: scoped("content:write").input(z.object({
|
|
448
|
+
spaceId: uuid,
|
|
449
|
+
id: uuid
|
|
450
|
+
})).handler(async ({ input, context }) => {
|
|
451
|
+
await assertOnDocument(context, input.spaceId, "content:write", input.id);
|
|
452
|
+
return context.approvals.withdraw(input.spaceId, input.id, toActor(context, input.spaceId));
|
|
453
|
+
}),
|
|
454
|
+
/** Approves and publishes; the requester is told. */
|
|
455
|
+
approve: scoped("content:publish").input(z.object({
|
|
456
|
+
spaceId: uuid,
|
|
457
|
+
id: uuid,
|
|
458
|
+
note: note.optional()
|
|
459
|
+
})).handler(async ({ input, context }) => {
|
|
460
|
+
await assertOnDocument(context, input.spaceId, "content:publish", input.id);
|
|
461
|
+
return context.approvals.approve(input.spaceId, input.id, toActor(context, input.spaceId), input.note?.trim() || null);
|
|
462
|
+
}),
|
|
463
|
+
/** Sends the document back with a note; the requester is told. */
|
|
464
|
+
reject: scoped("content:publish").input(z.object({
|
|
465
|
+
spaceId: uuid,
|
|
466
|
+
id: uuid,
|
|
467
|
+
note: note.optional()
|
|
468
|
+
})).handler(async ({ input, context }) => {
|
|
469
|
+
await assertOnDocument(context, input.spaceId, "content:publish", input.id);
|
|
470
|
+
return context.approvals.reject(input.spaceId, input.id, toActor(context, input.spaceId), input.note?.trim() || null);
|
|
471
|
+
}),
|
|
325
472
|
versions: scoped("content:read").input(z.object({
|
|
326
473
|
spaceId: uuid,
|
|
327
474
|
id: uuid
|
|
@@ -403,6 +550,7 @@ const contentTypeSchema = z.object({
|
|
|
403
550
|
isPublishable: z.boolean().optional(),
|
|
404
551
|
isVisibleInTree: z.boolean().optional(),
|
|
405
552
|
canBeVisibleInMenu: z.boolean().optional(),
|
|
553
|
+
requiresApproval: z.boolean().optional(),
|
|
406
554
|
fields: z.array(fieldSchema).default([])
|
|
407
555
|
});
|
|
408
556
|
const contentTypeRouter = {
|
|
@@ -519,6 +667,51 @@ const menuRouter = {
|
|
|
519
667
|
})).handler(async ({ input, context }) => context.menus.usedIn(input.spaceId, input.localizationId))
|
|
520
668
|
};
|
|
521
669
|
//#endregion
|
|
670
|
+
//#region src/routers/notification.ts
|
|
671
|
+
const kind = z.enum(NOTIFICATION_KINDS);
|
|
672
|
+
const ids = z.array(uuid).min(1).max(200);
|
|
673
|
+
/**
|
|
674
|
+
* Each kind's channels, every one optional: absent means the default. A partial record,
|
|
675
|
+
* so a kind not named keeps its default; strict objects, so a misspelt channel is an
|
|
676
|
+
* error rather than a silently dropped choice.
|
|
677
|
+
*/
|
|
678
|
+
const preferencesSchema = z.partialRecord(kind, z.strictObject(Object.fromEntries(NOTIFICATION_CHANNELS.map((channel) => [channel, z.boolean().optional()]))));
|
|
679
|
+
/**
|
|
680
|
+
* The caller's own inbox and preferences. Nothing here takes a `userId`: every
|
|
681
|
+
* procedure acts as the signed-in account, so there is no way to read or change what
|
|
682
|
+
* belongs to someone else.
|
|
683
|
+
*/
|
|
684
|
+
const notificationRouter = {
|
|
685
|
+
/** The kinds, the channels, and which channels this instance can actually send on. */
|
|
686
|
+
catalog: authed.handler(async ({ context }) => context.notifications.catalog()),
|
|
687
|
+
list: authed.input(z.object({
|
|
688
|
+
unreadOnly: z.boolean().default(false),
|
|
689
|
+
kinds: z.array(kind).optional(),
|
|
690
|
+
/** A space's notifications, `null` for instance-wide ones, absent for all. */
|
|
691
|
+
spaceId: uuid.nullable().optional(),
|
|
692
|
+
pagination: pagination({
|
|
693
|
+
limit: 25,
|
|
694
|
+
max: 100
|
|
695
|
+
}).optional()
|
|
696
|
+
}).default({ unreadOnly: false })).handler(async ({ input, context }) => context.notifications.list(context.principal.userId, {
|
|
697
|
+
unreadOnly: input.unreadOnly,
|
|
698
|
+
kinds: input.kinds,
|
|
699
|
+
spaceId: input.spaceId
|
|
700
|
+
}, input.pagination ?? {
|
|
701
|
+
limit: 25,
|
|
702
|
+
offset: 0
|
|
703
|
+
})),
|
|
704
|
+
unreadCount: authed.handler(async ({ context }) => ({ count: await context.notifications.unreadCount(context.principal.userId) })),
|
|
705
|
+
markRead: authed.input(z.object({ ids })).handler(async ({ input, context }) => ({ changed: await context.notifications.markRead(context.principal.userId, input.ids) })),
|
|
706
|
+
markUnread: authed.input(z.object({ ids })).handler(async ({ input, context }) => ({ changed: await context.notifications.markUnread(context.principal.userId, input.ids) })),
|
|
707
|
+
markAllRead: authed.handler(async ({ context }) => ({ changed: await context.notifications.markAllRead(context.principal.userId) })),
|
|
708
|
+
delete: authed.input(z.object({ ids })).handler(async ({ input, context }) => ({ deleted: await context.notifications.delete(context.principal.userId, input.ids) })),
|
|
709
|
+
deleteRead: authed.handler(async ({ context }) => ({ deleted: await context.notifications.deleteRead(context.principal.userId) })),
|
|
710
|
+
/** Every kind with the caller's effective choice per channel, defaults filled in. */
|
|
711
|
+
preferences: authed.handler(async ({ context }) => context.notifications.preferences(context.principal.userId)),
|
|
712
|
+
setPreferences: authed.input(z.object({ preferences: preferencesSchema })).handler(async ({ input, context }) => context.notifications.setPreferences(context.principal.userId, input.preferences))
|
|
713
|
+
};
|
|
714
|
+
//#endregion
|
|
522
715
|
//#region src/routers/role.ts
|
|
523
716
|
const roleSchema = z.object({
|
|
524
717
|
spaceId: uuid,
|
|
@@ -559,6 +752,8 @@ const roleRouter = {
|
|
|
559
752
|
};
|
|
560
753
|
//#endregion
|
|
561
754
|
//#region src/routers/space.ts
|
|
755
|
+
/** The parts of a space an export carries or an import restores; all of them when absent. */
|
|
756
|
+
const exportSections = z.array(z.enum(SPACE_EXPORT_SECTIONS)).min(1);
|
|
562
757
|
const spaceSchema = z.object({
|
|
563
758
|
name: z.string().min(1).max(200),
|
|
564
759
|
machineName,
|
|
@@ -586,7 +781,21 @@ const spaceRouter = {
|
|
|
586
781
|
return allowed ? all.filter((space) => allowed.includes(space.id)) : all;
|
|
587
782
|
}),
|
|
588
783
|
get: scoped("space:read").input(z.object({ spaceId: uuid })).handler(async ({ input, context }) => context.repos.spaces.findById(input.spaceId)),
|
|
589
|
-
|
|
784
|
+
/**
|
|
785
|
+
* `starter` fills the new space with the basic setup, a content model, a few
|
|
786
|
+
* published pages and a main menu, so the first thing its owner sees is a site rather
|
|
787
|
+
* than an empty tree. See `applySpaceStarter`.
|
|
788
|
+
*/
|
|
789
|
+
create: superadmin.input(spaceSchema.extend({ starter: z.boolean().default(false) })).handler(async ({ input, context }) => {
|
|
790
|
+
const { starter, ...data } = input;
|
|
791
|
+
const { principal } = context;
|
|
792
|
+
const space = await context.spaces.create(data, principal.userId);
|
|
793
|
+
if (starter) await applySpaceStarter(context, space, {
|
|
794
|
+
userId: principal.userId,
|
|
795
|
+
roles: actorRoles(principal, space.id)
|
|
796
|
+
});
|
|
797
|
+
return space;
|
|
798
|
+
}),
|
|
590
799
|
update: scoped("space:write").input(spaceSchema.partial().extend({ spaceId: uuid })).handler(async ({ input, context }) => {
|
|
591
800
|
const { spaceId, ...data } = input;
|
|
592
801
|
return context.spaces.update(spaceId, data);
|
|
@@ -617,9 +826,15 @@ const spaceRouter = {
|
|
|
617
826
|
* an export is every field of every document in one file, regardless of who may read
|
|
618
827
|
* what.
|
|
619
828
|
*/
|
|
620
|
-
export: scoped("space:write").input(z.object({
|
|
829
|
+
export: scoped("space:write").input(z.object({
|
|
830
|
+
spaceId: uuid,
|
|
831
|
+
sections: exportSections.optional()
|
|
832
|
+
})).handler(async ({ input, context }) => context.spaces.export(input.spaceId, input.sections)),
|
|
621
833
|
/** Restores such a document into an instance that does not hold the space yet. Superadmin, because it creates a space. */
|
|
622
|
-
import: superadmin.input(z.object({
|
|
834
|
+
import: superadmin.input(z.object({
|
|
835
|
+
payload: z.unknown(),
|
|
836
|
+
sections: exportSections.optional()
|
|
837
|
+
})).handler(async ({ input, context }) => context.spaces.import(input.payload, context.principal.userId, input.sections)),
|
|
623
838
|
members: scoped("user:read").input(z.object({ spaceId: uuid })).handler(async ({ input, context }) => context.spaces.members(input.spaceId)),
|
|
624
839
|
grant: scoped("user:write").input(z.object({
|
|
625
840
|
spaceId: uuid,
|
|
@@ -730,6 +945,19 @@ const userRouter = {
|
|
|
730
945
|
await context.users.delete(context.principal.userId, input.userId);
|
|
731
946
|
return { ok: true };
|
|
732
947
|
}),
|
|
948
|
+
/** The caller's own profile: name and email. The instance role is not theirs to set. */
|
|
949
|
+
updateProfile: authed.input(z.object({
|
|
950
|
+
name: displayName.optional(),
|
|
951
|
+
email: email.optional()
|
|
952
|
+
})).handler(async ({ input, context }) => context.users.update(context.principal.userId, input)),
|
|
953
|
+
/** A new password for the caller, given the current one. Other sessions stay signed in. */
|
|
954
|
+
changePassword: authed.input(z.object({
|
|
955
|
+
currentPassword: z.string().min(1).max(200),
|
|
956
|
+
password
|
|
957
|
+
})).handler(async ({ input, context }) => {
|
|
958
|
+
await context.users.changePassword(context.principal.userId, input.currentPassword, input.password);
|
|
959
|
+
return { ok: true };
|
|
960
|
+
}),
|
|
733
961
|
apiKeys: authed.handler(async ({ context }) => context.apiKeys.list(context.principal.userId)),
|
|
734
962
|
issueApiKey: authed.input(z.object({
|
|
735
963
|
name: z.string().min(1).max(100),
|
|
@@ -958,7 +1186,7 @@ const workflowRouter = {
|
|
|
958
1186
|
//#region src/context.ts
|
|
959
1187
|
/** The runtime's RPC-facing slice, without whatever else the host keeps on it. */
|
|
960
1188
|
function pickRpcRuntime(runtime) {
|
|
961
|
-
const { manablox, repos, auth, apiKeys, media, content, contentTypes, spaces, users, menus, roles, workflows } = runtime;
|
|
1189
|
+
const { manablox, repos, auth, apiKeys, media, content, contentTypes, spaces, users, menus, roles, workflows, audit, notifications, approvals } = runtime;
|
|
962
1190
|
return {
|
|
963
1191
|
manablox,
|
|
964
1192
|
repos,
|
|
@@ -971,7 +1199,10 @@ function pickRpcRuntime(runtime) {
|
|
|
971
1199
|
users,
|
|
972
1200
|
menus,
|
|
973
1201
|
roles,
|
|
974
|
-
workflows
|
|
1202
|
+
workflows,
|
|
1203
|
+
audit,
|
|
1204
|
+
notifications,
|
|
1205
|
+
approvals
|
|
975
1206
|
};
|
|
976
1207
|
}
|
|
977
1208
|
//#endregion
|
|
@@ -988,7 +1219,9 @@ const router = {
|
|
|
988
1219
|
users: userRouter,
|
|
989
1220
|
menus: menuRouter,
|
|
990
1221
|
roles: roleRouter,
|
|
991
|
-
workflows: workflowRouter
|
|
1222
|
+
workflows: workflowRouter,
|
|
1223
|
+
audit: auditRouter,
|
|
1224
|
+
notifications: notificationRouter
|
|
992
1225
|
};
|
|
993
1226
|
//#endregion
|
|
994
1227
|
export { authed, base, pickRpcRuntime, router, scoped, superadmin, toOrpcError };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@manablox/api-rpc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
"main": "./dist/index.js",
|
|
12
12
|
"types": "./dist/index.d.ts",
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@manablox/auth": "0.
|
|
15
|
-
"@manablox/core": "0.
|
|
16
|
-
"@manablox/db": "0.
|
|
17
|
-
"@manablox/media": "0.
|
|
18
|
-
"@manablox/services": "0.
|
|
19
|
-
"@manablox/workflows": "0.
|
|
14
|
+
"@manablox/auth": "0.4.0",
|
|
15
|
+
"@manablox/core": "0.4.0",
|
|
16
|
+
"@manablox/db": "0.4.0",
|
|
17
|
+
"@manablox/media": "0.4.0",
|
|
18
|
+
"@manablox/services": "0.4.0",
|
|
19
|
+
"@manablox/workflows": "0.4.0",
|
|
20
20
|
"@orpc/server": "^1.15.0",
|
|
21
21
|
"@orpc/openapi": "^1.15.0",
|
|
22
22
|
"zod": "^4.5.4"
|