@kernhq/module-tracker 0.1.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.
Files changed (158) hide show
  1. package/LICENSE +662 -0
  2. package/README.md +265 -0
  3. package/dist/contract/events.d.ts +140 -0
  4. package/dist/contract/events.d.ts.map +1 -0
  5. package/dist/contract/events.js +51 -0
  6. package/dist/contract/events.js.map +1 -0
  7. package/dist/contract/index.d.ts +6 -0
  8. package/dist/contract/index.d.ts.map +1 -0
  9. package/dist/contract/index.js +6 -0
  10. package/dist/contract/index.js.map +1 -0
  11. package/dist/contract/models.d.ts +2547 -0
  12. package/dist/contract/models.d.ts.map +1 -0
  13. package/dist/contract/models.js +1214 -0
  14. package/dist/contract/models.js.map +1 -0
  15. package/dist/contract/notifications.d.ts +4 -0
  16. package/dist/contract/notifications.d.ts.map +1 -0
  17. package/dist/contract/notifications.js +46 -0
  18. package/dist/contract/notifications.js.map +1 -0
  19. package/dist/contract/permissions.d.ts +161 -0
  20. package/dist/contract/permissions.d.ts.map +1 -0
  21. package/dist/contract/permissions.js +192 -0
  22. package/dist/contract/permissions.js.map +1 -0
  23. package/dist/contract/router.d.ts +9659 -0
  24. package/dist/contract/router.d.ts.map +1 -0
  25. package/dist/contract/router.js +732 -0
  26. package/dist/contract/router.js.map +1 -0
  27. package/dist/kql/ast.d.ts +90 -0
  28. package/dist/kql/ast.d.ts.map +1 -0
  29. package/dist/kql/ast.js +80 -0
  30. package/dist/kql/ast.js.map +1 -0
  31. package/dist/kql/dates.d.ts +16 -0
  32. package/dist/kql/dates.d.ts.map +1 -0
  33. package/dist/kql/dates.js +54 -0
  34. package/dist/kql/dates.js.map +1 -0
  35. package/dist/kql/fields.d.ts +61 -0
  36. package/dist/kql/fields.d.ts.map +1 -0
  37. package/dist/kql/fields.js +106 -0
  38. package/dist/kql/fields.js.map +1 -0
  39. package/dist/kql/index.d.ts +16 -0
  40. package/dist/kql/index.d.ts.map +1 -0
  41. package/dist/kql/index.js +16 -0
  42. package/dist/kql/index.js.map +1 -0
  43. package/dist/kql/lexer.d.ts +28 -0
  44. package/dist/kql/lexer.d.ts.map +1 -0
  45. package/dist/kql/lexer.js +127 -0
  46. package/dist/kql/lexer.js.map +1 -0
  47. package/dist/kql/parser.d.ts +14 -0
  48. package/dist/kql/parser.d.ts.map +1 -0
  49. package/dist/kql/parser.js +290 -0
  50. package/dist/kql/parser.js.map +1 -0
  51. package/dist/kql/suggest.d.ts +8 -0
  52. package/dist/kql/suggest.d.ts.map +1 -0
  53. package/dist/kql/suggest.js +96 -0
  54. package/dist/kql/suggest.js.map +1 -0
  55. package/dist/kql/validate.d.ts +10 -0
  56. package/dist/kql/validate.d.ts.map +1 -0
  57. package/dist/kql/validate.js +84 -0
  58. package/dist/kql/validate.js.map +1 -0
  59. package/dist/server/index.d.ts +17 -0
  60. package/dist/server/index.d.ts.map +1 -0
  61. package/dist/server/index.js +583 -0
  62. package/dist/server/index.js.map +1 -0
  63. package/dist/server/kql/compile.d.ts +49 -0
  64. package/dist/server/kql/compile.d.ts.map +1 -0
  65. package/dist/server/kql/compile.js +372 -0
  66. package/dist/server/kql/compile.js.map +1 -0
  67. package/dist/server/rank.d.ts +23 -0
  68. package/dist/server/rank.d.ts.map +1 -0
  69. package/dist/server/rank.js +66 -0
  70. package/dist/server/rank.js.map +1 -0
  71. package/dist/server/rich.d.ts +30 -0
  72. package/dist/server/rich.d.ts.map +1 -0
  73. package/dist/server/rich.js +97 -0
  74. package/dist/server/rich.js.map +1 -0
  75. package/dist/server/router.d.ts +10589 -0
  76. package/dist/server/router.d.ts.map +1 -0
  77. package/dist/server/router.js +431 -0
  78. package/dist/server/router.js.map +1 -0
  79. package/dist/server/schema.d.ts +6622 -0
  80. package/dist/server/schema.d.ts.map +1 -0
  81. package/dist/server/schema.js +581 -0
  82. package/dist/server/schema.js.map +1 -0
  83. package/dist/server/services/access.d.ts +53 -0
  84. package/dist/server/services/access.d.ts.map +1 -0
  85. package/dist/server/services/access.js +146 -0
  86. package/dist/server/services/access.js.map +1 -0
  87. package/dist/server/services/comments.d.ts +36 -0
  88. package/dist/server/services/comments.d.ts.map +1 -0
  89. package/dist/server/services/comments.js +231 -0
  90. package/dist/server/services/comments.js.map +1 -0
  91. package/dist/server/services/config.d.ts +130 -0
  92. package/dist/server/services/config.d.ts.map +1 -0
  93. package/dist/server/services/config.js +797 -0
  94. package/dist/server/services/config.js.map +1 -0
  95. package/dist/server/services/db.d.ts +93 -0
  96. package/dist/server/services/db.d.ts.map +1 -0
  97. package/dist/server/services/db.js +519 -0
  98. package/dist/server/services/db.js.map +1 -0
  99. package/dist/server/services/imports.d.ts +37 -0
  100. package/dist/server/services/imports.d.ts.map +1 -0
  101. package/dist/server/services/imports.js +324 -0
  102. package/dist/server/services/imports.js.map +1 -0
  103. package/dist/server/services/index.d.ts +36 -0
  104. package/dist/server/services/index.d.ts.map +1 -0
  105. package/dist/server/services/index.js +57 -0
  106. package/dist/server/services/index.js.map +1 -0
  107. package/dist/server/services/intake.d.ts +47 -0
  108. package/dist/server/services/intake.d.ts.map +1 -0
  109. package/dist/server/services/intake.js +268 -0
  110. package/dist/server/services/intake.js.map +1 -0
  111. package/dist/server/services/issues.d.ts +763 -0
  112. package/dist/server/services/issues.d.ts.map +1 -0
  113. package/dist/server/services/issues.js +1381 -0
  114. package/dist/server/services/issues.js.map +1 -0
  115. package/dist/server/services/notify.d.ts +51 -0
  116. package/dist/server/services/notify.d.ts.map +1 -0
  117. package/dist/server/services/notify.js +90 -0
  118. package/dist/server/services/notify.js.map +1 -0
  119. package/dist/server/services/planning.d.ts +62 -0
  120. package/dist/server/services/planning.d.ts.map +1 -0
  121. package/dist/server/services/planning.js +614 -0
  122. package/dist/server/services/planning.js.map +1 -0
  123. package/dist/server/services/projects.d.ts +43 -0
  124. package/dist/server/services/projects.d.ts.map +1 -0
  125. package/dist/server/services/projects.js +375 -0
  126. package/dist/server/services/projects.js.map +1 -0
  127. package/dist/server/services/query.d.ts +24 -0
  128. package/dist/server/services/query.d.ts.map +1 -0
  129. package/dist/server/services/query.js +312 -0
  130. package/dist/server/services/query.js.map +1 -0
  131. package/dist/server/services/reports.d.ts +30 -0
  132. package/dist/server/services/reports.d.ts.map +1 -0
  133. package/dist/server/services/reports.js +295 -0
  134. package/dist/server/services/reports.js.map +1 -0
  135. package/dist/server/services/time.d.ts +53 -0
  136. package/dist/server/services/time.d.ts.map +1 -0
  137. package/dist/server/services/time.js +192 -0
  138. package/dist/server/services/time.js.map +1 -0
  139. package/dist/server/services/transitions.d.ts +61 -0
  140. package/dist/server/services/transitions.d.ts.map +1 -0
  141. package/dist/server/services/transitions.js +493 -0
  142. package/dist/server/services/transitions.js.map +1 -0
  143. package/dist/server/services/views.d.ts +33 -0
  144. package/dist/server/services/views.d.ts.map +1 -0
  145. package/dist/server/services/views.js +195 -0
  146. package/dist/server/services/views.js.map +1 -0
  147. package/migrations/0000_init.sql +529 -0
  148. package/migrations/0001_rls.sql +259 -0
  149. package/migrations/meta/0000_snapshot.json +4140 -0
  150. package/migrations/meta/_journal.json +20 -0
  151. package/package.json +82 -0
  152. package/src/client/api.ts +15 -0
  153. package/src/client/format.ts +164 -0
  154. package/src/client/group.ts +150 -0
  155. package/src/client/index.ts +60 -0
  156. package/src/client/kql.ts +492 -0
  157. package/src/client/rank.ts +87 -0
  158. package/src/client/types.ts +57 -0
package/README.md ADDED
@@ -0,0 +1,265 @@
1
+ # @kernhq/module-tracker
2
+
3
+ Issue tracking for Kern: projects, work items, configurable workflows, a query language, planning
4
+ (cycles, milestones, versions), saved views, time tracking, reports, public intake and imports.
5
+
6
+ ```ts
7
+ import trackerModule from '@kernhq/module-tracker/server'
8
+
9
+ await createKernel({ service: 'core', modules: [coreModule, trackerModule] })
10
+ ```
11
+
12
+ | entry point | contains |
13
+ |---|---|
14
+ | `@kernhq/module-tracker/contract` | Zod models, the oRPC contract, events, permissions, notification types |
15
+ | `@kernhq/module-tracker/kql` | the query language: lexer, parser, validator, autocomplete (isomorphic — no database) |
16
+ | `@kernhq/module-tracker/server` | `defineServerModule`: Drizzle schema, migrations, router, procedures, jobs, search, resolvers |
17
+ | `@kernhq/module-tracker/migrations` | the SQL the kernel applies into `mod_tracker` |
18
+
19
+ Routes are mounted at `/api/tracker`; other modules reach it through `kernel.call('tracker.*')`.
20
+
21
+ ---
22
+
23
+ ## Domain model
24
+
25
+ ### Projects
26
+
27
+ A **project** owns a key (`KRN`), a set of work item types, at least one workflow, and everything
28
+ planned inside it. Issue keys are allocated from a per-project counter row (`issue_counters`), so
29
+ `KRN-1`, `KRN-2`, … are gapless and concurrent creates serialise on one narrow row instead of the
30
+ project itself.
31
+
32
+ Visibility is `workspace` (anyone with `tracker.project.view`) or `private` (project members only).
33
+ Project membership carries a role (`admin` / `member` / `viewer`) and is the unit that per-project
34
+ permission bindings hang off.
35
+
36
+ A project is created from a **template** — `software`, `kanban`, `simple` or `blank` — which seeds a
37
+ workflow, the work item types and four built-in views. An existing project can be snapshotted into a
38
+ reusable `ProjectTemplate` and new projects created from it.
39
+
40
+ ### Work items
41
+
42
+ | concept | what it is |
43
+ |---|---|
44
+ | **Work item type** | Epic / Story / Task / Bug / Sub-task. Carries a hierarchy `level` (`-1` sub-item, `0` standard, `1` epic, `2` initiative), an optional workflow and a field layout. |
45
+ | **Issue** | The work item itself. Key, type, title, rich-text description, status, priority, assignees, labels, components, versions, cycle, milestone, parent, estimate, dates, custom fields, watchers. |
46
+ | **Hierarchy** | `parentId` plus per-workspace `HierarchyRules` (`allowSkipLevels`, `allowSameLevel`, `maxSubItemDepth`). A child may never sit above its parent. |
47
+ | **Rank** | A base-62 fractional index. Reordering writes one row, never the rest of the list. |
48
+ | **Relations** | `blocks`, `relates`, `duplicates`, `clones` and their inverses. Stored as a pair of rows so either side lists its relations with one indexed lookup. |
49
+
50
+ `Issue.custom` is a JSONB bag keyed by custom-field key; `cf.<key>` addresses it in KQL, and a GIN
51
+ index makes those lookups cheap.
52
+
53
+ ### Workflows
54
+
55
+ Statuses and transitions come from [`@kernhq/workflow`](../workflow). A transition can carry
56
+ **conditions** (offered only when they pass), **validators** (checked against what the user
57
+ submitted), **post-functions** (planned as intents the tracker executes: set a field, assign,
58
+ set a resolution, notify, call a webhook, create a sub-item) and an **approval** spec.
59
+
60
+ Which workflow an issue follows is resolved in this order:
61
+
62
+ ```
63
+ work item type's explicit workflow
64
+ → the project's workflow scheme (per-type mapping, else its default)
65
+ → the project's default workflow
66
+ → the workspace default workflow
67
+ ```
68
+
69
+ Every status change writes an `issue_status_history` row with the time spent in the previous status.
70
+ That table is the source for the cumulative flow and cycle-time reports — the reports are never
71
+ recomputed from the current state.
72
+
73
+ ### Planning
74
+
75
+ **Cycles** are numbered per project and move through `upcoming → active → completed`. Starting one
76
+ snapshots its scope so velocity can compare committed against completed; completing one rolls
77
+ unfinished work into the next cycle (or the backlog) and records the carry-over. **Milestones**,
78
+ **versions** and **components** are project-scoped; **labels** may be project- or workspace-scoped,
79
+ and labels sharing a `groupName` are mutually exclusive on an issue.
80
+
81
+ ### Views
82
+
83
+ A view is a saved KQL query plus how to draw it (`list`, `board`, `calendar`, `timeline`,
84
+ `spreadsheet`), its grouping, ordering and visible columns. Visibility is `private`, `project` or
85
+ `workspace`; **pinning is per user**, so it lives in its own table and never mutates the shared row.
86
+
87
+ ### Intake and email
88
+
89
+ Enabling intake mints a token. `GET /api/tracker/intake/{token}` and `POST` to the same path are the
90
+ only unauthenticated procedures: they are rate-limited, carry a honeypot field, and land in the
91
+ triage queue. Inbound email arrives from the mail module through
92
+ `kernel.call('tracker.issues.createFromEmail', …)` and threads onto an existing issue by
93
+ `In-Reply-To` / `References`, then by an issue key in the subject, before creating anything new.
94
+
95
+ ### Time
96
+
97
+ A **worklog** adds to `timeSpentSec` and (optionally) reduces the remaining estimate. A user may have
98
+ one running **timer** per workspace; starting a second one stops the first, so time is never counted
99
+ twice.
100
+
101
+ ---
102
+
103
+ ## Tenancy
104
+
105
+ Every tenant table carries `workspace_id`, a composite index starting with it, and an RLS policy
106
+ generated by `rlsPolicySql` (see `migrations/0001_rls.sql`). All access goes through
107
+ `kernel.database.withWorkspace(workspaceId, …)`, which sets the `app.workspace_id` the policy reads —
108
+ outside it the tables are empty.
109
+
110
+ There is **no wildcard escape hatch**. Two tables therefore sit deliberately outside RLS because they
111
+ have to be readable before a tenant is known, and they contain nothing but ids:
112
+
113
+ - `workspaces` — which workspaces the tracker is active in, so the scheduled jobs can iterate them;
114
+ - `intake_tokens` — token → workspace, so an anonymous intake request can be routed at all.
115
+
116
+ ---
117
+
118
+ ## KQL — Kern Query Language
119
+
120
+ A JQL-like language over issues. It is parsed and validated in `@kernhq/module-tracker/kql` (which
121
+ runs in the browser too, for autocomplete and error underlining) and compiled to parameterised SQL
122
+ server-side.
123
+
124
+ ### Grammar
125
+
126
+ ```ebnf
127
+ query = [ expr ] [ order-clause ] ;
128
+
129
+ expr = or-expr ;
130
+ or-expr = and-expr { "or" and-expr } ;
131
+ and-expr = unary { "and" unary } ;
132
+ unary = "not" unary
133
+ | "(" expr ")"
134
+ | comparison ;
135
+
136
+ comparison = field ( operator value
137
+ | "in" "(" value { "," value } ")"
138
+ | "not" "in" "(" value { "," value } ")"
139
+ | "is" [ "not" ] "empty" ) ;
140
+
141
+ operator = "=" | "!=" | "<" | "<=" | ">" | ">=" | "~" | "!~" ;
142
+
143
+ value = string | number | date | reldate
144
+ | "true" | "false" | "null"
145
+ | bareword | function ;
146
+ function = ident "(" [ value { "," value } ] ")" ;
147
+
148
+ order-clause = "order" "by" order-item { "," order-item } ;
149
+ order-item = field [ "asc" | "desc" ] ;
150
+
151
+ field = ident | "cf." ident ; (* custom fields *)
152
+ ident = letter { letter | digit | "_" | "." | "-" } ;
153
+ string = '"' { char | '\' char } '"' | "'" { char | '\' char } "'" ;
154
+ number = [ "+" | "-" ] digit { digit } [ "." digit { digit } ] ;
155
+ date = 4digit "-" 2digit "-" 2digit [ ( "T" | " " ) time ] ;
156
+ reldate = [ "+" | "-" ] digit { digit } ( "h" | "d" | "w" | "m" | "y" ) ;
157
+ ```
158
+
159
+ Keywords (`and`, `or`, `not`, `in`, `is`, `empty`, `order`, `by`, `asc`, `desc`, `true`, `false`,
160
+ `null`) are case-insensitive; values are not. `and` binds tighter than `or`. An empty query matches
161
+ everything the caller may see.
162
+
163
+ ### Operators by field kind
164
+
165
+ | kind | operators |
166
+ |---|---|
167
+ | text | `~` `!~` `=` `!=` `is empty` `is not empty` |
168
+ | enum (priority, statusCategory, select) | `=` `!=` `in` `not in` `<` `<=` `>` `>=` `is empty` `is not empty` |
169
+ | user (assignee, reporter, watcher) | `=` `!=` `in` `not in` `is empty` `is not empty` |
170
+ | number (estimate, timeSpent, `cf.*`) | `=` `!=` `<` `<=` `>` `>=` `in` `not in` `is empty` `is not empty` |
171
+ | date / datetime | `=` `!=` `<` `<=` `>` `>=` `is empty` `is not empty` |
172
+ | boolean (triage, archived) | `=` `!=` |
173
+ | ref (project, type, status, label, component, version, cycle, milestone, parent) | `=` `!=` `in` `not in` `is empty` `is not empty` |
174
+ | key | `=` `!=` `in` `not in` `~` `!~` |
175
+
176
+ `priority` compares by severity, not alphabetically: `priority > medium` matches `high` and `urgent`.
177
+ Array-valued fields (`assignee`, `label`, `component`, `version`, `watcher`) use membership: `=` and
178
+ `in` mean "contains any of", and `!=` / `not in` negate that.
179
+
180
+ ### Functions
181
+
182
+ | function | meaning |
183
+ |---|---|
184
+ | `currentUser()` | the authenticated user |
185
+ | `now()` | the current instant |
186
+ | `startOfDay(n?)` `startOfWeek(n?)` `startOfMonth(n?)` | UTC boundaries, with an optional offset (`startOfWeek(-1)` = last week) |
187
+ | `activeCycle()` | the active cycle of each queried project |
188
+ | `openCycles()` | active and upcoming cycles |
189
+ | `membersOf("group")` | members of a workspace group |
190
+
191
+ Relative dates are written inline: `-7d`, `+2w`, `-3m`, `-1y`, `-6h`.
192
+
193
+ ### Worked examples
194
+
195
+ ```kql
196
+ -- everything assigned to me that is not finished, most urgent first
197
+ assignee = currentUser() and statusCategory != done order by priority desc, updated desc
198
+
199
+ -- this cycle's board, excluding the backlog
200
+ cycle = activeCycle() and statusCategory != backlog order by rank
201
+
202
+ -- bugs nobody has picked up, filed in the last week
203
+ type = bug and assignee is empty and created > -7d
204
+
205
+ -- overdue work that is neither done nor cancelled
206
+ due < startOfDay() and statusCategory not in (done, cancelled)
207
+
208
+ -- regressions that block something else
209
+ label = regression and status in ("In Progress", "In Review")
210
+
211
+ -- full-text across title and description
212
+ text ~ "connection reset" order by updated desc
213
+
214
+ -- a custom field, addressed by its key
215
+ cf.story_points >= 8 and cf.impact = high
216
+
217
+ -- negation and grouping
218
+ not (label = chore or label = docs) and priority >= high
219
+
220
+ -- issues with no estimate that are already in flight
221
+ estimate is empty and statusCategory = in_progress
222
+
223
+ -- one specific issue, or a family of keys
224
+ key = KRN-123
225
+ key ~ KRN
226
+ ```
227
+
228
+ Names resolve to ids server-side: `label = regression`, `status = "In Progress"`,
229
+ `project = KRN`, `type = bug` all work, as do raw uuids. A name that matches nothing yields no
230
+ results rather than an error, so a renamed label degrades to an empty list instead of a failure.
231
+
232
+ `kql.parse` returns the AST, a normalised form (`printQuery`), errors with character spans, and
233
+ autocomplete suggestions; `kql.fields` lists every queryable field with its operators and known
234
+ values, including `cf.*` for the projects in scope.
235
+
236
+ ---
237
+
238
+ ## Events, jobs and procedures
239
+
240
+ **Events** (`tracker.issue.created/updated/status_changed/assigned/commented/deleted/archived/due`,
241
+ `tracker.cycle.started/completed`, `tracker.project.*`, `tracker.version.released`) are published on
242
+ every mutation, alongside a `kernel.realtime.change` so open clients update without a refresh.
243
+
244
+ **Jobs** run per workspace: `due-soon` (daily), `recurring` (every 15 minutes), `cycles` (hourly:
245
+ auto-start, auto-complete with roll-over, cycle-ending reminders, waking snoozed triage items), `sla`
246
+ (every 10 minutes) and `import` (queued).
247
+
248
+ **Procedures** for other modules: `tracker.issues.get`, `.getMany`, `.create`, `.update`,
249
+ `.createFromEmail`, `.addComment`, `tracker.projects.get`, `.members`, `tracker.collab.access`. They
250
+ are service-to-service and reject end-user principals.
251
+
252
+ ---
253
+
254
+ ## Development
255
+
256
+ ```bash
257
+ pnpm --filter @kernhq/module-tracker test # unit + integration (needs Postgres 18)
258
+ pnpm --filter @kernhq/module-tracker typecheck
259
+ pnpm --filter @kernhq/module-tracker db:generate # after changing src/server/schema.ts
260
+ ```
261
+
262
+ Integration tests create their own scratch database from `DATABASE_URL` (default
263
+ `postgres://kern:kern@localhost:5432/kern`) and drop it afterwards. After regenerating the schema,
264
+ re-generate the RLS migration for any new tenant table — it is derived from `TENANT_TABLES` with
265
+ `rlsPolicySql`, and generated Drizzle SQL never contains RLS.
@@ -0,0 +1,140 @@
1
+ import { z } from 'zod';
2
+ /** Events published by the tracker module (also available as automation triggers). */
3
+ export declare const trackerEvents: {
4
+ issueCreated: import("@kernhq/contracts").EventDef<"tracker.issue.created", z.ZodObject<{
5
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
6
+ projectId: z.ZodUUID;
7
+ issueId: z.ZodUUID;
8
+ key: z.ZodString;
9
+ typeId: z.ZodUUID;
10
+ title: z.ZodString;
11
+ statusId: z.ZodString;
12
+ priority: z.ZodEnum<{
13
+ none: "none";
14
+ low: "low";
15
+ medium: "medium";
16
+ high: "high";
17
+ urgent: "urgent";
18
+ }>;
19
+ assigneeIds: z.ZodArray<z.core.$ZodBranded<z.ZodUUID, "UserId", "out">>;
20
+ triage: z.ZodBoolean;
21
+ source: z.ZodString;
22
+ }, z.core.$strip>>;
23
+ issueUpdated: import("@kernhq/contracts").EventDef<"tracker.issue.updated", z.ZodObject<{
24
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
25
+ projectId: z.ZodUUID;
26
+ issueId: z.ZodUUID;
27
+ key: z.ZodString;
28
+ changes: z.ZodArray<z.ZodObject<{
29
+ field: z.ZodString;
30
+ from: z.ZodUnknown;
31
+ to: z.ZodUnknown;
32
+ }, z.core.$strip>>;
33
+ }, z.core.$strip>>;
34
+ issueStatusChanged: import("@kernhq/contracts").EventDef<"tracker.issue.status_changed", z.ZodObject<{
35
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
36
+ projectId: z.ZodUUID;
37
+ issueId: z.ZodUUID;
38
+ key: z.ZodString;
39
+ fromStatusId: z.ZodNullable<z.ZodString>;
40
+ toStatusId: z.ZodString;
41
+ fromCategory: z.ZodNullable<z.ZodEnum<{
42
+ backlog: "backlog";
43
+ todo: "todo";
44
+ in_progress: "in_progress";
45
+ done: "done";
46
+ cancelled: "cancelled";
47
+ triage: "triage";
48
+ }>>;
49
+ toCategory: z.ZodEnum<{
50
+ backlog: "backlog";
51
+ todo: "todo";
52
+ in_progress: "in_progress";
53
+ done: "done";
54
+ cancelled: "cancelled";
55
+ triage: "triage";
56
+ }>;
57
+ transitionId: z.ZodNullable<z.ZodString>;
58
+ resolution: z.ZodNullable<z.ZodString>;
59
+ }, z.core.$strip>>;
60
+ issueAssigned: import("@kernhq/contracts").EventDef<"tracker.issue.assigned", z.ZodObject<{
61
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
62
+ projectId: z.ZodUUID;
63
+ issueId: z.ZodUUID;
64
+ key: z.ZodString;
65
+ assigneeIds: z.ZodArray<z.core.$ZodBranded<z.ZodUUID, "UserId", "out">>;
66
+ added: z.ZodArray<z.core.$ZodBranded<z.ZodUUID, "UserId", "out">>;
67
+ removed: z.ZodArray<z.core.$ZodBranded<z.ZodUUID, "UserId", "out">>;
68
+ }, z.core.$strip>>;
69
+ issueCommented: import("@kernhq/contracts").EventDef<"tracker.issue.commented", z.ZodObject<{
70
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
71
+ projectId: z.ZodUUID;
72
+ issueId: z.ZodUUID;
73
+ key: z.ZodString;
74
+ commentId: z.ZodUUID;
75
+ parentId: z.ZodNullable<z.ZodUUID>;
76
+ authorId: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "UserId", "out">>;
77
+ }, z.core.$strip>>;
78
+ issueDeleted: import("@kernhq/contracts").EventDef<"tracker.issue.deleted", z.ZodObject<{
79
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
80
+ projectId: z.ZodUUID;
81
+ issueId: z.ZodUUID;
82
+ key: z.ZodString;
83
+ }, z.core.$strip>>;
84
+ issueArchived: import("@kernhq/contracts").EventDef<"tracker.issue.archived", z.ZodObject<{
85
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
86
+ projectId: z.ZodUUID;
87
+ issueId: z.ZodUUID;
88
+ key: z.ZodString;
89
+ archived: z.ZodBoolean;
90
+ }, z.core.$strip>>;
91
+ issueDue: import("@kernhq/contracts").EventDef<"tracker.issue.due", z.ZodObject<{
92
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
93
+ projectId: z.ZodUUID;
94
+ issueId: z.ZodUUID;
95
+ key: z.ZodString;
96
+ dueDate: z.ZodString;
97
+ assigneeIds: z.ZodArray<z.core.$ZodBranded<z.ZodUUID, "UserId", "out">>;
98
+ }, z.core.$strip>>;
99
+ cycleStarted: import("@kernhq/contracts").EventDef<"tracker.cycle.started", z.ZodObject<{
100
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
101
+ projectId: z.ZodUUID;
102
+ cycleId: z.ZodUUID;
103
+ number: z.ZodNumber;
104
+ }, z.core.$strip>>;
105
+ cycleCompleted: import("@kernhq/contracts").EventDef<"tracker.cycle.completed", z.ZodObject<{
106
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
107
+ projectId: z.ZodUUID;
108
+ cycleId: z.ZodUUID;
109
+ number: z.ZodNumber;
110
+ carriedOver: z.ZodNumber;
111
+ }, z.core.$strip>>;
112
+ projectCreated: import("@kernhq/contracts").EventDef<"tracker.project.created", z.ZodObject<{
113
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
114
+ projectId: z.ZodUUID;
115
+ key: z.ZodString;
116
+ name: z.ZodString;
117
+ }, z.core.$strip>>;
118
+ projectUpdated: import("@kernhq/contracts").EventDef<"tracker.project.updated", z.ZodObject<{
119
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
120
+ projectId: z.ZodUUID;
121
+ changes: z.ZodArray<z.ZodString>;
122
+ }, z.core.$strip>>;
123
+ projectArchived: import("@kernhq/contracts").EventDef<"tracker.project.archived", z.ZodObject<{
124
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
125
+ projectId: z.ZodUUID;
126
+ archived: z.ZodBoolean;
127
+ }, z.core.$strip>>;
128
+ projectDeleted: import("@kernhq/contracts").EventDef<"tracker.project.deleted", z.ZodObject<{
129
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
130
+ projectId: z.ZodUUID;
131
+ key: z.ZodString;
132
+ }, z.core.$strip>>;
133
+ versionReleased: import("@kernhq/contracts").EventDef<"tracker.version.released", z.ZodObject<{
134
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
135
+ projectId: z.ZodUUID;
136
+ versionId: z.ZodUUID;
137
+ name: z.ZodString;
138
+ }, z.core.$strip>>;
139
+ };
140
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/contract/events.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAUvB,sFAAsF;AACtF,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiFzB,CAAA"}
@@ -0,0 +1,51 @@
1
+ import { defineEvent, Id, UserId, WorkspaceId } from '@kernhq/contracts';
2
+ import { z } from 'zod';
3
+ import { IssueKey, Priority, StatusCategory } from './models.js';
4
+ const issueRef = z.object({
5
+ workspaceId: WorkspaceId,
6
+ projectId: Id,
7
+ issueId: Id,
8
+ key: IssueKey,
9
+ });
10
+ /** Events published by the tracker module (also available as automation triggers). */
11
+ export const trackerEvents = {
12
+ issueCreated: defineEvent('tracker.issue.created', issueRef.extend({
13
+ typeId: Id,
14
+ title: z.string(),
15
+ statusId: z.string(),
16
+ priority: Priority,
17
+ assigneeIds: z.array(UserId),
18
+ triage: z.boolean(),
19
+ source: z.string(),
20
+ })),
21
+ issueUpdated: defineEvent('tracker.issue.updated', issueRef.extend({
22
+ changes: z.array(z.object({ field: z.string(), from: z.unknown(), to: z.unknown() })),
23
+ })),
24
+ issueStatusChanged: defineEvent('tracker.issue.status_changed', issueRef.extend({
25
+ fromStatusId: z.string().nullable(),
26
+ toStatusId: z.string(),
27
+ fromCategory: StatusCategory.nullable(),
28
+ toCategory: StatusCategory,
29
+ transitionId: z.string().nullable(),
30
+ resolution: z.string().nullable(),
31
+ })),
32
+ issueAssigned: defineEvent('tracker.issue.assigned', issueRef.extend({ assigneeIds: z.array(UserId), added: z.array(UserId), removed: z.array(UserId) })),
33
+ issueCommented: defineEvent('tracker.issue.commented', issueRef.extend({ commentId: Id, parentId: Id.nullable(), authorId: UserId.nullable() })),
34
+ issueDeleted: defineEvent('tracker.issue.deleted', issueRef),
35
+ issueArchived: defineEvent('tracker.issue.archived', issueRef.extend({ archived: z.boolean() })),
36
+ issueDue: defineEvent('tracker.issue.due', issueRef.extend({ dueDate: z.string(), assigneeIds: z.array(UserId) })),
37
+ cycleStarted: defineEvent('tracker.cycle.started', z.object({ workspaceId: WorkspaceId, projectId: Id, cycleId: Id, number: z.number().int() })),
38
+ cycleCompleted: defineEvent('tracker.cycle.completed', z.object({
39
+ workspaceId: WorkspaceId,
40
+ projectId: Id,
41
+ cycleId: Id,
42
+ number: z.number().int(),
43
+ carriedOver: z.number().int(),
44
+ })),
45
+ projectCreated: defineEvent('tracker.project.created', z.object({ workspaceId: WorkspaceId, projectId: Id, key: z.string(), name: z.string() })),
46
+ projectUpdated: defineEvent('tracker.project.updated', z.object({ workspaceId: WorkspaceId, projectId: Id, changes: z.array(z.string()) })),
47
+ projectArchived: defineEvent('tracker.project.archived', z.object({ workspaceId: WorkspaceId, projectId: Id, archived: z.boolean() })),
48
+ projectDeleted: defineEvent('tracker.project.deleted', z.object({ workspaceId: WorkspaceId, projectId: Id, key: z.string() })),
49
+ versionReleased: defineEvent('tracker.version.released', z.object({ workspaceId: WorkspaceId, projectId: Id, versionId: Id, name: z.string() })),
50
+ };
51
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/contract/events.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export * from './events.js';
2
+ export * from './models.js';
3
+ export * from './notifications.js';
4
+ export * from './permissions.js';
5
+ export * from './router.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA"}
@@ -0,0 +1,6 @@
1
+ export * from './events.js';
2
+ export * from './models.js';
3
+ export * from './notifications.js';
4
+ export * from './permissions.js';
5
+ export * from './router.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA"}