@kelpie/schemas 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.
- package/LICENSE +661 -0
- package/README.md +33 -0
- package/dist/account.d.ts +79 -0
- package/dist/account.d.ts.map +1 -0
- package/dist/account.js +66 -0
- package/dist/account.js.map +1 -0
- package/dist/activity.d.ts +23 -0
- package/dist/activity.d.ts.map +1 -0
- package/dist/activity.js +27 -0
- package/dist/activity.js.map +1 -0
- package/dist/agentTask.d.ts +97 -0
- package/dist/agentTask.d.ts.map +1 -0
- package/dist/agentTask.js +127 -0
- package/dist/agentTask.js.map +1 -0
- package/dist/candidate.d.ts +39 -0
- package/dist/candidate.d.ts.map +1 -0
- package/dist/candidate.js +40 -0
- package/dist/candidate.js.map +1 -0
- package/dist/company.d.ts +38 -0
- package/dist/company.d.ts.map +1 -0
- package/dist/company.js +57 -0
- package/dist/company.js.map +1 -0
- package/dist/dashboard.d.ts +112 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +165 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/deal.d.ts +40 -0
- package/dist/deal.d.ts.map +1 -0
- package/dist/deal.js +59 -0
- package/dist/deal.js.map +1 -0
- package/dist/decision.d.ts +46 -0
- package/dist/decision.d.ts.map +1 -0
- package/dist/decision.js +48 -0
- package/dist/decision.js.map +1 -0
- package/dist/form.d.ts +97 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +98 -0
- package/dist/form.js.map +1 -0
- package/dist/formSubmission.d.ts +47 -0
- package/dist/formSubmission.d.ts.map +1 -0
- package/dist/formSubmission.js +47 -0
- package/dist/formSubmission.js.map +1 -0
- package/dist/handbookPage.d.ts +52 -0
- package/dist/handbookPage.d.ts.map +1 -0
- package/dist/handbookPage.js +56 -0
- package/dist/handbookPage.js.map +1 -0
- package/dist/importExport.d.ts +143 -0
- package/dist/importExport.d.ts.map +1 -0
- package/dist/importExport.js +239 -0
- package/dist/importExport.js.map +1 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/invite.d.ts +35 -0
- package/dist/invite.d.ts.map +1 -0
- package/dist/invite.js +31 -0
- package/dist/invite.js.map +1 -0
- package/dist/mcpTool.d.ts +26 -0
- package/dist/mcpTool.d.ts.map +1 -0
- package/dist/mcpTool.js +13 -0
- package/dist/mcpTool.js.map +1 -0
- package/dist/member.d.ts +26 -0
- package/dist/member.d.ts.map +1 -0
- package/dist/member.js +24 -0
- package/dist/member.js.map +1 -0
- package/dist/moduleSetting.d.ts +19 -0
- package/dist/moduleSetting.d.ts.map +1 -0
- package/dist/moduleSetting.js +16 -0
- package/dist/moduleSetting.js.map +1 -0
- package/dist/note.d.ts +34 -0
- package/dist/note.d.ts.map +1 -0
- package/dist/note.js +35 -0
- package/dist/note.js.map +1 -0
- package/dist/opportunity.d.ts +30 -0
- package/dist/opportunity.d.ts.map +1 -0
- package/dist/opportunity.js +41 -0
- package/dist/opportunity.js.map +1 -0
- package/dist/partnership.d.ts +36 -0
- package/dist/partnership.d.ts.map +1 -0
- package/dist/partnership.js +50 -0
- package/dist/partnership.js.map +1 -0
- package/dist/person.d.ts +45 -0
- package/dist/person.d.ts.map +1 -0
- package/dist/person.js +60 -0
- package/dist/person.js.map +1 -0
- package/dist/pipelineStage.d.ts +29 -0
- package/dist/pipelineStage.d.ts.map +1 -0
- package/dist/pipelineStage.js +38 -0
- package/dist/pipelineStage.js.map +1 -0
- package/dist/planItem.d.ts +43 -0
- package/dist/planItem.d.ts.map +1 -0
- package/dist/planItem.js +44 -0
- package/dist/planItem.js.map +1 -0
- package/dist/position.d.ts +22 -0
- package/dist/position.d.ts.map +1 -0
- package/dist/position.js +25 -0
- package/dist/position.js.map +1 -0
- package/dist/raise.d.ts +38 -0
- package/dist/raise.d.ts.map +1 -0
- package/dist/raise.js +53 -0
- package/dist/raise.js.map +1 -0
- package/dist/role.d.ts +21 -0
- package/dist/role.d.ts.map +1 -0
- package/dist/role.js +21 -0
- package/dist/role.js.map +1 -0
- package/dist/search.d.ts +41 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +41 -0
- package/dist/search.js.map +1 -0
- package/dist/session.d.ts +92 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +67 -0
- package/dist/session.js.map +1 -0
- package/dist/values.d.ts +216 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +329 -0
- package/dist/values.js.map +1 -0
- package/dist/webhook.d.ts +85 -0
- package/dist/webhook.d.ts.map +1 -0
- package/dist/webhook.js +75 -0
- package/dist/webhook.js.map +1 -0
- package/dist/wire.d.ts +40 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +40 -0
- package/dist/wire.js.map +1 -0
- package/package.json +51 -0
- package/src/account.ts +149 -0
- package/src/activity.ts +52 -0
- package/src/agentTask.ts +254 -0
- package/src/candidate.ts +83 -0
- package/src/company.ts +98 -0
- package/src/dashboard.ts +307 -0
- package/src/deal.ts +102 -0
- package/src/decision.ts +106 -0
- package/src/form.ts +217 -0
- package/src/formSubmission.ts +99 -0
- package/src/handbookPage.ts +99 -0
- package/src/importExport.ts +339 -0
- package/src/index.ts +324 -0
- package/src/invite.ts +69 -0
- package/src/mcpTool.ts +32 -0
- package/src/member.ts +53 -0
- package/src/moduleSetting.ts +37 -0
- package/src/note.ts +73 -0
- package/src/opportunity.ts +74 -0
- package/src/partnership.ts +89 -0
- package/src/person.ts +116 -0
- package/src/pipelineStage.ts +71 -0
- package/src/planItem.ts +91 -0
- package/src/position.ts +50 -0
- package/src/raise.ts +94 -0
- package/src/role.ts +45 -0
- package/src/search.ts +86 -0
- package/src/session.ts +177 -0
- package/src/values.ts +427 -0
- package/src/webhook.ts +164 -0
- package/src/wire.ts +51 -0
package/src/dashboard.ts
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ACTIVITY_KINDS,
|
|
5
|
+
PIPELINE_KINDS,
|
|
6
|
+
PLAN_ITEM_STATUSES,
|
|
7
|
+
RECORD_TARGET_TYPES,
|
|
8
|
+
} from './values.ts'
|
|
9
|
+
import type { ActivityKind, PipelineKind, PlanItemStatus, RecordTargetType } from './values.ts'
|
|
10
|
+
import { idSchema, nullableTimestampSchema, timestampSchema } from './wire.ts'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Wire shape for `GET /v1/dashboard`. Read-only, so there is no body builder.
|
|
14
|
+
*
|
|
15
|
+
* One object rather than a list envelope: the workspace home is several
|
|
16
|
+
* questions answered together, and two of them — a contact going cold, a
|
|
17
|
+
* partnership touchpoint at hand — are not questions any resource list asks.
|
|
18
|
+
*
|
|
19
|
+
* Every attention signal is `{ total, items }`. `total` counts every matching
|
|
20
|
+
* row; `items` holds however many the request's `?limit=` allowed. A page
|
|
21
|
+
* showing four rows still reports the true count beside them.
|
|
22
|
+
*
|
|
23
|
+
* Rows that point at another record carry `targetName` already resolved. The
|
|
24
|
+
* dashboard is the one place where every row points somewhere different, so
|
|
25
|
+
* naming them client-side would mean a request per row.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/** A row naming a record it is not itself. `targetName` is null when the target no longer resolves. */
|
|
29
|
+
export interface TargetRef {
|
|
30
|
+
readonly targetType: RecordTargetType
|
|
31
|
+
readonly targetId: string
|
|
32
|
+
readonly targetName: string | null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const targetRef = {
|
|
36
|
+
target_type: z.enum(RECORD_TARGET_TYPES),
|
|
37
|
+
target_id: idSchema,
|
|
38
|
+
target_name: z.string().nullable(),
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function toTargetRef(wire: {
|
|
42
|
+
target_type: RecordTargetType
|
|
43
|
+
target_id: string
|
|
44
|
+
target_name: string | null
|
|
45
|
+
}): TargetRef {
|
|
46
|
+
return {
|
|
47
|
+
targetType: wire.target_type,
|
|
48
|
+
targetId: wire.target_id,
|
|
49
|
+
targetName: wire.target_name,
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface DashboardSignal<Item> {
|
|
54
|
+
readonly total: number
|
|
55
|
+
readonly items: readonly Item[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** `{ total, items }` over one item schema. Generic so each signal declares only its row. */
|
|
59
|
+
function signalSchema<Item>(item: z.ZodType<Item, unknown>): z.ZodType<DashboardSignal<Item>, unknown> {
|
|
60
|
+
return z.object({ total: z.number().int(), items: z.array(item) })
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface DashboardPipeline {
|
|
64
|
+
readonly kind: PipelineKind
|
|
65
|
+
/** Records sitting in a stage the workspace has marked open. */
|
|
66
|
+
readonly open: number
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const dashboardPipelineSchema: z.ZodType<DashboardPipeline, unknown> = z.object({
|
|
70
|
+
kind: z.enum(PIPELINE_KINDS),
|
|
71
|
+
open: z.number().int(),
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* An open plan item that is late or due inside the week.
|
|
76
|
+
*
|
|
77
|
+
* `targetType` is narrower than a `TargetRef`'s in practice — a plan item only
|
|
78
|
+
* attaches to the four pipelines — but it is decoded against the wider set so
|
|
79
|
+
* one target shape serves every signal here.
|
|
80
|
+
*/
|
|
81
|
+
export interface DashboardPlanItem extends TargetRef {
|
|
82
|
+
readonly id: string
|
|
83
|
+
/** `YYYY-MM-DD`, per `api.md` date-only fields. */
|
|
84
|
+
readonly date: string
|
|
85
|
+
readonly title: string
|
|
86
|
+
readonly ownerId: string | null
|
|
87
|
+
readonly status: PlanItemStatus
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const dashboardPlanItemSchema: z.ZodType<DashboardPlanItem, unknown> = z
|
|
91
|
+
.object({
|
|
92
|
+
...targetRef,
|
|
93
|
+
id: idSchema,
|
|
94
|
+
date: z.string(),
|
|
95
|
+
title: z.string(),
|
|
96
|
+
owner_id: idSchema.nullable(),
|
|
97
|
+
status: z.enum(PLAN_ITEM_STATUSES),
|
|
98
|
+
})
|
|
99
|
+
.transform((wire) => ({
|
|
100
|
+
...toTargetRef(wire),
|
|
101
|
+
id: wire.id,
|
|
102
|
+
date: wire.date,
|
|
103
|
+
title: wire.title,
|
|
104
|
+
ownerId: wire.owner_id,
|
|
105
|
+
status: wire.status,
|
|
106
|
+
}))
|
|
107
|
+
|
|
108
|
+
export interface DashboardTouchpoint {
|
|
109
|
+
readonly id: string
|
|
110
|
+
readonly name: string
|
|
111
|
+
readonly companyId: string
|
|
112
|
+
/** `YYYY-MM-DD`. */
|
|
113
|
+
readonly nextTouchpoint: string
|
|
114
|
+
/** Whether the date has already passed. Both are in the list, oldest first. */
|
|
115
|
+
readonly overdue: boolean
|
|
116
|
+
readonly ownerId: string | null
|
|
117
|
+
readonly summary: string
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const dashboardTouchpointSchema: z.ZodType<DashboardTouchpoint, unknown> = z
|
|
121
|
+
.object({
|
|
122
|
+
id: idSchema,
|
|
123
|
+
name: z.string(),
|
|
124
|
+
company_id: idSchema,
|
|
125
|
+
next_touchpoint: z.string(),
|
|
126
|
+
overdue: z.boolean(),
|
|
127
|
+
owner_id: idSchema.nullable(),
|
|
128
|
+
summary: z.string(),
|
|
129
|
+
})
|
|
130
|
+
.transform((wire) => ({
|
|
131
|
+
id: wire.id,
|
|
132
|
+
name: wire.name,
|
|
133
|
+
companyId: wire.company_id,
|
|
134
|
+
nextTouchpoint: wire.next_touchpoint,
|
|
135
|
+
overdue: wire.overdue,
|
|
136
|
+
ownerId: wire.owner_id,
|
|
137
|
+
summary: wire.summary,
|
|
138
|
+
}))
|
|
139
|
+
|
|
140
|
+
export interface DashboardStaleContact {
|
|
141
|
+
readonly id: string
|
|
142
|
+
readonly name: string
|
|
143
|
+
readonly email: string | null
|
|
144
|
+
readonly lastContactedAt: Date
|
|
145
|
+
/** Whole days from the last contact to today, both read in the workspace's zone. */
|
|
146
|
+
readonly daysSinceContact: number
|
|
147
|
+
readonly summary: string
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const dashboardStaleContactSchema: z.ZodType<DashboardStaleContact, unknown> = z
|
|
151
|
+
.object({
|
|
152
|
+
id: idSchema,
|
|
153
|
+
name: z.string(),
|
|
154
|
+
email: z.string().nullable(),
|
|
155
|
+
last_contacted_at: timestampSchema,
|
|
156
|
+
days_since_contact: z.number().int(),
|
|
157
|
+
summary: z.string(),
|
|
158
|
+
})
|
|
159
|
+
.transform((wire) => ({
|
|
160
|
+
id: wire.id,
|
|
161
|
+
name: wire.name,
|
|
162
|
+
email: wire.email,
|
|
163
|
+
lastContactedAt: wire.last_contacted_at,
|
|
164
|
+
daysSinceContact: wire.days_since_contact,
|
|
165
|
+
summary: wire.summary,
|
|
166
|
+
}))
|
|
167
|
+
|
|
168
|
+
export interface DashboardActivity extends TargetRef {
|
|
169
|
+
readonly id: string
|
|
170
|
+
readonly kind: ActivityKind
|
|
171
|
+
readonly actorMemberId: string | null
|
|
172
|
+
readonly actorLabel: string | null
|
|
173
|
+
readonly action: string
|
|
174
|
+
readonly detail: string | null
|
|
175
|
+
readonly createdAt: Date
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const dashboardActivitySchema: z.ZodType<DashboardActivity, unknown> = z
|
|
179
|
+
.object({
|
|
180
|
+
...targetRef,
|
|
181
|
+
id: idSchema,
|
|
182
|
+
kind: z.enum(ACTIVITY_KINDS),
|
|
183
|
+
actor_member_id: idSchema.nullable(),
|
|
184
|
+
actor_label: z.string().nullable(),
|
|
185
|
+
action: z.string(),
|
|
186
|
+
detail: z.string().nullable(),
|
|
187
|
+
created_at: timestampSchema,
|
|
188
|
+
})
|
|
189
|
+
.transform((wire) => ({
|
|
190
|
+
...toTargetRef(wire),
|
|
191
|
+
id: wire.id,
|
|
192
|
+
kind: wire.kind,
|
|
193
|
+
actorMemberId: wire.actor_member_id,
|
|
194
|
+
actorLabel: wire.actor_label,
|
|
195
|
+
action: wire.action,
|
|
196
|
+
detail: wire.detail,
|
|
197
|
+
createdAt: wire.created_at,
|
|
198
|
+
}))
|
|
199
|
+
|
|
200
|
+
export interface DashboardNote extends TargetRef {
|
|
201
|
+
readonly id: string
|
|
202
|
+
readonly body: string
|
|
203
|
+
readonly authorId: string | null
|
|
204
|
+
readonly pinned: boolean
|
|
205
|
+
readonly createdAt: Date
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const dashboardNoteSchema: z.ZodType<DashboardNote, unknown> = z
|
|
209
|
+
.object({
|
|
210
|
+
...targetRef,
|
|
211
|
+
id: idSchema,
|
|
212
|
+
body: z.string(),
|
|
213
|
+
author_id: idSchema.nullable(),
|
|
214
|
+
pinned: z.boolean(),
|
|
215
|
+
created_at: timestampSchema,
|
|
216
|
+
})
|
|
217
|
+
.transform((wire) => ({
|
|
218
|
+
...toTargetRef(wire),
|
|
219
|
+
id: wire.id,
|
|
220
|
+
body: wire.body,
|
|
221
|
+
authorId: wire.author_id,
|
|
222
|
+
pinned: wire.pinned,
|
|
223
|
+
createdAt: wire.created_at,
|
|
224
|
+
}))
|
|
225
|
+
|
|
226
|
+
export interface DashboardDecision extends TargetRef {
|
|
227
|
+
readonly id: string
|
|
228
|
+
readonly body: string
|
|
229
|
+
readonly rationale: string | null
|
|
230
|
+
readonly decidedAt: Date
|
|
231
|
+
readonly ownerId: string | null
|
|
232
|
+
readonly dueAt: Date | null
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const dashboardDecisionSchema: z.ZodType<DashboardDecision, unknown> = z
|
|
236
|
+
.object({
|
|
237
|
+
...targetRef,
|
|
238
|
+
id: idSchema,
|
|
239
|
+
body: z.string(),
|
|
240
|
+
rationale: z.string().nullable(),
|
|
241
|
+
decided_at: timestampSchema,
|
|
242
|
+
owner_id: idSchema.nullable(),
|
|
243
|
+
due_at: nullableTimestampSchema,
|
|
244
|
+
})
|
|
245
|
+
.transform((wire) => ({
|
|
246
|
+
...toTargetRef(wire),
|
|
247
|
+
id: wire.id,
|
|
248
|
+
body: wire.body,
|
|
249
|
+
rationale: wire.rationale,
|
|
250
|
+
decidedAt: wire.decided_at,
|
|
251
|
+
ownerId: wire.owner_id,
|
|
252
|
+
dueAt: wire.due_at,
|
|
253
|
+
}))
|
|
254
|
+
|
|
255
|
+
export interface Dashboard {
|
|
256
|
+
readonly generatedAt: Date
|
|
257
|
+
/** The day every signal was measured against, `YYYY-MM-DD`. */
|
|
258
|
+
readonly today: string
|
|
259
|
+
/** The workspace's IANA zone, which is what fixed `today`. */
|
|
260
|
+
readonly timezone: string
|
|
261
|
+
/** How long since a contact before it counts as going cold. */
|
|
262
|
+
readonly staleContactDays: number
|
|
263
|
+
/** How far ahead "due soon" looks. */
|
|
264
|
+
readonly upcomingDays: number
|
|
265
|
+
readonly pipelines: readonly DashboardPipeline[]
|
|
266
|
+
readonly overduePlanItems: DashboardSignal<DashboardPlanItem>
|
|
267
|
+
readonly dueSoonPlanItems: DashboardSignal<DashboardPlanItem>
|
|
268
|
+
readonly partnershipTouchpoints: DashboardSignal<DashboardTouchpoint>
|
|
269
|
+
readonly staleContacts: DashboardSignal<DashboardStaleContact>
|
|
270
|
+
readonly recentActivity: readonly DashboardActivity[]
|
|
271
|
+
readonly recentNotes: readonly DashboardNote[]
|
|
272
|
+
readonly recentDecisions: readonly DashboardDecision[]
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export const dashboardSchema: z.ZodType<Dashboard, unknown> = z
|
|
276
|
+
.object({
|
|
277
|
+
generated_at: timestampSchema,
|
|
278
|
+
today: z.string(),
|
|
279
|
+
timezone: z.string(),
|
|
280
|
+
stale_contact_days: z.number().int(),
|
|
281
|
+
upcoming_days: z.number().int(),
|
|
282
|
+
pipelines: z.array(dashboardPipelineSchema),
|
|
283
|
+
overdue_plan_items: signalSchema(dashboardPlanItemSchema),
|
|
284
|
+
due_soon_plan_items: signalSchema(dashboardPlanItemSchema),
|
|
285
|
+
partnership_touchpoints: signalSchema(dashboardTouchpointSchema),
|
|
286
|
+
stale_contacts: signalSchema(dashboardStaleContactSchema),
|
|
287
|
+
recent_activity: z.array(dashboardActivitySchema),
|
|
288
|
+
recent_notes: z.array(dashboardNoteSchema),
|
|
289
|
+
recent_decisions: z.array(dashboardDecisionSchema),
|
|
290
|
+
})
|
|
291
|
+
.transform(
|
|
292
|
+
(wire): Dashboard => ({
|
|
293
|
+
generatedAt: wire.generated_at,
|
|
294
|
+
today: wire.today,
|
|
295
|
+
timezone: wire.timezone,
|
|
296
|
+
staleContactDays: wire.stale_contact_days,
|
|
297
|
+
upcomingDays: wire.upcoming_days,
|
|
298
|
+
pipelines: wire.pipelines,
|
|
299
|
+
overduePlanItems: wire.overdue_plan_items,
|
|
300
|
+
dueSoonPlanItems: wire.due_soon_plan_items,
|
|
301
|
+
partnershipTouchpoints: wire.partnership_touchpoints,
|
|
302
|
+
staleContacts: wire.stale_contacts,
|
|
303
|
+
recentActivity: wire.recent_activity,
|
|
304
|
+
recentNotes: wire.recent_notes,
|
|
305
|
+
recentDecisions: wire.recent_decisions,
|
|
306
|
+
}),
|
|
307
|
+
)
|
package/src/deal.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import { definedFields, idSchema, recordTimestamps } from './wire.ts'
|
|
4
|
+
import type { RecordTimestamps } from './wire.ts'
|
|
5
|
+
|
|
6
|
+
/** Wire and write shapes for `/v1/deals`. */
|
|
7
|
+
|
|
8
|
+
export interface Deal extends RecordTimestamps {
|
|
9
|
+
readonly id: string
|
|
10
|
+
readonly name: string
|
|
11
|
+
readonly companyId: string
|
|
12
|
+
readonly stageId: string
|
|
13
|
+
readonly valueCents: number | null
|
|
14
|
+
readonly currency: string | null
|
|
15
|
+
readonly ownerId: string | null
|
|
16
|
+
/** `YYYY-MM-DD`, per `api.md` date-only fields. */
|
|
17
|
+
readonly expectedClose: string | null
|
|
18
|
+
readonly personIds: readonly string[]
|
|
19
|
+
readonly competitors: readonly string[]
|
|
20
|
+
readonly risks: string
|
|
21
|
+
readonly whyWin: string
|
|
22
|
+
readonly summary: string
|
|
23
|
+
readonly tags: readonly string[]
|
|
24
|
+
readonly externalId: string | null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const dealSchema: z.ZodType<Deal, unknown> = z
|
|
28
|
+
.object({
|
|
29
|
+
id: idSchema,
|
|
30
|
+
name: z.string(),
|
|
31
|
+
company_id: idSchema,
|
|
32
|
+
stage_id: idSchema,
|
|
33
|
+
value_cents: z.number().int().nullable(),
|
|
34
|
+
currency: z.string().nullable(),
|
|
35
|
+
owner_id: idSchema.nullable(),
|
|
36
|
+
expected_close: z.string().nullable(),
|
|
37
|
+
person_ids: z.array(idSchema),
|
|
38
|
+
competitors: z.array(z.string()),
|
|
39
|
+
risks: z.string(),
|
|
40
|
+
why_win: z.string(),
|
|
41
|
+
summary: z.string(),
|
|
42
|
+
tags: z.array(z.string()),
|
|
43
|
+
external_id: z.string().nullable(),
|
|
44
|
+
...recordTimestamps,
|
|
45
|
+
})
|
|
46
|
+
.transform(
|
|
47
|
+
(wire): Deal => ({
|
|
48
|
+
id: wire.id,
|
|
49
|
+
name: wire.name,
|
|
50
|
+
companyId: wire.company_id,
|
|
51
|
+
stageId: wire.stage_id,
|
|
52
|
+
valueCents: wire.value_cents,
|
|
53
|
+
currency: wire.currency,
|
|
54
|
+
ownerId: wire.owner_id,
|
|
55
|
+
expectedClose: wire.expected_close,
|
|
56
|
+
personIds: wire.person_ids,
|
|
57
|
+
competitors: wire.competitors,
|
|
58
|
+
risks: wire.risks,
|
|
59
|
+
whyWin: wire.why_win,
|
|
60
|
+
summary: wire.summary,
|
|
61
|
+
tags: wire.tags,
|
|
62
|
+
externalId: wire.external_id,
|
|
63
|
+
createdAt: wire.created_at,
|
|
64
|
+
updatedAt: wire.updated_at,
|
|
65
|
+
}),
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
export interface DealInput {
|
|
69
|
+
readonly name?: string
|
|
70
|
+
readonly companyId?: string
|
|
71
|
+
readonly stageId?: string
|
|
72
|
+
readonly valueCents?: number | null
|
|
73
|
+
readonly currency?: string | null
|
|
74
|
+
readonly ownerId?: string | null
|
|
75
|
+
readonly expectedClose?: string | null
|
|
76
|
+
readonly personIds?: readonly string[]
|
|
77
|
+
readonly competitors?: readonly string[]
|
|
78
|
+
readonly risks?: string
|
|
79
|
+
readonly whyWin?: string
|
|
80
|
+
readonly summary?: string
|
|
81
|
+
readonly tags?: readonly string[]
|
|
82
|
+
readonly externalId?: string | null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function dealBody(input: DealInput): Record<string, unknown> {
|
|
86
|
+
return definedFields({
|
|
87
|
+
name: input.name,
|
|
88
|
+
company_id: input.companyId,
|
|
89
|
+
stage_id: input.stageId,
|
|
90
|
+
value_cents: input.valueCents,
|
|
91
|
+
currency: input.currency,
|
|
92
|
+
owner_id: input.ownerId,
|
|
93
|
+
expected_close: input.expectedClose,
|
|
94
|
+
person_ids: input.personIds,
|
|
95
|
+
competitors: input.competitors,
|
|
96
|
+
risks: input.risks,
|
|
97
|
+
why_win: input.whyWin,
|
|
98
|
+
summary: input.summary,
|
|
99
|
+
tags: input.tags,
|
|
100
|
+
external_id: input.externalId,
|
|
101
|
+
})
|
|
102
|
+
}
|
package/src/decision.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import { RECORD_TARGET_TYPES } from './values.ts'
|
|
4
|
+
import type { RecordTargetType } from './values.ts'
|
|
5
|
+
import {
|
|
6
|
+
definedFields,
|
|
7
|
+
idSchema,
|
|
8
|
+
nullableTimestampSchema,
|
|
9
|
+
recordTimestamps,
|
|
10
|
+
timestampSchema,
|
|
11
|
+
} from './wire.ts'
|
|
12
|
+
import type { RecordTimestamps } from './wire.ts'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Wire and write shapes for `/v1/decisions`.
|
|
16
|
+
*
|
|
17
|
+
* A decision is what the workspace decided or promised: a commitment agents
|
|
18
|
+
* must not contradict, kept as a queryable record rather than note text.
|
|
19
|
+
*
|
|
20
|
+
* `ownerId` is a workspace member id, null when nobody carries the commitment.
|
|
21
|
+
* `dueAt` is the optional deadline on it; `decidedAt` is when it was made and is
|
|
22
|
+
* never null.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
export interface Decision extends RecordTimestamps {
|
|
26
|
+
readonly id: string
|
|
27
|
+
readonly targetType: RecordTargetType
|
|
28
|
+
readonly targetId: string
|
|
29
|
+
readonly body: string
|
|
30
|
+
readonly rationale: string | null
|
|
31
|
+
readonly decidedAt: Date
|
|
32
|
+
readonly ownerId: string | null
|
|
33
|
+
readonly dueAt: Date | null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const decisionSchema: z.ZodType<Decision, unknown> = z
|
|
37
|
+
.object({
|
|
38
|
+
id: idSchema,
|
|
39
|
+
target_type: z.enum(RECORD_TARGET_TYPES),
|
|
40
|
+
target_id: idSchema,
|
|
41
|
+
body: z.string(),
|
|
42
|
+
rationale: z.string().nullable(),
|
|
43
|
+
decided_at: timestampSchema,
|
|
44
|
+
owner_id: idSchema.nullable(),
|
|
45
|
+
due_at: nullableTimestampSchema,
|
|
46
|
+
...recordTimestamps,
|
|
47
|
+
})
|
|
48
|
+
.transform(
|
|
49
|
+
(wire): Decision => ({
|
|
50
|
+
id: wire.id,
|
|
51
|
+
targetType: wire.target_type,
|
|
52
|
+
targetId: wire.target_id,
|
|
53
|
+
body: wire.body,
|
|
54
|
+
rationale: wire.rationale,
|
|
55
|
+
decidedAt: wire.decided_at,
|
|
56
|
+
ownerId: wire.owner_id,
|
|
57
|
+
dueAt: wire.due_at,
|
|
58
|
+
createdAt: wire.created_at,
|
|
59
|
+
updatedAt: wire.updated_at,
|
|
60
|
+
}),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
export interface CreateDecisionInput {
|
|
64
|
+
readonly targetType: RecordTargetType
|
|
65
|
+
readonly targetId: string
|
|
66
|
+
readonly body: string
|
|
67
|
+
readonly rationale?: string | null
|
|
68
|
+
/** Absent means now: most decisions are recorded as they are made. */
|
|
69
|
+
readonly decidedAt?: Date
|
|
70
|
+
/** Absent means the caller; null means nobody carries it. */
|
|
71
|
+
readonly ownerId?: string | null
|
|
72
|
+
readonly dueAt?: Date | null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function createDecisionBody(input: CreateDecisionInput): Record<string, unknown> {
|
|
76
|
+
return definedFields({
|
|
77
|
+
target_type: input.targetType,
|
|
78
|
+
target_id: input.targetId,
|
|
79
|
+
body: input.body,
|
|
80
|
+
rationale: input.rationale,
|
|
81
|
+
decided_at: input.decidedAt === undefined ? undefined : input.decidedAt.toISOString(),
|
|
82
|
+
owner_id: input.ownerId,
|
|
83
|
+
due_at:
|
|
84
|
+
input.dueAt === undefined ? undefined : input.dueAt === null ? null : input.dueAt.toISOString(),
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** The target never moves: re-filing a decision under another record is a delete and a create. */
|
|
89
|
+
export interface DecisionInput {
|
|
90
|
+
readonly body?: string
|
|
91
|
+
readonly rationale?: string | null
|
|
92
|
+
readonly decidedAt?: Date
|
|
93
|
+
readonly ownerId?: string | null
|
|
94
|
+
readonly dueAt?: Date | null
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function decisionBody(input: DecisionInput): Record<string, unknown> {
|
|
98
|
+
return definedFields({
|
|
99
|
+
body: input.body,
|
|
100
|
+
rationale: input.rationale,
|
|
101
|
+
decided_at: input.decidedAt === undefined ? undefined : input.decidedAt.toISOString(),
|
|
102
|
+
owner_id: input.ownerId,
|
|
103
|
+
due_at:
|
|
104
|
+
input.dueAt === undefined ? undefined : input.dueAt === null ? null : input.dueAt.toISOString(),
|
|
105
|
+
})
|
|
106
|
+
}
|