@btst/stack 2.4.0 → 2.5.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/packages/stack/src/plugins/ai-chat/api/plugin.cjs +33 -47
- package/dist/packages/stack/src/plugins/ai-chat/api/plugin.mjs +33 -47
- package/dist/packages/stack/src/plugins/ai-chat/client/plugin.cjs +14 -21
- package/dist/packages/stack/src/plugins/ai-chat/client/plugin.mjs +15 -22
- package/dist/packages/stack/src/plugins/blog/api/plugin.cjs +28 -45
- package/dist/packages/stack/src/plugins/blog/api/plugin.mjs +22 -39
- package/dist/packages/stack/src/plugins/blog/client/plugin.cjs +23 -27
- package/dist/packages/stack/src/plugins/blog/client/plugin.mjs +24 -28
- package/dist/packages/stack/src/plugins/cms/api/plugin.cjs +14 -17
- package/dist/packages/stack/src/plugins/cms/api/plugin.mjs +14 -17
- package/dist/packages/stack/src/plugins/cms/client/plugin.cjs +11 -15
- package/dist/packages/stack/src/plugins/cms/client/plugin.mjs +12 -16
- package/dist/packages/stack/src/plugins/form-builder/api/plugin.cjs +58 -62
- package/dist/packages/stack/src/plugins/form-builder/api/plugin.mjs +58 -62
- package/dist/packages/stack/src/plugins/form-builder/client/plugin.cjs +12 -12
- package/dist/packages/stack/src/plugins/form-builder/client/plugin.mjs +13 -13
- package/dist/packages/stack/src/plugins/kanban/api/plugin.cjs +86 -117
- package/dist/packages/stack/src/plugins/kanban/api/plugin.mjs +83 -114
- package/dist/packages/stack/src/plugins/kanban/client/plugin.cjs +22 -29
- package/dist/packages/stack/src/plugins/kanban/client/plugin.mjs +23 -30
- package/dist/packages/stack/src/plugins/ui-builder/client/plugin.cjs +8 -8
- package/dist/packages/stack/src/plugins/ui-builder/client/plugin.mjs +9 -9
- package/dist/packages/stack/src/plugins/utils.cjs +42 -0
- package/dist/packages/stack/src/plugins/utils.mjs +41 -1
- package/dist/plugins/ai-chat/api/index.d.cts +1 -1
- package/dist/plugins/ai-chat/api/index.d.mts +1 -1
- package/dist/plugins/ai-chat/api/index.d.ts +1 -1
- package/dist/plugins/ai-chat/client/hooks/index.d.cts +1 -1
- package/dist/plugins/ai-chat/client/hooks/index.d.mts +1 -1
- package/dist/plugins/ai-chat/client/hooks/index.d.ts +1 -1
- package/dist/plugins/ai-chat/client/index.d.cts +8 -8
- package/dist/plugins/ai-chat/client/index.d.mts +8 -8
- package/dist/plugins/ai-chat/client/index.d.ts +8 -8
- package/dist/plugins/ai-chat/query-keys.d.cts +1 -1
- package/dist/plugins/ai-chat/query-keys.d.mts +1 -1
- package/dist/plugins/ai-chat/query-keys.d.ts +1 -1
- package/dist/plugins/blog/api/index.d.cts +1 -1
- package/dist/plugins/blog/api/index.d.mts +1 -1
- package/dist/plugins/blog/api/index.d.ts +1 -1
- package/dist/plugins/blog/client/index.d.cts +12 -12
- package/dist/plugins/blog/client/index.d.mts +12 -12
- package/dist/plugins/blog/client/index.d.ts +12 -12
- package/dist/plugins/blog/query-keys.d.cts +1 -1
- package/dist/plugins/blog/query-keys.d.mts +1 -1
- package/dist/plugins/blog/query-keys.d.ts +1 -1
- package/dist/plugins/client/index.cjs +1 -0
- package/dist/plugins/client/index.d.cts +8 -1
- package/dist/plugins/client/index.d.mts +8 -1
- package/dist/plugins/client/index.d.ts +8 -1
- package/dist/plugins/client/index.mjs +1 -1
- package/dist/plugins/cms/api/index.d.cts +2 -2
- package/dist/plugins/cms/api/index.d.mts +2 -2
- package/dist/plugins/cms/api/index.d.ts +2 -2
- package/dist/plugins/cms/client/hooks/index.d.cts +1 -1
- package/dist/plugins/cms/client/hooks/index.d.mts +1 -1
- package/dist/plugins/cms/client/hooks/index.d.ts +1 -1
- package/dist/plugins/cms/client/index.d.cts +6 -6
- package/dist/plugins/cms/client/index.d.mts +6 -6
- package/dist/plugins/cms/client/index.d.ts +6 -6
- package/dist/plugins/cms/query-keys.d.cts +2 -2
- package/dist/plugins/cms/query-keys.d.mts +2 -2
- package/dist/plugins/cms/query-keys.d.ts +2 -2
- package/dist/plugins/form-builder/api/index.d.cts +2 -2
- package/dist/plugins/form-builder/api/index.d.mts +2 -2
- package/dist/plugins/form-builder/api/index.d.ts +2 -2
- package/dist/plugins/form-builder/client/components/index.d.cts +1 -1
- package/dist/plugins/form-builder/client/components/index.d.mts +1 -1
- package/dist/plugins/form-builder/client/components/index.d.ts +1 -1
- package/dist/plugins/form-builder/client/hooks/index.d.cts +1 -1
- package/dist/plugins/form-builder/client/hooks/index.d.mts +1 -1
- package/dist/plugins/form-builder/client/hooks/index.d.ts +1 -1
- package/dist/plugins/form-builder/client/index.d.cts +6 -6
- package/dist/plugins/form-builder/client/index.d.mts +6 -6
- package/dist/plugins/form-builder/client/index.d.ts +6 -6
- package/dist/plugins/form-builder/query-keys.d.cts +2 -2
- package/dist/plugins/form-builder/query-keys.d.mts +2 -2
- package/dist/plugins/form-builder/query-keys.d.ts +2 -2
- package/dist/plugins/kanban/api/index.d.cts +1 -1
- package/dist/plugins/kanban/api/index.d.mts +1 -1
- package/dist/plugins/kanban/api/index.d.ts +1 -1
- package/dist/plugins/kanban/client/index.d.cts +12 -12
- package/dist/plugins/kanban/client/index.d.mts +12 -12
- package/dist/plugins/kanban/client/index.d.ts +12 -12
- package/dist/plugins/kanban/query-keys.d.cts +1 -1
- package/dist/plugins/kanban/query-keys.d.mts +1 -1
- package/dist/plugins/kanban/query-keys.d.ts +1 -1
- package/dist/plugins/ui-builder/client/hooks/index.d.cts +1 -1
- package/dist/plugins/ui-builder/client/hooks/index.d.mts +1 -1
- package/dist/plugins/ui-builder/client/hooks/index.d.ts +1 -1
- package/dist/plugins/ui-builder/client/index.d.cts +3 -3
- package/dist/plugins/ui-builder/client/index.d.mts +3 -3
- package/dist/plugins/ui-builder/client/index.d.ts +3 -3
- package/dist/plugins/ui-builder/index.d.cts +2 -2
- package/dist/plugins/ui-builder/index.d.mts +2 -2
- package/dist/plugins/ui-builder/index.d.ts +2 -2
- package/dist/shared/{stack.C-WUPMT6.d.cts → stack.B2xZTSiO.d.cts} +4 -4
- package/dist/shared/{stack.CczspVn2.d.mts → stack.B58oHdqm.d.mts} +1 -1
- package/dist/shared/{stack.CVDTkMoO.d.mts → stack.B8QD11QU.d.cts} +7 -7
- package/dist/shared/{stack.CVDTkMoO.d.cts → stack.B8QD11QU.d.mts} +7 -7
- package/dist/shared/{stack.CVDTkMoO.d.ts → stack.B8QD11QU.d.ts} +7 -7
- package/dist/shared/{stack.Kq2-QzOC.d.ts → stack.BDVEpue1.d.ts} +2 -2
- package/dist/shared/{stack.B7ONvlD_.d.mts → stack.BTvbxZvw.d.cts} +2 -2
- package/dist/shared/{stack.DdI5W6MB.d.mts → stack.BozPgbrZ.d.cts} +19 -19
- package/dist/shared/{stack.DdI5W6MB.d.cts → stack.BozPgbrZ.d.mts} +19 -19
- package/dist/shared/{stack.DdI5W6MB.d.ts → stack.BozPgbrZ.d.ts} +19 -19
- package/dist/shared/{stack.BUkC2EsZ.d.cts → stack.C9Mg2Q46.d.cts} +1 -1
- package/dist/shared/{stack.BEn34wW6.d.ts → stack.CTDVxbrA.d.ts} +12 -12
- package/dist/shared/{stack.C-Ptrz8s.d.ts → stack.Cj_zKww4.d.ts} +4 -4
- package/dist/shared/{stack.BepFXT3w.d.mts → stack.CxaFNQCV.d.mts} +25 -25
- package/dist/shared/{stack.DWoCZff7.d.cts → stack.D-b5zbPm.d.cts} +12 -12
- package/dist/shared/{stack.kcdnD4gA.d.cts → stack.DTtmJPQO.d.mts} +2 -2
- package/dist/shared/{stack.CL8ts1Mu.d.ts → stack.DXnclTG7.d.ts} +8 -8
- package/dist/shared/{stack.heOA9gzA.d.cts → stack.DaZM10cp.d.cts} +8 -8
- package/dist/shared/{stack.DTDxgFj8.d.mts → stack.FVWf2JhZ.d.mts} +12 -12
- package/dist/shared/{stack.Dk5r4W1F.d.mts → stack.cfCkioTe.d.mts} +8 -8
- package/dist/shared/{stack.6fUOjLs9.d.mts → stack.dH7u-TJH.d.mts} +4 -4
- package/dist/shared/{stack.CgWzG5jH.d.ts → stack.j75TpKh2.d.ts} +25 -25
- package/dist/shared/{stack.D3GB6wKv.d.cts → stack.n1_i1p2B.d.cts} +25 -25
- package/dist/shared/{stack.DASmUVjX.d.ts → stack.sO33ZDhK.d.ts} +1 -1
- package/package.json +1 -1
- package/src/plugins/ai-chat/api/plugin.ts +48 -63
- package/src/plugins/ai-chat/client/plugin.tsx +23 -31
- package/src/plugins/blog/api/plugin.ts +31 -47
- package/src/plugins/blog/client/plugin.tsx +36 -39
- package/src/plugins/client/index.ts +5 -1
- package/src/plugins/cms/api/plugin.ts +14 -17
- package/src/plugins/cms/client/plugin.tsx +18 -21
- package/src/plugins/cms/types.ts +7 -7
- package/src/plugins/form-builder/api/plugin.ts +64 -64
- package/src/plugins/form-builder/client/plugin.tsx +19 -18
- package/src/plugins/form-builder/types.ts +19 -24
- package/src/plugins/kanban/api/plugin.ts +111 -136
- package/src/plugins/kanban/client/plugin.tsx +35 -41
- package/src/plugins/ui-builder/client/plugin.tsx +11 -10
- package/src/plugins/ui-builder/types.ts +4 -4
- package/src/plugins/utils.ts +92 -1
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
defineClientPlugin,
|
|
5
5
|
createApiClient,
|
|
6
6
|
isConnectionError,
|
|
7
|
+
runClientHookWithShim,
|
|
7
8
|
} from "@btst/stack/plugins/client";
|
|
8
9
|
import { createRoute } from "@btst/yar";
|
|
9
10
|
import type { QueryClient } from "@tanstack/react-query";
|
|
@@ -53,24 +54,24 @@ export interface LoaderContext {
|
|
|
53
54
|
*/
|
|
54
55
|
export interface FormBuilderClientHooks {
|
|
55
56
|
/**
|
|
56
|
-
* Called before loading the form list page.
|
|
57
|
+
* Called before loading the form list page. Throw an error to cancel loading.
|
|
57
58
|
* @param context - Loader context with path, params, etc.
|
|
58
59
|
*/
|
|
59
|
-
beforeLoadFormList?: (context: LoaderContext) => Promise<
|
|
60
|
+
beforeLoadFormList?: (context: LoaderContext) => Promise<void> | void;
|
|
60
61
|
/**
|
|
61
62
|
* Called after the form list is loaded.
|
|
62
63
|
* @param context - Loader context
|
|
63
64
|
*/
|
|
64
65
|
afterLoadFormList?: (context: LoaderContext) => Promise<void> | void;
|
|
65
66
|
/**
|
|
66
|
-
* Called before loading the form builder page.
|
|
67
|
+
* Called before loading the form builder page. Throw an error to cancel loading.
|
|
67
68
|
* @param id - The form ID (undefined for new forms)
|
|
68
69
|
* @param context - Loader context
|
|
69
70
|
*/
|
|
70
71
|
beforeLoadFormBuilder?: (
|
|
71
72
|
id: string | undefined,
|
|
72
73
|
context: LoaderContext,
|
|
73
|
-
) => Promise<
|
|
74
|
+
) => Promise<void> | void;
|
|
74
75
|
/**
|
|
75
76
|
* Called after the form builder is loaded.
|
|
76
77
|
* @param id - The form ID (undefined for new forms)
|
|
@@ -81,14 +82,14 @@ export interface FormBuilderClientHooks {
|
|
|
81
82
|
context: LoaderContext,
|
|
82
83
|
) => Promise<void> | void;
|
|
83
84
|
/**
|
|
84
|
-
* Called before loading the submissions page.
|
|
85
|
+
* Called before loading the submissions page. Throw an error to cancel loading.
|
|
85
86
|
* @param formId - The form ID
|
|
86
87
|
* @param context - Loader context
|
|
87
88
|
*/
|
|
88
89
|
beforeLoadSubmissions?: (
|
|
89
90
|
formId: string,
|
|
90
91
|
context: LoaderContext,
|
|
91
|
-
) => Promise<
|
|
92
|
+
) => Promise<void> | void;
|
|
92
93
|
/**
|
|
93
94
|
* Called after the submissions page is loaded.
|
|
94
95
|
* @param formId - The form ID
|
|
@@ -146,10 +147,10 @@ function createFormListLoader(config: FormBuilderClientConfig) {
|
|
|
146
147
|
try {
|
|
147
148
|
// Before hook - authorization check
|
|
148
149
|
if (hooks?.beforeLoadFormList) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
await runClientHookWithShim(
|
|
151
|
+
() => hooks.beforeLoadFormList!(context),
|
|
152
|
+
"Load prevented by beforeLoadFormList hook",
|
|
153
|
+
);
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
const client = createApiClient<FormBuilderApiRouter>({
|
|
@@ -235,10 +236,10 @@ function createFormBuilderLoader(
|
|
|
235
236
|
try {
|
|
236
237
|
// Before hook - authorization check
|
|
237
238
|
if (hooks?.beforeLoadFormBuilder) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
await runClientHookWithShim(
|
|
240
|
+
() => hooks.beforeLoadFormBuilder!(id, context),
|
|
241
|
+
"Load prevented by beforeLoadFormBuilder hook",
|
|
242
|
+
);
|
|
242
243
|
}
|
|
243
244
|
|
|
244
245
|
const client = createApiClient<FormBuilderApiRouter>({
|
|
@@ -309,10 +310,10 @@ function createSubmissionsLoader(
|
|
|
309
310
|
try {
|
|
310
311
|
// Before hook - authorization check
|
|
311
312
|
if (hooks?.beforeLoadSubmissions) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
313
|
+
await runClientHookWithShim(
|
|
314
|
+
() => hooks.beforeLoadSubmissions!(formId, context),
|
|
315
|
+
"Load prevented by beforeLoadSubmissions hook",
|
|
316
|
+
);
|
|
316
317
|
}
|
|
317
318
|
|
|
318
319
|
const client = createApiClient<FormBuilderApiRouter>({
|
|
@@ -167,23 +167,21 @@ export interface FormUpdate {
|
|
|
167
167
|
* Backend hooks for Form Builder plugin
|
|
168
168
|
*
|
|
169
169
|
* All CRUD hooks receive ipAddress and headers for auth/rate limiting.
|
|
170
|
-
*
|
|
170
|
+
* Throw an error from onBefore* hooks to reject the operation (throws 403).
|
|
171
171
|
*/
|
|
172
172
|
export interface FormBuilderBackendHooks {
|
|
173
173
|
// ============================================================================
|
|
174
174
|
// FORM CRUD HOOKS (Admin operations)
|
|
175
175
|
// ============================================================================
|
|
176
176
|
|
|
177
|
-
/** Called before listing forms.
|
|
178
|
-
onBeforeListForms?: (
|
|
179
|
-
ctx: FormBuilderHookContext,
|
|
180
|
-
) => Promise<boolean> | boolean;
|
|
177
|
+
/** Called before listing forms. Throw an error to deny access (403). */
|
|
178
|
+
onBeforeListForms?: (ctx: FormBuilderHookContext) => Promise<void> | void;
|
|
181
179
|
|
|
182
|
-
/** Called before creating a form.
|
|
180
|
+
/** Called before creating a form. Throw an error to deny, or return modified data. */
|
|
183
181
|
onBeforeFormCreated?: (
|
|
184
182
|
data: FormInput,
|
|
185
183
|
ctx: FormBuilderHookContext,
|
|
186
|
-
) => Promise<FormInput |
|
|
184
|
+
) => Promise<FormInput | void> | FormInput | void;
|
|
187
185
|
|
|
188
186
|
/** Called after a form is created */
|
|
189
187
|
onAfterFormCreated?: (
|
|
@@ -191,18 +189,18 @@ export interface FormBuilderBackendHooks {
|
|
|
191
189
|
ctx: FormBuilderHookContext,
|
|
192
190
|
) => Promise<void> | void;
|
|
193
191
|
|
|
194
|
-
/** Called before getting a form by ID or slug.
|
|
192
|
+
/** Called before getting a form by ID or slug. Throw an error to deny access. */
|
|
195
193
|
onBeforeGetForm?: (
|
|
196
194
|
idOrSlug: string,
|
|
197
195
|
ctx: FormBuilderHookContext,
|
|
198
|
-
) => Promise<
|
|
196
|
+
) => Promise<void> | void;
|
|
199
197
|
|
|
200
|
-
/** Called before updating a form.
|
|
198
|
+
/** Called before updating a form. Throw an error to deny, or return modified data. */
|
|
201
199
|
onBeforeFormUpdated?: (
|
|
202
200
|
id: string,
|
|
203
201
|
data: FormUpdate,
|
|
204
202
|
ctx: FormBuilderHookContext,
|
|
205
|
-
) => Promise<FormUpdate |
|
|
203
|
+
) => Promise<FormUpdate | void> | FormUpdate | void;
|
|
206
204
|
|
|
207
205
|
/** Called after a form is updated */
|
|
208
206
|
onAfterFormUpdated?: (
|
|
@@ -210,11 +208,11 @@ export interface FormBuilderBackendHooks {
|
|
|
210
208
|
ctx: FormBuilderHookContext,
|
|
211
209
|
) => Promise<void> | void;
|
|
212
210
|
|
|
213
|
-
/** Called before deleting a form.
|
|
211
|
+
/** Called before deleting a form. Throw an error to deny. */
|
|
214
212
|
onBeforeFormDeleted?: (
|
|
215
213
|
id: string,
|
|
216
214
|
ctx: FormBuilderHookContext,
|
|
217
|
-
) => Promise<
|
|
215
|
+
) => Promise<void> | void;
|
|
218
216
|
|
|
219
217
|
/** Called after a form is deleted */
|
|
220
218
|
onAfterFormDeleted?: (
|
|
@@ -230,16 +228,13 @@ export interface FormBuilderBackendHooks {
|
|
|
230
228
|
* Called before processing a form submission.
|
|
231
229
|
* Use for: spam protection, rate limiting, data validation/enrichment.
|
|
232
230
|
*
|
|
233
|
-
*
|
|
231
|
+
* Throw an error to reject submission (400), or return modified data to continue.
|
|
234
232
|
*/
|
|
235
233
|
onBeforeSubmission?: (
|
|
236
234
|
formSlug: string,
|
|
237
235
|
data: Record<string, unknown>,
|
|
238
236
|
ctx: SubmissionHookContext,
|
|
239
|
-
) =>
|
|
240
|
-
| Promise<Record<string, unknown> | false>
|
|
241
|
-
| Record<string, unknown>
|
|
242
|
-
| false;
|
|
237
|
+
) => Promise<Record<string, unknown> | void> | Record<string, unknown> | void;
|
|
243
238
|
|
|
244
239
|
/**
|
|
245
240
|
* Called after a submission is saved.
|
|
@@ -263,23 +258,23 @@ export interface FormBuilderBackendHooks {
|
|
|
263
258
|
// SUBMISSIONS MANAGEMENT HOOKS (Admin viewing submissions)
|
|
264
259
|
// ============================================================================
|
|
265
260
|
|
|
266
|
-
/** Called before listing submissions.
|
|
261
|
+
/** Called before listing submissions. Throw an error to deny access (403). */
|
|
267
262
|
onBeforeListSubmissions?: (
|
|
268
263
|
formId: string,
|
|
269
264
|
ctx: FormBuilderHookContext,
|
|
270
|
-
) => Promise<
|
|
265
|
+
) => Promise<void> | void;
|
|
271
266
|
|
|
272
|
-
/** Called before getting a submission.
|
|
267
|
+
/** Called before getting a submission. Throw an error to deny access. */
|
|
273
268
|
onBeforeGetSubmission?: (
|
|
274
269
|
submissionId: string,
|
|
275
270
|
ctx: FormBuilderHookContext,
|
|
276
|
-
) => Promise<
|
|
271
|
+
) => Promise<void> | void;
|
|
277
272
|
|
|
278
|
-
/** Called before deleting a submission.
|
|
273
|
+
/** Called before deleting a submission. Throw an error to deny. */
|
|
279
274
|
onBeforeSubmissionDeleted?: (
|
|
280
275
|
submissionId: string,
|
|
281
276
|
ctx: FormBuilderHookContext,
|
|
282
|
-
) => Promise<
|
|
277
|
+
) => Promise<void> | void;
|
|
283
278
|
|
|
284
279
|
/** Called after a submission is deleted */
|
|
285
280
|
onAfterSubmissionDeleted?: (
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
getKanbanColumnsByBoardId,
|
|
31
31
|
} from "./mutations";
|
|
32
32
|
import { KANBAN_QUERY_KEYS } from "./query-key-defs";
|
|
33
|
+
import { runHookWithShim } from "../../utils";
|
|
33
34
|
import { serializeBoard } from "./serializers";
|
|
34
35
|
import type { QueryClient } from "@tanstack/react-query";
|
|
35
36
|
|
|
@@ -101,41 +102,41 @@ export interface KanbanApiContext<
|
|
|
101
102
|
export interface KanbanBackendHooks {
|
|
102
103
|
// ============ Board Hooks ============
|
|
103
104
|
/**
|
|
104
|
-
* Called before listing boards.
|
|
105
|
+
* Called before listing boards. Throw an error to deny access.
|
|
105
106
|
*/
|
|
106
107
|
onBeforeListBoards?: (
|
|
107
108
|
filter: z.infer<typeof BoardListQuerySchema>,
|
|
108
109
|
context: KanbanApiContext,
|
|
109
|
-
) => Promise<
|
|
110
|
+
) => Promise<void> | void;
|
|
110
111
|
/**
|
|
111
|
-
* Called before creating a board.
|
|
112
|
+
* Called before creating a board. Throw an error to deny access.
|
|
112
113
|
*/
|
|
113
114
|
onBeforeCreateBoard?: (
|
|
114
115
|
data: z.infer<typeof createBoardSchema>,
|
|
115
116
|
context: KanbanApiContext,
|
|
116
|
-
) => Promise<
|
|
117
|
+
) => Promise<void> | void;
|
|
117
118
|
/**
|
|
118
|
-
* Called before reading a single board.
|
|
119
|
+
* Called before reading a single board. Throw an error to deny access.
|
|
119
120
|
*/
|
|
120
121
|
onBeforeReadBoard?: (
|
|
121
122
|
boardId: string,
|
|
122
123
|
context: KanbanApiContext,
|
|
123
|
-
) => Promise<
|
|
124
|
+
) => Promise<void> | void;
|
|
124
125
|
/**
|
|
125
|
-
* Called before updating a board.
|
|
126
|
+
* Called before updating a board. Throw an error to deny access.
|
|
126
127
|
*/
|
|
127
128
|
onBeforeUpdateBoard?: (
|
|
128
129
|
boardId: string,
|
|
129
130
|
data: z.infer<typeof updateBoardSchema>,
|
|
130
131
|
context: KanbanApiContext,
|
|
131
|
-
) => Promise<
|
|
132
|
+
) => Promise<void> | void;
|
|
132
133
|
/**
|
|
133
|
-
* Called before deleting a board.
|
|
134
|
+
* Called before deleting a board. Throw an error to deny access.
|
|
134
135
|
*/
|
|
135
136
|
onBeforeDeleteBoard?: (
|
|
136
137
|
boardId: string,
|
|
137
138
|
context: KanbanApiContext,
|
|
138
|
-
) => Promise<
|
|
139
|
+
) => Promise<void> | void;
|
|
139
140
|
|
|
140
141
|
/**
|
|
141
142
|
* Called after boards are listed successfully.
|
|
@@ -214,27 +215,27 @@ export interface KanbanBackendHooks {
|
|
|
214
215
|
|
|
215
216
|
// ============ Column Hooks ============
|
|
216
217
|
/**
|
|
217
|
-
* Called before creating a column.
|
|
218
|
+
* Called before creating a column. Throw an error to deny access.
|
|
218
219
|
*/
|
|
219
220
|
onBeforeCreateColumn?: (
|
|
220
221
|
data: z.infer<typeof createColumnSchema>,
|
|
221
222
|
context: KanbanApiContext,
|
|
222
|
-
) => Promise<
|
|
223
|
+
) => Promise<void> | void;
|
|
223
224
|
/**
|
|
224
|
-
* Called before updating a column.
|
|
225
|
+
* Called before updating a column. Throw an error to deny access.
|
|
225
226
|
*/
|
|
226
227
|
onBeforeUpdateColumn?: (
|
|
227
228
|
columnId: string,
|
|
228
229
|
data: z.infer<typeof updateColumnSchema>,
|
|
229
230
|
context: KanbanApiContext,
|
|
230
|
-
) => Promise<
|
|
231
|
+
) => Promise<void> | void;
|
|
231
232
|
/**
|
|
232
|
-
* Called before deleting a column.
|
|
233
|
+
* Called before deleting a column. Throw an error to deny access.
|
|
233
234
|
*/
|
|
234
235
|
onBeforeDeleteColumn?: (
|
|
235
236
|
columnId: string,
|
|
236
237
|
context: KanbanApiContext,
|
|
237
|
-
) => Promise<
|
|
238
|
+
) => Promise<void> | void;
|
|
238
239
|
|
|
239
240
|
/**
|
|
240
241
|
* Called after a column is created successfully
|
|
@@ -260,27 +261,27 @@ export interface KanbanBackendHooks {
|
|
|
260
261
|
|
|
261
262
|
// ============ Task Hooks ============
|
|
262
263
|
/**
|
|
263
|
-
* Called before creating a task.
|
|
264
|
+
* Called before creating a task. Throw an error to deny access.
|
|
264
265
|
*/
|
|
265
266
|
onBeforeCreateTask?: (
|
|
266
267
|
data: z.infer<typeof createTaskSchema>,
|
|
267
268
|
context: KanbanApiContext,
|
|
268
|
-
) => Promise<
|
|
269
|
+
) => Promise<void> | void;
|
|
269
270
|
/**
|
|
270
|
-
* Called before updating a task.
|
|
271
|
+
* Called before updating a task. Throw an error to deny access.
|
|
271
272
|
*/
|
|
272
273
|
onBeforeUpdateTask?: (
|
|
273
274
|
taskId: string,
|
|
274
275
|
data: z.infer<typeof updateTaskSchema>,
|
|
275
276
|
context: KanbanApiContext,
|
|
276
|
-
) => Promise<
|
|
277
|
+
) => Promise<void> | void;
|
|
277
278
|
/**
|
|
278
|
-
* Called before deleting a task.
|
|
279
|
+
* Called before deleting a task. Throw an error to deny access.
|
|
279
280
|
*/
|
|
280
281
|
onBeforeDeleteTask?: (
|
|
281
282
|
taskId: string,
|
|
282
283
|
context: KanbanApiContext,
|
|
283
|
-
) => Promise<
|
|
284
|
+
) => Promise<void> | void;
|
|
284
285
|
|
|
285
286
|
/**
|
|
286
287
|
* Called after a task is created successfully
|
|
@@ -346,12 +347,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
|
|
|
346
347
|
|
|
347
348
|
try {
|
|
348
349
|
if (hooks?.onBeforeListBoards) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
350
|
+
await runHookWithShim(
|
|
351
|
+
() => hooks.onBeforeListBoards!(query, context),
|
|
352
|
+
ctx.error,
|
|
353
|
+
"Unauthorized: Cannot list boards",
|
|
354
|
+
);
|
|
355
355
|
}
|
|
356
356
|
|
|
357
357
|
const result = await getAllBoards(adapter, query);
|
|
@@ -381,12 +381,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
|
|
|
381
381
|
|
|
382
382
|
try {
|
|
383
383
|
if (hooks?.onBeforeReadBoard) {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
384
|
+
await runHookWithShim(
|
|
385
|
+
() => hooks.onBeforeReadBoard!(params.id, context),
|
|
386
|
+
ctx.error,
|
|
387
|
+
"Unauthorized: Cannot read board",
|
|
388
|
+
);
|
|
390
389
|
}
|
|
391
390
|
|
|
392
391
|
const result = await getBoardById(adapter, params.id);
|
|
@@ -423,15 +422,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
|
|
|
423
422
|
|
|
424
423
|
try {
|
|
425
424
|
if (hooks?.onBeforeCreateBoard) {
|
|
426
|
-
|
|
427
|
-
ctx.body,
|
|
428
|
-
|
|
425
|
+
await runHookWithShim(
|
|
426
|
+
() => hooks.onBeforeCreateBoard!(ctx.body, context),
|
|
427
|
+
ctx.error,
|
|
428
|
+
"Unauthorized: Cannot create board",
|
|
429
429
|
);
|
|
430
|
-
if (!canCreate) {
|
|
431
|
-
throw ctx.error(403, {
|
|
432
|
-
message: "Unauthorized: Cannot create board",
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
430
|
}
|
|
436
431
|
|
|
437
432
|
const { ...boardData } = ctx.body;
|
|
@@ -516,16 +511,16 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
|
|
|
516
511
|
|
|
517
512
|
try {
|
|
518
513
|
if (hooks?.onBeforeUpdateBoard) {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
514
|
+
await runHookWithShim(
|
|
515
|
+
() =>
|
|
516
|
+
hooks.onBeforeUpdateBoard!(
|
|
517
|
+
ctx.params.id,
|
|
518
|
+
{ ...ctx.body, id: ctx.params.id },
|
|
519
|
+
context,
|
|
520
|
+
),
|
|
521
|
+
ctx.error,
|
|
522
|
+
"Unauthorized: Cannot update board",
|
|
523
523
|
);
|
|
524
|
-
if (!canUpdate) {
|
|
525
|
-
throw ctx.error(403, {
|
|
526
|
-
message: "Unauthorized: Cannot update board",
|
|
527
|
-
});
|
|
528
|
-
}
|
|
529
524
|
}
|
|
530
525
|
|
|
531
526
|
const { slug: rawSlug, ...restBoardData } = ctx.body;
|
|
@@ -595,15 +590,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
|
|
|
595
590
|
}
|
|
596
591
|
|
|
597
592
|
if (hooks?.onBeforeDeleteBoard) {
|
|
598
|
-
|
|
599
|
-
ctx.params.id,
|
|
600
|
-
|
|
593
|
+
await runHookWithShim(
|
|
594
|
+
() => hooks.onBeforeDeleteBoard!(ctx.params.id, context),
|
|
595
|
+
ctx.error,
|
|
596
|
+
"Unauthorized: Cannot delete board",
|
|
601
597
|
);
|
|
602
|
-
if (!canDelete) {
|
|
603
|
-
throw ctx.error(403, {
|
|
604
|
-
message: "Unauthorized: Cannot delete board",
|
|
605
|
-
});
|
|
606
|
-
}
|
|
607
598
|
}
|
|
608
599
|
|
|
609
600
|
await adapter.delete<Board>({
|
|
@@ -641,15 +632,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
|
|
|
641
632
|
|
|
642
633
|
try {
|
|
643
634
|
if (hooks?.onBeforeCreateColumn) {
|
|
644
|
-
|
|
645
|
-
ctx.body,
|
|
646
|
-
|
|
635
|
+
await runHookWithShim(
|
|
636
|
+
() => hooks.onBeforeCreateColumn!(ctx.body, context),
|
|
637
|
+
ctx.error,
|
|
638
|
+
"Unauthorized: Cannot create column",
|
|
647
639
|
);
|
|
648
|
-
if (!canCreate) {
|
|
649
|
-
throw ctx.error(403, {
|
|
650
|
-
message: "Unauthorized: Cannot create column",
|
|
651
|
-
});
|
|
652
|
-
}
|
|
653
640
|
}
|
|
654
641
|
|
|
655
642
|
// Get existing columns to determine order
|
|
@@ -704,16 +691,16 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
|
|
|
704
691
|
|
|
705
692
|
try {
|
|
706
693
|
if (hooks?.onBeforeUpdateColumn) {
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
694
|
+
await runHookWithShim(
|
|
695
|
+
() =>
|
|
696
|
+
hooks.onBeforeUpdateColumn!(
|
|
697
|
+
ctx.params.id,
|
|
698
|
+
{ ...ctx.body, id: ctx.params.id },
|
|
699
|
+
context,
|
|
700
|
+
),
|
|
701
|
+
ctx.error,
|
|
702
|
+
"Unauthorized: Cannot update column",
|
|
711
703
|
);
|
|
712
|
-
if (!canUpdate) {
|
|
713
|
-
throw ctx.error(403, {
|
|
714
|
-
message: "Unauthorized: Cannot update column",
|
|
715
|
-
});
|
|
716
|
-
}
|
|
717
704
|
}
|
|
718
705
|
|
|
719
706
|
const updated = await adapter.update<Column>({
|
|
@@ -765,15 +752,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
|
|
|
765
752
|
}
|
|
766
753
|
|
|
767
754
|
if (hooks?.onBeforeDeleteColumn) {
|
|
768
|
-
|
|
769
|
-
ctx.params.id,
|
|
770
|
-
|
|
755
|
+
await runHookWithShim(
|
|
756
|
+
() => hooks.onBeforeDeleteColumn!(ctx.params.id, context),
|
|
757
|
+
ctx.error,
|
|
758
|
+
"Unauthorized: Cannot delete column",
|
|
771
759
|
);
|
|
772
|
-
if (!canDelete) {
|
|
773
|
-
throw ctx.error(403, {
|
|
774
|
-
message: "Unauthorized: Cannot delete column",
|
|
775
|
-
});
|
|
776
|
-
}
|
|
777
760
|
}
|
|
778
761
|
|
|
779
762
|
await adapter.delete<Column>({
|
|
@@ -810,16 +793,16 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
|
|
|
810
793
|
for (let i = 0; i < columnIds.length; i++) {
|
|
811
794
|
const columnId = columnIds[i];
|
|
812
795
|
if (!columnId) continue;
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
796
|
+
await runHookWithShim(
|
|
797
|
+
() =>
|
|
798
|
+
hooks.onBeforeUpdateColumn!(
|
|
799
|
+
columnId,
|
|
800
|
+
{ id: columnId, order: i },
|
|
801
|
+
context,
|
|
802
|
+
),
|
|
803
|
+
ctx.error,
|
|
804
|
+
"Unauthorized: Cannot reorder columns",
|
|
817
805
|
);
|
|
818
|
-
if (!canUpdate) {
|
|
819
|
-
throw ctx.error(403, {
|
|
820
|
-
message: "Unauthorized: Cannot reorder columns",
|
|
821
|
-
});
|
|
822
|
-
}
|
|
823
806
|
}
|
|
824
807
|
}
|
|
825
808
|
|
|
@@ -869,15 +852,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
|
|
|
869
852
|
|
|
870
853
|
try {
|
|
871
854
|
if (hooks?.onBeforeCreateTask) {
|
|
872
|
-
|
|
873
|
-
ctx.body,
|
|
874
|
-
|
|
855
|
+
await runHookWithShim(
|
|
856
|
+
() => hooks.onBeforeCreateTask!(ctx.body, context),
|
|
857
|
+
ctx.error,
|
|
858
|
+
"Unauthorized: Cannot create task",
|
|
875
859
|
);
|
|
876
|
-
if (!canCreate) {
|
|
877
|
-
throw ctx.error(403, {
|
|
878
|
-
message: "Unauthorized: Cannot create task",
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
860
|
}
|
|
882
861
|
|
|
883
862
|
// Get existing tasks in column to determine order
|
|
@@ -939,16 +918,16 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
|
|
|
939
918
|
|
|
940
919
|
try {
|
|
941
920
|
if (hooks?.onBeforeUpdateTask) {
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
921
|
+
await runHookWithShim(
|
|
922
|
+
() =>
|
|
923
|
+
hooks.onBeforeUpdateTask!(
|
|
924
|
+
ctx.params.id,
|
|
925
|
+
{ ...ctx.body, id: ctx.params.id },
|
|
926
|
+
context,
|
|
927
|
+
),
|
|
928
|
+
ctx.error,
|
|
929
|
+
"Unauthorized: Cannot update task",
|
|
946
930
|
);
|
|
947
|
-
if (!canUpdate) {
|
|
948
|
-
throw ctx.error(403, {
|
|
949
|
-
message: "Unauthorized: Cannot update task",
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
931
|
}
|
|
953
932
|
|
|
954
933
|
const updated = await adapter.update<Task>({
|
|
@@ -1000,15 +979,11 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
|
|
|
1000
979
|
}
|
|
1001
980
|
|
|
1002
981
|
if (hooks?.onBeforeDeleteTask) {
|
|
1003
|
-
|
|
1004
|
-
ctx.params.id,
|
|
1005
|
-
|
|
982
|
+
await runHookWithShim(
|
|
983
|
+
() => hooks.onBeforeDeleteTask!(ctx.params.id, context),
|
|
984
|
+
ctx.error,
|
|
985
|
+
"Unauthorized: Cannot delete task",
|
|
1006
986
|
);
|
|
1007
|
-
if (!canDelete) {
|
|
1008
|
-
throw ctx.error(403, {
|
|
1009
|
-
message: "Unauthorized: Cannot delete task",
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
987
|
}
|
|
1013
988
|
|
|
1014
989
|
await adapter.delete<Task>({
|
|
@@ -1052,16 +1027,16 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
|
|
|
1052
1027
|
|
|
1053
1028
|
// Check authorization before moving task
|
|
1054
1029
|
if (hooks?.onBeforeUpdateTask) {
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1030
|
+
await runHookWithShim(
|
|
1031
|
+
() =>
|
|
1032
|
+
hooks.onBeforeUpdateTask!(
|
|
1033
|
+
taskId,
|
|
1034
|
+
{ id: taskId, columnId: targetColumnId, order: targetOrder },
|
|
1035
|
+
context,
|
|
1036
|
+
),
|
|
1037
|
+
ctx.error,
|
|
1038
|
+
"Unauthorized: Cannot move task",
|
|
1059
1039
|
);
|
|
1060
|
-
if (!canUpdate) {
|
|
1061
|
-
throw ctx.error(403, {
|
|
1062
|
-
message: "Unauthorized: Cannot move task",
|
|
1063
|
-
});
|
|
1064
|
-
}
|
|
1065
1040
|
}
|
|
1066
1041
|
|
|
1067
1042
|
// Update task with new column and order
|
|
@@ -1105,16 +1080,16 @@ export const kanbanBackendPlugin = (hooks?: KanbanBackendHooks) =>
|
|
|
1105
1080
|
for (let i = 0; i < taskIds.length; i++) {
|
|
1106
1081
|
const taskId = taskIds[i];
|
|
1107
1082
|
if (!taskId) continue;
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1083
|
+
await runHookWithShim(
|
|
1084
|
+
() =>
|
|
1085
|
+
hooks.onBeforeUpdateTask!(
|
|
1086
|
+
taskId,
|
|
1087
|
+
{ id: taskId, order: i },
|
|
1088
|
+
context,
|
|
1089
|
+
),
|
|
1090
|
+
ctx.error,
|
|
1091
|
+
"Unauthorized: Cannot reorder tasks",
|
|
1112
1092
|
);
|
|
1113
|
-
if (!canUpdate) {
|
|
1114
|
-
throw ctx.error(403, {
|
|
1115
|
-
message: "Unauthorized: Cannot reorder tasks",
|
|
1116
|
-
});
|
|
1117
|
-
}
|
|
1118
1093
|
}
|
|
1119
1094
|
}
|
|
1120
1095
|
|