@maudecode/kanvil 0.7.0 → 0.8.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/apps/server/dist/{auth-D43XyHdK.mjs → auth-CVnX2XIg.mjs} +3759 -3550
- package/apps/server/dist/client.mjs +1 -1
- package/apps/server/dist/{config-CQVdVhXv.mjs → config-DUIdB3c2.mjs} +1 -1
- package/apps/server/dist/index.mjs +12 -7
- package/apps/server/dist/migrate.mjs +1 -1
- package/apps/server/dist/recover.mjs +3 -3
- package/apps/server/dist/{src-D70GmZ5R.mjs → src-CC6lPIU_.mjs} +107 -9
- package/apps/server/drizzle/0042_public_boards.sql +37 -0
- package/apps/server/drizzle/0043_epics.sql +18 -0
- package/apps/server/drizzle/meta/0042_snapshot.json +7315 -0
- package/apps/server/drizzle/meta/0043_snapshot.json +7466 -0
- package/apps/server/drizzle/meta/_journal.json +14 -0
- package/apps/web/dist/assets/{CompositeItem-DLTRsada.js → CompositeItem-GgAhkI4m.js} +1 -1
- package/apps/web/dist/assets/{Trans-DqX-qhvQ.js → Trans-CT9xiBED.js} +1 -1
- package/apps/web/dist/assets/admin-settings-C7yxWm-w.js +1 -0
- package/apps/web/dist/assets/{archive-restore-BO1qng5Y.js → archive-restore-UgUTJyLs.js} +1 -1
- package/apps/web/dist/assets/az-AZ-IkRP1MKo.js +1 -0
- package/apps/web/dist/assets/{az-CEW7-a7J.js → az-CZ46yOUl.js} +1 -1
- package/apps/web/dist/assets/{configuration-D0zvwrXt.js → configuration-CU0QOaRw.js} +1 -1
- package/apps/web/dist/assets/{corner-left-up-Be7Z9Uhf.js → corner-left-up-BFY7AqdS.js} +1 -1
- package/apps/web/dist/assets/{de-B-oGVMYL.js → de-CseN-3gF.js} +1 -1
- package/apps/web/dist/assets/de-DE-DCGKtpLI.js +1 -0
- package/apps/web/dist/assets/default-CN9zcRIy.js +56 -0
- package/apps/web/dist/assets/{delivery-settings-BrwSrFIN.js → delivery-settings-xx9Bb6Ux.js} +1 -1
- package/apps/web/dist/assets/el-GR-C5w6p2-z.js +1 -0
- package/apps/web/dist/assets/{el-Cp69kezM.js → el-psH0HOR3.js} +1 -1
- package/apps/web/dist/assets/{ellipsis-B0AE8ax3.js → ellipsis-CcIJddV1.js} +1 -1
- package/apps/web/dist/assets/{es-CwOMVSTx.js → es-BVvGrD23.js} +1 -1
- package/apps/web/dist/assets/es-ES-PjJlJCgT.js +1 -0
- package/apps/web/dist/assets/{forms-CkVpTx29.js → forms-BUNl5uNV.js} +1 -1
- package/apps/web/dist/assets/{fr-Cy4co9ic.js → fr-C43nghuI.js} +1 -1
- package/apps/web/dist/assets/fr-FR-CGWH-Juq.js +1 -0
- package/apps/web/dist/assets/{hi-DbO9TKwg.js → hi-Ds3DGtT-.js} +1 -1
- package/apps/web/dist/assets/hi-IN-B3bq8hDJ.js +1 -0
- package/apps/web/dist/assets/{highlight-zMg1BK-r.js → highlight-BPZqoBaO.js} +1 -1
- package/apps/web/dist/assets/id-ID-98H240ET.js +1 -0
- package/apps/web/dist/assets/{id-B6NKsmCm.js → id-tg9awgd8.js} +1 -1
- package/apps/web/dist/assets/index-CvFGdLre.js +17 -0
- package/apps/web/dist/assets/{invitations-Dp40BfHj.js → invitations-fH1M9hYm.js} +1 -1
- package/apps/web/dist/assets/{it-ilGYCq2T.js → it-DRwaWQyE.js} +1 -1
- package/apps/web/dist/assets/it-IT-CNS9cL7I.js +1 -0
- package/apps/web/dist/assets/{item-DQR89bKm.js → item-BbaYy50i.js} +1 -1
- package/apps/web/dist/assets/ja-JP-BFTqtCdV.js +1 -0
- package/apps/web/dist/assets/{ja-D-hJRTgP.js → ja-jj4Ubayf.js} +1 -1
- package/apps/web/dist/assets/{ko-ajkkoJ_d.js → ko-Dn2pRrar.js} +1 -1
- package/apps/web/dist/assets/ko-KR-C58VNccr.js +1 -0
- package/apps/web/dist/assets/{label-settings-ChnKkA97.js → label-settings-CbBHSL0c.js} +1 -1
- package/apps/web/dist/assets/{markdown-blocks-CpzrwyQG.js → markdown-blocks-B6mE_S6n.js} +1 -1
- package/apps/web/dist/assets/{member-directory-XK0ZEGUq.js → member-directory-CoGg7WkW.js} +1 -1
- package/apps/web/dist/assets/{mk-C_sMyEHX.js → mk-DQUnXaPE.js} +1 -1
- package/apps/web/dist/assets/mk-MK-Bf5RDo6j.js +1 -0
- package/apps/web/dist/assets/{nl-D9-P4UPR.js → nl-C_Ijir3O.js} +1 -1
- package/apps/web/dist/assets/nl-NL-D1WFJyOY.js +1 -0
- package/apps/web/dist/assets/{pl-CiQM7fSB.js → pl-DgWXgYL-.js} +1 -1
- package/apps/web/dist/assets/pl-PL--jAcwyAK.js +1 -0
- package/apps/web/dist/assets/project-lifecycle-BHWCMq7o.js +1 -0
- package/apps/web/dist/assets/project-settings-CBG5CZX1.js +1 -0
- package/apps/web/dist/assets/pt-BR-CJQfuq9b.js +1 -0
- package/apps/web/dist/assets/{pt-BR-CP7dhuqF.js → pt-BR-N-Xvp9fC.js} +1 -1
- package/apps/web/dist/assets/public-project-DmTr9q4c.js +1 -0
- package/apps/web/dist/assets/{rotate-ccw-Cxh9ec2s.js → rotate-ccw-BFYpBtuj.js} +1 -1
- package/apps/web/dist/assets/{ru-DxJ6vF4E.js → ru-Ctq-5FYl.js} +1 -1
- package/apps/web/dist/assets/ru-RU-BtvJ8Glt.js +1 -0
- package/apps/web/dist/assets/{search-DfbsqO7M.js → search-C2QaoLwI.js} +3 -3
- package/apps/web/dist/assets/{send-DrJl0BKm.js → send-qRPqbT2m.js} +1 -1
- package/apps/web/dist/assets/{settings-BN8uIiBG.js → settings-BNPajW83.js} +1 -1
- package/apps/web/dist/assets/{sliders-horizontal-DM47MLe7.js → sliders-horizontal-S16oyCiS.js} +1 -1
- package/apps/web/dist/assets/{switch-CJTVGtE9.js → switch-C44Ouoqh.js} +1 -1
- package/apps/web/dist/assets/{table-BLLjFPWD.js → table-esnQpDtb.js} +1 -1
- package/apps/web/dist/assets/ticket-detail-BVyScTlg.js +2 -0
- package/apps/web/dist/assets/ticket-editor-BjuSeeiA.js +2 -0
- package/apps/web/dist/assets/ticket-list-BCvUvYju.js +6 -0
- package/apps/web/dist/assets/ticket-list-controls-BiC5CIwM.js +1 -0
- package/apps/web/dist/assets/{tr-9IkdRLIk.js → tr-CYT8BT69.js} +1 -1
- package/apps/web/dist/assets/tr-TR-Bahy-EKO.js +1 -0
- package/apps/web/dist/assets/uk-UA-DDVgxWqX.js +1 -0
- package/apps/web/dist/assets/{uk-Btampsdw.js → uk-y7QuyfrN.js} +1 -1
- package/apps/web/dist/assets/{upload-HARy4VC_.js → upload-e7CmLHhp.js} +1 -1
- package/apps/web/dist/assets/{useQueries-CTeEF--U.js → useQueries-U3MOyy3t.js} +1 -1
- package/apps/web/dist/assets/{vi-BVZzOn_n.js → vi-Bz2g5X1k.js} +1 -1
- package/apps/web/dist/assets/vi-VN-CuHCYxNQ.js +1 -0
- package/apps/web/dist/assets/{visual-editor-DKBPjqV-.js → visual-editor-UmVaDXWu.js} +1 -1
- package/apps/web/dist/assets/workspace-settings-BiWJqW0m.js +1 -0
- package/apps/web/dist/assets/zh-CN-C94sYOI_.js +1 -0
- package/apps/web/dist/assets/{zh-CN-BoUvF2do.js → zh-CN-y_sluDUm.js} +1 -1
- package/apps/web/dist/assets/zh-TW-BoRQfGQL.js +1 -0
- package/apps/web/dist/assets/{zh-TW-CEIDGTWe.js → zh-TW-DIEfy239.js} +1 -1
- package/apps/web/dist/index.html +7 -7
- package/docs/api.md +31 -3
- package/docs/mobile.md +1 -1
- package/docs/plan.md +1 -0
- package/docs/releasing.md +1 -1
- package/package.json +1 -1
- package/apps/web/dist/assets/admin-settings-BQzdZII5.js +0 -1
- package/apps/web/dist/assets/az-AZ-SXqDBPA2.js +0 -1
- package/apps/web/dist/assets/de-DE-CB7aQ6b-.js +0 -1
- package/apps/web/dist/assets/default-CCI1vZxa.js +0 -56
- package/apps/web/dist/assets/el-GR-D1ntYXIt.js +0 -1
- package/apps/web/dist/assets/es-ES-CllsaxcB.js +0 -1
- package/apps/web/dist/assets/fr-FR-DxTw6LgI.js +0 -1
- package/apps/web/dist/assets/hi-IN-CaQga-0l.js +0 -1
- package/apps/web/dist/assets/id-ID-DBfwAAqU.js +0 -1
- package/apps/web/dist/assets/index-CZuFd4LM.js +0 -17
- package/apps/web/dist/assets/it-IT-BckuH9Nl.js +0 -1
- package/apps/web/dist/assets/ja-JP-BAGgJKZ4.js +0 -1
- package/apps/web/dist/assets/ko-KR-srIEsTP5.js +0 -1
- package/apps/web/dist/assets/mk-MK-BTa1NMAF.js +0 -1
- package/apps/web/dist/assets/nl-NL-CQduhDqs.js +0 -1
- package/apps/web/dist/assets/pl-PL-BdZMf7Ni.js +0 -1
- package/apps/web/dist/assets/project-lifecycle-VYecJuiP.js +0 -1
- package/apps/web/dist/assets/project-settings-Bs6YEpo9.js +0 -1
- package/apps/web/dist/assets/pt-BR-DZEXJNHg.js +0 -1
- package/apps/web/dist/assets/public-project-DUfWDao9.js +0 -1
- package/apps/web/dist/assets/ru-RU-CZKEe7P8.js +0 -1
- package/apps/web/dist/assets/ticket-detail-BTy_3JeU.js +0 -2
- package/apps/web/dist/assets/ticket-editor-Bb_lyxnJ.js +0 -2
- package/apps/web/dist/assets/ticket-list-Botp5qf5.js +0 -6
- package/apps/web/dist/assets/ticket-list-controls-Dmkno4pm.js +0 -1
- package/apps/web/dist/assets/tr-TR-Y8wBLO8I.js +0 -1
- package/apps/web/dist/assets/uk-UA-CVpasGUS.js +0 -1
- package/apps/web/dist/assets/vi-VN-BMy-iJD-.js +0 -1
- package/apps/web/dist/assets/workspace-settings-DqzBNw60.js +0 -1
- package/apps/web/dist/assets/zh-CN-Cds_v0R8.js +0 -1
- package/apps/web/dist/assets/zh-TW-DCCYakXz.js +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Dt as streamErrorSchema, G as notificationIdsInput, J as notificationReadInput, K as notificationPreferencesInput, O as eventSchema, Q as operations, St as snapshotSchema, T as errorSchema, X as okSchema, Y as notificationsSchema, ct as publicOperations, et as parseOutput, o as checkpointSchema, q as notificationPreferencesSchema, t as ApiError, tt as parsePublicOutput } from "./src-
|
|
1
|
+
import { Dt as streamErrorSchema, G as notificationIdsInput, J as notificationReadInput, K as notificationPreferencesInput, O as eventSchema, Q as operations, St as snapshotSchema, T as errorSchema, X as okSchema, Y as notificationsSchema, ct as publicOperations, et as parseOutput, o as checkpointSchema, q as notificationPreferencesSchema, t as ApiError, tt as parsePublicOutput } from "./src-CC6lPIU_.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
import { constants } from "node:fs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Gt as searchSettingsSchema, Ut as s3SettingsSchema, g as defaultClaimMaxLeaseSeconds, s as claimLeaseSecondsSchema, zt as brokerSettingsSchema } from "./src-
|
|
1
|
+
import { Gt as searchSettingsSchema, Ut as s3SettingsSchema, g as defaultClaimMaxLeaseSeconds, s as claimLeaseSecondsSchema, zt as brokerSettingsSchema } from "./src-CC6lPIU_.mjs";
|
|
2
2
|
import pg from "pg";
|
|
3
3
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
4
4
|
import { relations } from "drizzle-orm";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as auth_schema_exports, d as user$1, i as apikey, n as createDatabase, r as account, t as readConfig, u as session } from "./config-
|
|
2
|
-
import { B as isPublicOperation, Bt as instanceUpdateSchema, G as notificationIdsInput, Gt as searchSettingsSchema, H as memberSchema, Ht as photoResultSchema, J as notificationReadInput, K as notificationPreferencesInput, Lt as adminInstanceSchema, M as hasAdminRole, N as importLimits, Ot as ticketExportFormat, P as importProviderDefinitions, Q as operations, R as isOperation, T as errorSchema, Tt as stateLabels, Ut as s3SettingsSchema, Vt as photoInputSchema, X as okSchema, _t as roles, ct as publicOperations, l as closedStates, lt as pushDeviceInput, q as notificationPreferencesSchema, t as ApiError, u as credentialInput, v as defaultWorkflow, vt as scopes, z as isPresetColor, zt as brokerSettingsSchema } from "./src-
|
|
1
|
+
import { a as auth_schema_exports, d as user$1, i as apikey, n as createDatabase, r as account, t as readConfig, u as session } from "./config-DUIdB3c2.mjs";
|
|
2
|
+
import { B as isPublicOperation, Bt as instanceUpdateSchema, G as notificationIdsInput, Gt as searchSettingsSchema, H as memberSchema, Ht as photoResultSchema, J as notificationReadInput, K as notificationPreferencesInput, Lt as adminInstanceSchema, M as hasAdminRole, N as importLimits, Ot as ticketExportFormat, P as importProviderDefinitions, Q as operations, R as isOperation, T as errorSchema, Tt as stateLabels, Ut as s3SettingsSchema, Vt as photoInputSchema, X as okSchema, _t as roles, ct as publicOperations, l as closedStates, lt as pushDeviceInput, q as notificationPreferencesSchema, t as ApiError, u as credentialInput, v as defaultWorkflow, vt as scopes, z as isPresetColor, zt as brokerSettingsSchema } from "./src-CC6lPIU_.mjs";
|
|
3
3
|
import { t as migrateDatabase } from "./migrate-C9f4ozB4.mjs";
|
|
4
|
-
import { $ as pushDevices, A as
|
|
4
|
+
import { $ as pushDevices, A as createWriteLimiter, B as createPollingJob, C as receiveRepositoryWebhook, D as createHttpSender, E as DeliveryError, F as createBlobStorage, G as eventClock, H as attachments, I as decodeImage, J as members, K as events, L as imageResponse, M as requireSession, N as createTicketSearch, O as receiveUpload, P as createTicketIndex, Q as projects, R as stagedUploadCutoff, S as readCalendarFeed, T as deliverBatch, U as blobObjects, V as attachmentContent, W as deliveryCheckpoints, X as notifications, Y as notificationPreferences, Z as profilePhotos, _ as one, a as deliverySecrets, at as workspaces, b as requireRow, c as ownerBlocker, d as createDomain, et as stagedUploads, f as errorEnvelope, g as notify, h as json, i as createInstance, it as workspaceMembers, j as requireInstanceAdmin, k as createAccess, l as appendEvents, m as intakeOnly, n as emailCodesAvailable, nt as subscriptions, o as otherActiveAdministrator, ot as matchesOIDCBinding, p as fail, r as signInProviders, rt as tickets, s as ownedWorkspaces, t as createAuth, tt as storageBackends, u as assertVersion, v as reachable, w as defaultSenders, x as processImports, y as recipientColumns, z as createObjectStore } from "./auth-CVnX2XIg.mjs";
|
|
5
5
|
import { serve } from "@hono/node-server";
|
|
6
6
|
import pg from "pg";
|
|
7
7
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
@@ -9,8 +9,8 @@ import { and, asc, count, desc, eq, gt, ilike, inArray, isNotNull, isNull, notIn
|
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
12
|
-
import { isDeepStrictEqual } from "node:util";
|
|
13
12
|
import { Expo } from "expo-server-sdk";
|
|
13
|
+
import { isDeepStrictEqual } from "node:util";
|
|
14
14
|
import { EventEmitter } from "node:events";
|
|
15
15
|
import { connect } from "amqplib";
|
|
16
16
|
import { OpenAPIHono, createRoute, z as z$1 } from "@hono/zod-openapi";
|
|
@@ -359,7 +359,7 @@ function createCleanup() {
|
|
|
359
359
|
}
|
|
360
360
|
//#endregion
|
|
361
361
|
//#region src/version.ts
|
|
362
|
-
const applicationVersion = "0.
|
|
362
|
+
const applicationVersion = "0.8.0";
|
|
363
363
|
//#endregion
|
|
364
364
|
//#region src/realtime.ts
|
|
365
365
|
async function createRealtime(config) {
|
|
@@ -3145,6 +3145,11 @@ const known = (name) => {
|
|
|
3145
3145
|
if (!isOperation(name)) throw new ApiError(404, "not_found", `No operation is named ${name}`);
|
|
3146
3146
|
return name;
|
|
3147
3147
|
};
|
|
3148
|
+
const refuseUnknownKeys = (name, input) => {
|
|
3149
|
+
const { shape } = toolInput(name);
|
|
3150
|
+
const unknown = Object.keys(input).filter((key) => !Object.hasOwn(shape, key));
|
|
3151
|
+
if (unknown.length > 0) throw new ApiError(400, "invalid_request", `${name} does not take ${unknown.join(", ")}. Valid keys: ${Object.keys(shape).join(", ") || "none"}.`);
|
|
3152
|
+
};
|
|
3148
3153
|
const summary = (name) => ({
|
|
3149
3154
|
name,
|
|
3150
3155
|
description: operations[name].description,
|
|
@@ -3189,6 +3194,7 @@ const runnerInput = {
|
|
|
3189
3194
|
function createMcp(auth, domain, access, config, limitWrite) {
|
|
3190
3195
|
const serve = (request, identity, limited = false) => {
|
|
3191
3196
|
const execute = async (name, input) => {
|
|
3197
|
+
refuseUnknownKeys(name, input);
|
|
3192
3198
|
if (limited && operations[name].method !== "GET") limitWrite(identity.ownerId);
|
|
3193
3199
|
const value = "view" in operations[name].input.shape ? {
|
|
3194
3200
|
view: defaultView,
|
|
@@ -3208,10 +3214,9 @@ function createMcp(auth, domain, access, config, limitWrite) {
|
|
|
3208
3214
|
}, { instructions });
|
|
3209
3215
|
for (const name of coreNames) {
|
|
3210
3216
|
const spec = operations[name];
|
|
3211
|
-
const inputSchema = toolInput(name).shape;
|
|
3212
3217
|
server.registerTool(name, {
|
|
3213
3218
|
description: spec.description,
|
|
3214
|
-
inputSchema,
|
|
3219
|
+
inputSchema: toolInput(name).loose(),
|
|
3215
3220
|
annotations: {
|
|
3216
3221
|
readOnlyHint: spec.method === "GET",
|
|
3217
3222
|
destructiveHint: false,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as createDatabase, t as readConfig } from "./config-
|
|
1
|
+
import { n as createDatabase, t as readConfig } from "./config-DUIdB3c2.mjs";
|
|
2
2
|
import { t as migrateDatabase } from "./migrate-C9f4ozB4.mjs";
|
|
3
3
|
//#region src/cli/migrate.ts
|
|
4
4
|
const database = createDatabase(readConfig());
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { d as user, n as createDatabase, r as account, t as readConfig, u as session } from "./config-
|
|
2
|
-
import { M as hasAdminRole } from "./src-
|
|
3
|
-
import { p as fail, q as instanceSettings, t as createAuth } from "./auth-
|
|
1
|
+
import { d as user, n as createDatabase, r as account, t as readConfig, u as session } from "./config-DUIdB3c2.mjs";
|
|
2
|
+
import { M as hasAdminRole } from "./src-CC6lPIU_.mjs";
|
|
3
|
+
import { p as fail, q as instanceSettings, t as createAuth } from "./auth-CVnX2XIg.mjs";
|
|
4
4
|
import { and, eq, isNull, sql } from "drizzle-orm";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { parseArgs } from "node:util";
|
|
@@ -441,6 +441,10 @@ var en_US_default = {
|
|
|
441
441
|
updated: "Time entry updated",
|
|
442
442
|
deleted: "Time entry deleted"
|
|
443
443
|
},
|
|
444
|
+
epic: {
|
|
445
|
+
created: "Epic created",
|
|
446
|
+
updated: "Epic updated"
|
|
447
|
+
},
|
|
444
448
|
label: {
|
|
445
449
|
created: "Label created",
|
|
446
450
|
updated: "Label updated",
|
|
@@ -494,6 +498,7 @@ var en_US_default = {
|
|
|
494
498
|
created: "Calendar feed created",
|
|
495
499
|
revoked: "Calendar feed revoked"
|
|
496
500
|
},
|
|
501
|
+
public_board: { updated: "Public board updated" },
|
|
497
502
|
project: {
|
|
498
503
|
created: "Project created",
|
|
499
504
|
updated: "Project updated",
|
|
@@ -1079,6 +1084,8 @@ var en_US_default = {
|
|
|
1079
1084
|
kind: "Kind",
|
|
1080
1085
|
difficulty: "Difficulty",
|
|
1081
1086
|
assignee: "Assignee",
|
|
1087
|
+
epic: "Epic",
|
|
1088
|
+
noEpic: "No epic",
|
|
1082
1089
|
you: "{{name}} (you)",
|
|
1083
1090
|
assignTo: "Assign to…",
|
|
1084
1091
|
set: "Set {{label, lowercase}}…",
|
|
@@ -1834,6 +1841,8 @@ var en_US_default = {
|
|
|
1834
1841
|
kept: "Kept",
|
|
1835
1842
|
projects: "Projects",
|
|
1836
1843
|
members: "Members",
|
|
1844
|
+
epics: "Epics",
|
|
1845
|
+
epicTickets: "Tickets leaving an epic",
|
|
1837
1846
|
sharedLabels: "Shared labels",
|
|
1838
1847
|
signIn: "Sign-in",
|
|
1839
1848
|
sessions: "Sessions",
|
|
@@ -3237,6 +3246,7 @@ const ticketFields = z.object({
|
|
|
3237
3246
|
scopes: tags,
|
|
3238
3247
|
assigneeId: z.string().nullable(),
|
|
3239
3248
|
parentId: z.uuid().nullable(),
|
|
3249
|
+
epicId: z.uuid().nullable(),
|
|
3240
3250
|
automationReady: z.boolean(),
|
|
3241
3251
|
holdReason: z.string().trim().max(2e3).nullable(),
|
|
3242
3252
|
startAt: dueDate.nullable(),
|
|
@@ -3345,7 +3355,8 @@ const projectImpactSchema = z.object({
|
|
|
3345
3355
|
copiedLabels: z.array(z.string()),
|
|
3346
3356
|
mergedLabels: z.array(z.string()),
|
|
3347
3357
|
subscriptions: count,
|
|
3348
|
-
invitations: count
|
|
3358
|
+
invitations: count,
|
|
3359
|
+
epicTickets: count.default(0)
|
|
3349
3360
|
}).nullable()
|
|
3350
3361
|
});
|
|
3351
3362
|
const workspaceImpactSchema = projectImpactSchema.omit({ transfer: true }).extend({
|
|
@@ -3356,7 +3367,8 @@ const workspaceImpactSchema = projectImpactSchema.omit({ transfer: true }).exten
|
|
|
3356
3367
|
})),
|
|
3357
3368
|
members: count,
|
|
3358
3369
|
invitations: count,
|
|
3359
|
-
labels: count
|
|
3370
|
+
labels: count,
|
|
3371
|
+
epics: count.default(0)
|
|
3360
3372
|
});
|
|
3361
3373
|
const deletedAccountName = "Deleted account";
|
|
3362
3374
|
const accountImpactSchema = z.object({
|
|
@@ -3378,6 +3390,7 @@ const accountImpactSchema = z.object({
|
|
|
3378
3390
|
});
|
|
3379
3391
|
const ticketSchema = z.object({
|
|
3380
3392
|
...ticketFields.shape,
|
|
3393
|
+
epicId: z.uuid().nullable().default(null),
|
|
3381
3394
|
startAt: dueDate.nullable().default(null),
|
|
3382
3395
|
stateId: z.uuid().nullable().default(null),
|
|
3383
3396
|
customFields: z.record(z.string(), customFieldValue).default({}),
|
|
@@ -3406,11 +3419,27 @@ const ticketSummarySchema = ticketSchema.pick({
|
|
|
3406
3419
|
scopes: true,
|
|
3407
3420
|
archived: true,
|
|
3408
3421
|
automationReady: true,
|
|
3422
|
+
epicId: true,
|
|
3409
3423
|
updatedAt: true,
|
|
3410
3424
|
version: true
|
|
3411
3425
|
});
|
|
3412
3426
|
const ticketView = { view: z.enum(["summary", "full"]).default("full") };
|
|
3413
3427
|
const ticketRow = z.union([ticketSchema, ticketSummarySchema]);
|
|
3428
|
+
const epicSchema = z.object({
|
|
3429
|
+
id: z.uuid(),
|
|
3430
|
+
workspaceId: z.uuid(),
|
|
3431
|
+
title: text.max(200),
|
|
3432
|
+
description: z.string(),
|
|
3433
|
+
archived: z.boolean(),
|
|
3434
|
+
version,
|
|
3435
|
+
createdBy: z.string(),
|
|
3436
|
+
createdAt: timestamp,
|
|
3437
|
+
updatedAt: timestamp
|
|
3438
|
+
});
|
|
3439
|
+
const epicRollupSchema = epicSchema.extend({
|
|
3440
|
+
openTickets: count,
|
|
3441
|
+
closedTickets: count
|
|
3442
|
+
});
|
|
3414
3443
|
const snapshotSchema = z.object({
|
|
3415
3444
|
project: projectSchema,
|
|
3416
3445
|
tickets: z.array(ticketSchema),
|
|
@@ -3585,6 +3614,25 @@ const calendarFeedSchema = z.object({
|
|
|
3585
3614
|
createdAt: timestamp,
|
|
3586
3615
|
active: z.boolean()
|
|
3587
3616
|
});
|
|
3617
|
+
const publicBoardSchema = z.object({
|
|
3618
|
+
projectId: z.uuid(),
|
|
3619
|
+
enabled: z.boolean(),
|
|
3620
|
+
title: text.max(80),
|
|
3621
|
+
labels: tags,
|
|
3622
|
+
excludedLabels: tags,
|
|
3623
|
+
version,
|
|
3624
|
+
createdAt: timestamp,
|
|
3625
|
+
updatedAt: timestamp
|
|
3626
|
+
});
|
|
3627
|
+
const publicBoardTicketSchema = z.object({
|
|
3628
|
+
projectId: z.uuid(),
|
|
3629
|
+
key: z.string(),
|
|
3630
|
+
title: z.string(),
|
|
3631
|
+
kind: z.enum(kinds),
|
|
3632
|
+
status: z.enum(states),
|
|
3633
|
+
createdAt: timestamp,
|
|
3634
|
+
updatedAt: timestamp
|
|
3635
|
+
});
|
|
3588
3636
|
const eventSchema = z.object({
|
|
3589
3637
|
id: z.string().regex(/^\d+$/),
|
|
3590
3638
|
projectId: z.uuid(),
|
|
@@ -3968,6 +4016,8 @@ const eventLabels = labelTable([
|
|
|
3968
4016
|
"label.created",
|
|
3969
4017
|
"label.updated",
|
|
3970
4018
|
"label.deleted",
|
|
4019
|
+
"epic.created",
|
|
4020
|
+
"epic.updated",
|
|
3971
4021
|
"workflow.created",
|
|
3972
4022
|
"workflow.updated",
|
|
3973
4023
|
"workflow.deleted",
|
|
@@ -4001,6 +4051,7 @@ const eventLabels = labelTable([
|
|
|
4001
4051
|
"resource.removed",
|
|
4002
4052
|
"calendar_feed.created",
|
|
4003
4053
|
"calendar_feed.revoked",
|
|
4054
|
+
"public_board.updated",
|
|
4004
4055
|
"project.created",
|
|
4005
4056
|
"project.updated",
|
|
4006
4057
|
"project.archived",
|
|
@@ -4340,6 +4391,37 @@ const operations = {
|
|
|
4340
4391
|
userId: z.string().min(1),
|
|
4341
4392
|
confirmEmail: z.string().trim().max(320)
|
|
4342
4393
|
}).strict(), okSchema, "DELETE", "/accounts/{userId}", "write", "Delete an account: remove its sign-in methods, sessions, API keys, OAuth grants, devices, notification settings, memberships, and photo, release its claims, cancel its imports, and replace its name and email with a `Deleted account` tombstone. Tickets, comments, evidence, and activity keep the account ID. Only the account itself or an instance administrator, signed in within the last 15 minutes, can delete; `confirmEmail` must repeat the account's email. Fails with `workspace_owner` while it owns a workspace and `last_administrator` for the last active instance or workspace administrator."),
|
|
4394
|
+
list_epics: op(z.object({
|
|
4395
|
+
workspaceId: z.uuid(),
|
|
4396
|
+
archived: ticketFilter.shape.archived,
|
|
4397
|
+
...pagination
|
|
4398
|
+
}), page(epicRollupSchema), "GET", "/workspaces/{workspaceId}/epics", "read", "List the workspace's Epics by title, each with open and closed ticket counts from the projects you can read, leaving out archived tickets. Archived Epics are left out unless `archived` is `include` or `only`."),
|
|
4399
|
+
get_epic: op(z.object({
|
|
4400
|
+
workspaceId: z.uuid(),
|
|
4401
|
+
epicId: z.uuid()
|
|
4402
|
+
}), epicRollupSchema, "GET", "/workspaces/{workspaceId}/epics/{epicId}", "read", "Read one Epic with the same ticket counts as list_epics."),
|
|
4403
|
+
create_epic: op(z.object({
|
|
4404
|
+
...mutation,
|
|
4405
|
+
workspaceId: z.uuid(),
|
|
4406
|
+
title: epicSchema.shape.title,
|
|
4407
|
+
description: z.string().max(1e5).default("")
|
|
4408
|
+
}).strict(), epicSchema, "POST", "/workspaces/{workspaceId}/epics", "write", "Create an Epic that groups tickets from any project in the workspace. Add a ticket by setting `epicId` through create_ticket, update_ticket, or bulk_update_tickets. Membership has no effect on readiness, eligibility, or claims. Reuse requestId when retrying this write."),
|
|
4409
|
+
update_epic: op(z.object({
|
|
4410
|
+
...mutation,
|
|
4411
|
+
workspaceId: z.uuid(),
|
|
4412
|
+
epicId: z.uuid(),
|
|
4413
|
+
expectedVersion: version,
|
|
4414
|
+
title: epicSchema.shape.title.optional(),
|
|
4415
|
+
description: z.string().max(1e5).optional(),
|
|
4416
|
+
archived: z.boolean().optional()
|
|
4417
|
+
}).strict().refine(({ title, description, archived }) => title !== void 0 || description !== void 0 || archived !== void 0, "Provide at least one change"), epicSchema, "PATCH", "/workspaces/{workspaceId}/epics/{epicId}", "write", "Change an Epic's title or description, archive it, or restore it with `archived: false`. An archived Epic keeps its members and refuses new ones. There is no deletion. Reuse requestId when retrying this write."),
|
|
4418
|
+
list_epic_tickets: op(z.object({
|
|
4419
|
+
workspaceId: z.uuid(),
|
|
4420
|
+
epicId: z.uuid(),
|
|
4421
|
+
archived: ticketFilter.shape.archived,
|
|
4422
|
+
...pagination,
|
|
4423
|
+
...ticketView
|
|
4424
|
+
}), page(ticketRow).extend({ restrictedCount: count }), "GET", "/workspaces/{workspaceId}/epics/{epicId}/tickets", "read", "List an Epic's tickets across projects, oldest first. Only tickets of projects you can read appear; `restrictedCount` counts the rest without naming them. With `view: \"summary\"`, rows omit the description and acceptance criteria."),
|
|
4343
4425
|
list_workspace_labels: op(z.object({ workspaceId: z.uuid() }), z.array(workspaceCatalogLabel), "GET", "/workspaces/{workspaceId}/labels", "read", "List the workspace's shared labels with ticket counts from the projects you can read, including archived tickets."),
|
|
4344
4426
|
create_workspace_label: op(z.object({
|
|
4345
4427
|
...mutation,
|
|
@@ -4362,7 +4444,7 @@ const operations = {
|
|
|
4362
4444
|
workspaceId: z.uuid(),
|
|
4363
4445
|
labelId: z.uuid(),
|
|
4364
4446
|
expectedVersion: version
|
|
4365
|
-
}).strict(), okSchema, "DELETE", "/workspaces/{workspaceId}/labels/{labelId}", "admin", "Delete a workspace label and remove it from tickets and saved-view filters in every project of the workspace atomically."),
|
|
4447
|
+
}).strict(), okSchema, "DELETE", "/workspaces/{workspaceId}/labels/{labelId}", "admin", "Delete a workspace label and remove it from tickets and saved-view filters in every project of the workspace atomically. Fails with `label_in_use` while an enabled public board in the workspace excludes it."),
|
|
4366
4448
|
list_invitations: op(z.object({ workspaceId: z.uuid() }), z.array(invitationSchema), "GET", "/workspaces/{workspaceId}/invitations", "admin", "List the workspace's pending and expired invitations."),
|
|
4367
4449
|
create_invitation: op(z.object({
|
|
4368
4450
|
workspaceId: z.uuid(),
|
|
@@ -4477,18 +4559,18 @@ const operations = {
|
|
|
4477
4559
|
expectedVersion: version,
|
|
4478
4560
|
archived: z.boolean()
|
|
4479
4561
|
}).strict(), projectSchema, "POST", "/projects/{projectId}/archive", "admin", "Archive a project, or restore it with `archived: false`. An archived project keeps its tickets and history but leaves default project lists, search, My work, calendar feeds, public links, reminders, and agent queues, and its tickets and settings are read only. Fails with `claim_conflict` while any ticket is claimed."),
|
|
4480
|
-
get_project_impact: op(projectInput.extend({ workspaceId: z.uuid().optional() }), projectImpactSchema, "GET", "/projects/{projectId}/impact", "admin", "Preview what deleting the project removes, or with `workspaceId`, what moving it to that workspace changes: who loses or gains access, labels copied or merged, follows and invitations dropped, and blockers such as claimed tickets."),
|
|
4562
|
+
get_project_impact: op(projectInput.extend({ workspaceId: z.uuid().optional() }), projectImpactSchema, "GET", "/projects/{projectId}/impact", "admin", "Preview what deleting the project removes, or with `workspaceId`, what moving it to that workspace changes: who loses or gains access, labels copied or merged, follows and invitations dropped, tickets leaving their Epic, and blockers such as claimed tickets."),
|
|
4481
4563
|
transfer_project: op(projectInput.extend({
|
|
4482
4564
|
...mutation,
|
|
4483
4565
|
expectedVersion: version,
|
|
4484
4566
|
workspaceId: z.uuid()
|
|
4485
|
-
}).strict(), projectSchema, "POST", "/projects/{projectId}/transfer", "admin", "Move a project with its tickets, keys, and links to another workspace. Requires administration of both workspaces and applies the reconciliation `get_project_impact` previews in one transaction
|
|
4567
|
+
}).strict(), projectSchema, "POST", "/projects/{projectId}/transfer", "admin", "Move a project with its tickets, keys, and links to another workspace. Requires administration of both workspaces and applies the reconciliation `get_project_impact` previews in one transaction, including clearing each ticket's `epicId`; blockers fail with their code."),
|
|
4486
4568
|
delete_project: op(projectInput.extend({
|
|
4487
4569
|
...mutation,
|
|
4488
4570
|
expectedVersion: version,
|
|
4489
4571
|
confirmKey: z.string().max(20)
|
|
4490
|
-
}).strict(), okSchema, "DELETE", "/projects/{projectId}", "admin", "Permanently delete a project's tickets, comments, attachments, labels, feeds, and destinations. `confirmKey` must repeat the project prefix. Fails with `claim_conflict` while any ticket is claimed. The prefix stays reserved and the activity history and deletion events are kept."),
|
|
4491
|
-
export_project: op(projectInput, ticketExportSchema, "GET", "/projects/{projectId}/export", "read", "Export the project's tickets, archived ones included, as a versioned `kanvil.tickets` bundle: workflow states, custom fields, labels, relationships, comments, evidence, and links, with the people they name. Records reference each other by source ID. `completeness.omitted` counts what the bundle leaves out, such as attachments and activity history."),
|
|
4572
|
+
}).strict(), okSchema, "DELETE", "/projects/{projectId}", "admin", "Permanently delete a project's tickets, comments, attachments, labels, feeds, public board, and destinations. `confirmKey` must repeat the project prefix. Fails with `claim_conflict` while any ticket is claimed. The prefix stays reserved and the activity history and deletion events are kept."),
|
|
4573
|
+
export_project: op(projectInput, ticketExportSchema, "GET", "/projects/{projectId}/export", "read", "Export the project's tickets, archived ones included, as a versioned `kanvil.tickets` bundle: workflow states, custom fields, labels, relationships, comments, evidence, and links, with the people they name. Records reference each other by source ID. `completeness.omitted` counts what the bundle leaves out, such as attachments, Epics, and activity history."),
|
|
4492
4574
|
preview_import: op(z.object({
|
|
4493
4575
|
...mutation,
|
|
4494
4576
|
workspaceId: z.uuid(),
|
|
@@ -4534,6 +4616,7 @@ const operations = {
|
|
|
4534
4616
|
...pagination,
|
|
4535
4617
|
relatedTo: id.optional(),
|
|
4536
4618
|
stateId: z.uuid().optional(),
|
|
4619
|
+
epicId: z.uuid().optional(),
|
|
4537
4620
|
scheduledFrom: timestamp.optional(),
|
|
4538
4621
|
scheduledTo: timestamp.optional(),
|
|
4539
4622
|
...ticketView
|
|
@@ -4559,7 +4642,7 @@ const operations = {
|
|
|
4559
4642
|
...mutation,
|
|
4560
4643
|
labelId: z.uuid(),
|
|
4561
4644
|
expectedVersion: version
|
|
4562
|
-
}).strict(), okSchema, "DELETE", "/projects/{projectId}/labels/{labelId}", "admin", "Delete a project label and remove it from tickets and saved-view filters atomically."),
|
|
4645
|
+
}).strict(), okSchema, "DELETE", "/projects/{projectId}/labels/{labelId}", "admin", "Delete a project label and remove it from tickets and saved-view filters atomically. Fails with `label_in_use` while the project's enabled public board excludes it."),
|
|
4563
4646
|
list_label_colors: op(z.object({}), z.array(z.object({
|
|
4564
4647
|
value: hexColor,
|
|
4565
4648
|
createdAt: timestamp
|
|
@@ -4648,13 +4731,14 @@ const operations = {
|
|
|
4648
4731
|
scopes: tags.default([]),
|
|
4649
4732
|
assigneeId: z.string().nullable().default(null),
|
|
4650
4733
|
parentId: z.uuid().nullable().default(null),
|
|
4734
|
+
epicId: z.uuid().nullable().default(null),
|
|
4651
4735
|
automationReady: z.boolean().default(false),
|
|
4652
4736
|
holdReason: z.string().trim().max(2e3).nullable().default(null),
|
|
4653
4737
|
archived: z.boolean().default(false),
|
|
4654
4738
|
uploads: z.array(z.uuid()).max(50).default([]),
|
|
4655
4739
|
customFields: customFieldValues.default({})
|
|
4656
4740
|
}).strict(), ticketSchema, "POST", "/projects/{projectId}/tickets", "write", "Create a ticket. `customFields` maps field IDs from list_custom_fields to values; omitted fields take their default, and a required field must end with a value. `uploads` lists your staged inline uploads in this project; they become its attachments in the same write, or nothing is created. Reuse requestId when retrying this write.", { mcp: "core" }),
|
|
4657
|
-
duplicate_ticket: op(ticketInput.extend(mutation).strict(), ticketSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/duplicate", "write", "Copy a ticket into a new To do ticket in the same project, linked to the original by a clones relationship. Copies requirements, properties, labels, scopes, and
|
|
4741
|
+
duplicate_ticket: op(ticketInput.extend(mutation).strict(), ticketSchema, "POST", "/projects/{projectId}/tickets/{ticketId}/duplicate", "write", "Copy a ticket into a new To do ticket in the same project, linked to the original by a clones relationship. Copies requirements, properties, labels, scopes, parent, and an Epic the caller can still add to; resets agent readiness and never copies claims, comments, evidence, attachments, or children. Reuse requestId when retrying this write."),
|
|
4658
4742
|
update_ticket: op(ticketInput.extend({
|
|
4659
4743
|
...mutation,
|
|
4660
4744
|
expectedVersion: version,
|
|
@@ -4886,6 +4970,18 @@ const operations = {
|
|
|
4886
4970
|
...mutation,
|
|
4887
4971
|
feedId: z.uuid()
|
|
4888
4972
|
}).strict(), okSchema, "DELETE", "/projects/{projectId}/calendar-feeds/{feedId}", "admin", "Revoke a calendar feed. Its link stops working immediately."),
|
|
4973
|
+
get_project_board: op(projectInput, z.object({
|
|
4974
|
+
board: publicBoardSchema.nullable(),
|
|
4975
|
+
tickets: z.array(publicBoardTicketSchema)
|
|
4976
|
+
}), "GET", "/projects/{projectId}/public-board", "admin", "Read the project's public board, or null, and the tickets it selects exactly as it publishes them, even while it is disabled."),
|
|
4977
|
+
update_project_board: op(projectInput.extend({
|
|
4978
|
+
...mutation,
|
|
4979
|
+
expectedVersion: version.nullable(),
|
|
4980
|
+
enabled: z.boolean(),
|
|
4981
|
+
title: text.max(80),
|
|
4982
|
+
labels: tags,
|
|
4983
|
+
excludedLabels: tags
|
|
4984
|
+
}).strict(), publicBoardSchema, "PATCH", "/projects/{projectId}/public-board", "admin", "Create or replace the project's public board. It publishes the key, title, kind, status, and dates of unarchived, unheld tickets that carry any of `labels` and none of `excludedLabels`. Enabling requires at least one label."),
|
|
4889
4985
|
list_views: op(projectInput, z.array(z.object({
|
|
4890
4986
|
id: z.uuid(),
|
|
4891
4987
|
name: z.string(),
|
|
@@ -5101,6 +5197,8 @@ const permissionOverrides = {
|
|
|
5101
5197
|
list_calendar_feeds: "sharing.manage",
|
|
5102
5198
|
create_calendar_feed: "sharing.manage",
|
|
5103
5199
|
revoke_calendar_feed: "sharing.manage",
|
|
5200
|
+
get_project_board: "sharing.manage",
|
|
5201
|
+
update_project_board: "sharing.manage",
|
|
5104
5202
|
update_project: null
|
|
5105
5203
|
};
|
|
5106
5204
|
function operationPermission(name) {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
CREATE TABLE "public_boards" (
|
|
2
|
+
"project_id" uuid PRIMARY KEY NOT NULL,
|
|
3
|
+
"enabled" boolean DEFAULT false NOT NULL,
|
|
4
|
+
"title" text NOT NULL,
|
|
5
|
+
"labels" text[] DEFAULT '{}' NOT NULL,
|
|
6
|
+
"excluded_labels" text[] DEFAULT '{}' NOT NULL,
|
|
7
|
+
"version" integer DEFAULT 1 NOT NULL,
|
|
8
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
9
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
10
|
+
);
|
|
11
|
+
--> statement-breakpoint
|
|
12
|
+
ALTER TABLE "public_boards" ADD CONSTRAINT "public_boards_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
13
|
+
CREATE VIEW "public_board_candidates" WITH (security_barrier) AS
|
|
14
|
+
SELECT t."project_id", t."key", t."title", t."kind", t."status", t."created_at", t."updated_at"
|
|
15
|
+
FROM "tickets" t
|
|
16
|
+
JOIN "public_boards" b ON b."project_id" = t."project_id"
|
|
17
|
+
JOIN "projects" p ON p."id" = b."project_id"
|
|
18
|
+
WHERE NOT t."archived"
|
|
19
|
+
AND t."hold_reason" IS NULL
|
|
20
|
+
AND t."labels" && b."labels"
|
|
21
|
+
AND NOT t."labels" && b."excluded_labels"
|
|
22
|
+
AND p."archived_at" IS NULL
|
|
23
|
+
AND p."deleted_at" IS NULL;
|
|
24
|
+
--> statement-breakpoint
|
|
25
|
+
CREATE VIEW "public_board_tickets" WITH (security_barrier) AS
|
|
26
|
+
SELECT c."project_id", c."key", c."title", c."kind", c."status", c."created_at", c."updated_at"
|
|
27
|
+
FROM "public_board_candidates" c
|
|
28
|
+
JOIN "public_boards" b ON b."project_id" = c."project_id"
|
|
29
|
+
WHERE b."enabled";
|
|
30
|
+
--> statement-breakpoint
|
|
31
|
+
CREATE VIEW "public_board_summaries" WITH (security_barrier) AS
|
|
32
|
+
SELECT b."project_id", b."title"
|
|
33
|
+
FROM "public_boards" b
|
|
34
|
+
JOIN "projects" p ON p."id" = b."project_id"
|
|
35
|
+
WHERE b."enabled"
|
|
36
|
+
AND p."archived_at" IS NULL
|
|
37
|
+
AND p."deleted_at" IS NULL;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
CREATE TABLE "epics" (
|
|
2
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
3
|
+
"workspace_id" uuid NOT NULL,
|
|
4
|
+
"title" text NOT NULL,
|
|
5
|
+
"description" text DEFAULT '' NOT NULL,
|
|
6
|
+
"archived" boolean DEFAULT false NOT NULL,
|
|
7
|
+
"created_by" text NOT NULL,
|
|
8
|
+
"version" integer DEFAULT 1 NOT NULL,
|
|
9
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
10
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
11
|
+
);
|
|
12
|
+
--> statement-breakpoint
|
|
13
|
+
ALTER TABLE "tickets" ADD COLUMN "epic_id" uuid;--> statement-breakpoint
|
|
14
|
+
ALTER TABLE "epics" ADD CONSTRAINT "epics_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
15
|
+
ALTER TABLE "epics" ADD CONSTRAINT "epics_created_by_user_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
16
|
+
CREATE INDEX "epic_workspace" ON "epics" USING btree ("workspace_id");--> statement-breakpoint
|
|
17
|
+
ALTER TABLE "tickets" ADD CONSTRAINT "tickets_epic_id_epics_id_fk" FOREIGN KEY ("epic_id") REFERENCES "public"."epics"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
|
18
|
+
CREATE INDEX "ticket_epic" ON "tickets" USING btree ("epic_id") WHERE "tickets"."epic_id" IS NOT NULL;
|