@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/planItem.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import { PIPELINE_KINDS, PLAN_ITEM_STATUSES } from './values.ts'
|
|
4
|
+
import type { PipelineKind, PlanItemStatus } from './values.ts'
|
|
5
|
+
import { definedFields, idSchema, recordTimestamps } from './wire.ts'
|
|
6
|
+
import type { RecordTimestamps } from './wire.ts'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Wire and write shapes for `/v1/plan_items`.
|
|
10
|
+
*
|
|
11
|
+
* A plan item is a dated action on one of the four pipelines. It replaces any
|
|
12
|
+
* next-step text field (`brief.md`): the date, the owner, and the status are
|
|
13
|
+
* columns, so "what is overdue" is a query rather than a read of prose.
|
|
14
|
+
*
|
|
15
|
+
* `ownerId` is a workspace member id and is null when nobody has taken it. The
|
|
16
|
+
* status is never null: the table defaults it to `todo`, so a plan item always
|
|
17
|
+
* says where it stands.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export interface PlanItem extends RecordTimestamps {
|
|
21
|
+
readonly id: string
|
|
22
|
+
readonly targetType: PipelineKind
|
|
23
|
+
readonly targetId: string
|
|
24
|
+
/** `YYYY-MM-DD`, per `api.md` date-only fields. */
|
|
25
|
+
readonly date: string
|
|
26
|
+
readonly title: string
|
|
27
|
+
readonly ownerId: string | null
|
|
28
|
+
readonly status: PlanItemStatus
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const planItemSchema: z.ZodType<PlanItem, unknown> = z
|
|
32
|
+
.object({
|
|
33
|
+
id: idSchema,
|
|
34
|
+
target_type: z.enum(PIPELINE_KINDS),
|
|
35
|
+
target_id: idSchema,
|
|
36
|
+
date: z.string(),
|
|
37
|
+
title: z.string(),
|
|
38
|
+
owner_id: idSchema.nullable(),
|
|
39
|
+
status: z.enum(PLAN_ITEM_STATUSES),
|
|
40
|
+
...recordTimestamps,
|
|
41
|
+
})
|
|
42
|
+
.transform(
|
|
43
|
+
(wire): PlanItem => ({
|
|
44
|
+
id: wire.id,
|
|
45
|
+
targetType: wire.target_type,
|
|
46
|
+
targetId: wire.target_id,
|
|
47
|
+
date: wire.date,
|
|
48
|
+
title: wire.title,
|
|
49
|
+
ownerId: wire.owner_id,
|
|
50
|
+
status: wire.status,
|
|
51
|
+
createdAt: wire.created_at,
|
|
52
|
+
updatedAt: wire.updated_at,
|
|
53
|
+
}),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
export interface CreatePlanItemInput {
|
|
57
|
+
readonly targetType: PipelineKind
|
|
58
|
+
readonly targetId: string
|
|
59
|
+
readonly date: string
|
|
60
|
+
readonly title: string
|
|
61
|
+
readonly ownerId?: string | null
|
|
62
|
+
readonly status?: PlanItemStatus
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function createPlanItemBody(input: CreatePlanItemInput): Record<string, unknown> {
|
|
66
|
+
return definedFields({
|
|
67
|
+
target_type: input.targetType,
|
|
68
|
+
target_id: input.targetId,
|
|
69
|
+
date: input.date,
|
|
70
|
+
title: input.title,
|
|
71
|
+
owner_id: input.ownerId,
|
|
72
|
+
status: input.status,
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** The target never moves: re-filing a plan item under another record is a delete and a create. */
|
|
77
|
+
export interface PlanItemInput {
|
|
78
|
+
readonly date?: string
|
|
79
|
+
readonly title?: string
|
|
80
|
+
readonly ownerId?: string | null
|
|
81
|
+
readonly status?: PlanItemStatus
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function planItemBody(input: PlanItemInput): Record<string, unknown> {
|
|
85
|
+
return definedFields({
|
|
86
|
+
date: input.date,
|
|
87
|
+
title: input.title,
|
|
88
|
+
owner_id: input.ownerId,
|
|
89
|
+
status: input.status,
|
|
90
|
+
})
|
|
91
|
+
}
|
package/src/position.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import { idSchema, recordTimestamps } from './wire.ts'
|
|
4
|
+
import type { RecordTimestamps } from './wire.ts'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Wire and write shapes for `/v1/positions`, the Person↔Company link that holds
|
|
8
|
+
* the job title. Moving a link to a different person or company is a delete and
|
|
9
|
+
* a create, which is why only the title is updatable.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export interface Position extends RecordTimestamps {
|
|
13
|
+
readonly id: string
|
|
14
|
+
readonly personId: string
|
|
15
|
+
readonly companyId: string
|
|
16
|
+
readonly title: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const positionSchema: z.ZodType<Position, unknown> = z
|
|
20
|
+
.object({
|
|
21
|
+
id: idSchema,
|
|
22
|
+
person_id: idSchema,
|
|
23
|
+
company_id: idSchema,
|
|
24
|
+
title: z.string(),
|
|
25
|
+
...recordTimestamps,
|
|
26
|
+
})
|
|
27
|
+
.transform(
|
|
28
|
+
(wire): Position => ({
|
|
29
|
+
id: wire.id,
|
|
30
|
+
personId: wire.person_id,
|
|
31
|
+
companyId: wire.company_id,
|
|
32
|
+
title: wire.title,
|
|
33
|
+
createdAt: wire.created_at,
|
|
34
|
+
updatedAt: wire.updated_at,
|
|
35
|
+
}),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
export interface CreatePositionInput {
|
|
39
|
+
readonly personId: string
|
|
40
|
+
readonly companyId: string
|
|
41
|
+
readonly title: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function createPositionBody(input: CreatePositionInput): Record<string, unknown> {
|
|
45
|
+
return { person_id: input.personId, company_id: input.companyId, title: input.title }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function updatePositionBody(title: string): Record<string, unknown> {
|
|
49
|
+
return { title }
|
|
50
|
+
}
|
package/src/raise.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
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/raises`. */
|
|
7
|
+
|
|
8
|
+
export interface Raise extends RecordTimestamps {
|
|
9
|
+
readonly id: string
|
|
10
|
+
readonly name: string
|
|
11
|
+
/** Required: the firm. The ongoing investor relationship stays a Partnership. */
|
|
12
|
+
readonly companyId: string
|
|
13
|
+
readonly stageId: string
|
|
14
|
+
readonly checkSizeCents: number | null
|
|
15
|
+
readonly currency: string | null
|
|
16
|
+
readonly thesisFit: string
|
|
17
|
+
/** Why they said no. Meaningful whenever set, not only in the passed stage. */
|
|
18
|
+
readonly passReason: string | null
|
|
19
|
+
readonly ownerId: string | null
|
|
20
|
+
/** `YYYY-MM-DD`, per `api.md` date-only fields. */
|
|
21
|
+
readonly expectedClose: string | null
|
|
22
|
+
readonly personIds: readonly string[]
|
|
23
|
+
readonly summary: string
|
|
24
|
+
readonly tags: readonly string[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const raiseSchema: z.ZodType<Raise, unknown> = z
|
|
28
|
+
.object({
|
|
29
|
+
id: idSchema,
|
|
30
|
+
name: z.string(),
|
|
31
|
+
company_id: idSchema,
|
|
32
|
+
stage_id: idSchema,
|
|
33
|
+
check_size_cents: z.number().int().nullable(),
|
|
34
|
+
currency: z.string().nullable(),
|
|
35
|
+
thesis_fit: z.string(),
|
|
36
|
+
pass_reason: z.string().nullable(),
|
|
37
|
+
owner_id: idSchema.nullable(),
|
|
38
|
+
expected_close: z.string().nullable(),
|
|
39
|
+
person_ids: z.array(idSchema),
|
|
40
|
+
summary: z.string(),
|
|
41
|
+
tags: z.array(z.string()),
|
|
42
|
+
...recordTimestamps,
|
|
43
|
+
})
|
|
44
|
+
.transform(
|
|
45
|
+
(wire): Raise => ({
|
|
46
|
+
id: wire.id,
|
|
47
|
+
name: wire.name,
|
|
48
|
+
companyId: wire.company_id,
|
|
49
|
+
stageId: wire.stage_id,
|
|
50
|
+
checkSizeCents: wire.check_size_cents,
|
|
51
|
+
currency: wire.currency,
|
|
52
|
+
thesisFit: wire.thesis_fit,
|
|
53
|
+
passReason: wire.pass_reason,
|
|
54
|
+
ownerId: wire.owner_id,
|
|
55
|
+
expectedClose: wire.expected_close,
|
|
56
|
+
personIds: wire.person_ids,
|
|
57
|
+
summary: wire.summary,
|
|
58
|
+
tags: wire.tags,
|
|
59
|
+
createdAt: wire.created_at,
|
|
60
|
+
updatedAt: wire.updated_at,
|
|
61
|
+
}),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
export interface RaiseInput {
|
|
65
|
+
readonly name?: string
|
|
66
|
+
readonly companyId?: string
|
|
67
|
+
readonly stageId?: string
|
|
68
|
+
readonly checkSizeCents?: number | null
|
|
69
|
+
readonly currency?: string | null
|
|
70
|
+
readonly thesisFit?: string
|
|
71
|
+
readonly passReason?: string | null
|
|
72
|
+
readonly ownerId?: string | null
|
|
73
|
+
readonly expectedClose?: string | null
|
|
74
|
+
readonly personIds?: readonly string[]
|
|
75
|
+
readonly summary?: string
|
|
76
|
+
readonly tags?: readonly string[]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function raiseBody(input: RaiseInput): Record<string, unknown> {
|
|
80
|
+
return definedFields({
|
|
81
|
+
name: input.name,
|
|
82
|
+
company_id: input.companyId,
|
|
83
|
+
stage_id: input.stageId,
|
|
84
|
+
check_size_cents: input.checkSizeCents,
|
|
85
|
+
currency: input.currency,
|
|
86
|
+
thesis_fit: input.thesisFit,
|
|
87
|
+
pass_reason: input.passReason,
|
|
88
|
+
owner_id: input.ownerId,
|
|
89
|
+
expected_close: input.expectedClose,
|
|
90
|
+
person_ids: input.personIds,
|
|
91
|
+
summary: input.summary,
|
|
92
|
+
tags: input.tags,
|
|
93
|
+
})
|
|
94
|
+
}
|
package/src/role.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import { ROLE_STATUSES } from './values.ts'
|
|
4
|
+
import type { RoleStatus } from './values.ts'
|
|
5
|
+
import { definedFields, idSchema, recordTimestamps } from './wire.ts'
|
|
6
|
+
import type { RecordTimestamps } from './wire.ts'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Wire and write shapes for `/v1/roles`, an opening the workspace is hiring for.
|
|
10
|
+
*
|
|
11
|
+
* A Role holds no people. Candidates attach through `/v1/candidates`, which is
|
|
12
|
+
* the Person↔Role link and the only place hiring pipeline state lives.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export interface Role extends RecordTimestamps {
|
|
16
|
+
readonly id: string
|
|
17
|
+
readonly title: string
|
|
18
|
+
readonly status: RoleStatus
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const roleSchema: z.ZodType<Role, unknown> = z
|
|
22
|
+
.object({
|
|
23
|
+
id: idSchema,
|
|
24
|
+
title: z.string(),
|
|
25
|
+
status: z.enum(ROLE_STATUSES),
|
|
26
|
+
...recordTimestamps,
|
|
27
|
+
})
|
|
28
|
+
.transform(
|
|
29
|
+
(wire): Role => ({
|
|
30
|
+
id: wire.id,
|
|
31
|
+
title: wire.title,
|
|
32
|
+
status: wire.status,
|
|
33
|
+
createdAt: wire.created_at,
|
|
34
|
+
updatedAt: wire.updated_at,
|
|
35
|
+
}),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
export interface RoleInput {
|
|
39
|
+
readonly title?: string
|
|
40
|
+
readonly status?: RoleStatus
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function roleBody(input: RoleInput): Record<string, unknown> {
|
|
44
|
+
return definedFields({ title: input.title, status: input.status })
|
|
45
|
+
}
|
package/src/search.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import { SEARCH_COLLECTIONS } from './values.ts'
|
|
4
|
+
import type { SearchCollection } from './values.ts'
|
|
5
|
+
import { idSchema } from './wire.ts'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Wire shape for `GET /v1/search`. Read-only, so there is no body builder.
|
|
9
|
+
*
|
|
10
|
+
* One object rather than a list envelope, and no cursor. Nine collections are
|
|
11
|
+
* ranked separately, because a `ts_rank` over a handbook page and one over a
|
|
12
|
+
* three-word Role title are not comparable numbers, and "which Deal" and "which
|
|
13
|
+
* handbook page" are different questions anyway.
|
|
14
|
+
*
|
|
15
|
+
* Every group carries `total`, the count of every match, beside `items`, which
|
|
16
|
+
* holds however many the request's `?limit=` allowed.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export interface SearchResult {
|
|
20
|
+
readonly id: string
|
|
21
|
+
readonly title: string
|
|
22
|
+
/**
|
|
23
|
+
* The record's one line of context, already resolved. What it holds depends on
|
|
24
|
+
* the collection: an email for a Person, a domain for a Company, the stage
|
|
25
|
+
* label for a Deal or Raise, the kind for an Opportunity or Partnership, the
|
|
26
|
+
* status for a Role, the slug for a handbook page, and the date decided for a
|
|
27
|
+
* Decision as `YYYY-MM-DD`.
|
|
28
|
+
*/
|
|
29
|
+
readonly subtitle: string | null
|
|
30
|
+
/** A fragment of the record's prose centred on the match. Empty when it has none. */
|
|
31
|
+
readonly snippet: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface SearchResultGroup {
|
|
35
|
+
readonly collection: SearchCollection
|
|
36
|
+
readonly total: number
|
|
37
|
+
readonly items: readonly SearchResult[]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface SearchResults {
|
|
41
|
+
readonly query: string
|
|
42
|
+
readonly limit: number
|
|
43
|
+
/** Across every group, which is what a "12 results" heading is built from. */
|
|
44
|
+
readonly total: number
|
|
45
|
+
readonly groups: readonly SearchResultGroup[]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const resultSchema: z.ZodType<SearchResult, unknown> = z
|
|
49
|
+
.object({
|
|
50
|
+
id: idSchema,
|
|
51
|
+
title: z.string(),
|
|
52
|
+
subtitle: z.string().nullable(),
|
|
53
|
+
snippet: z.string(),
|
|
54
|
+
})
|
|
55
|
+
.transform((wire) => ({
|
|
56
|
+
id: wire.id,
|
|
57
|
+
title: wire.title,
|
|
58
|
+
subtitle: wire.subtitle,
|
|
59
|
+
snippet: wire.snippet,
|
|
60
|
+
}))
|
|
61
|
+
|
|
62
|
+
const groupSchema: z.ZodType<SearchResultGroup, unknown> = z
|
|
63
|
+
.object({
|
|
64
|
+
type: z.enum(SEARCH_COLLECTIONS),
|
|
65
|
+
total: z.number().int(),
|
|
66
|
+
items: z.array(resultSchema),
|
|
67
|
+
})
|
|
68
|
+
.transform((wire) => ({
|
|
69
|
+
collection: wire.type,
|
|
70
|
+
total: wire.total,
|
|
71
|
+
items: wire.items,
|
|
72
|
+
}))
|
|
73
|
+
|
|
74
|
+
export const searchResultsSchema: z.ZodType<SearchResults, unknown> = z
|
|
75
|
+
.object({
|
|
76
|
+
query: z.string(),
|
|
77
|
+
limit: z.number().int(),
|
|
78
|
+
total: z.number().int(),
|
|
79
|
+
groups: z.array(groupSchema),
|
|
80
|
+
})
|
|
81
|
+
.transform((wire) => ({
|
|
82
|
+
query: wire.query,
|
|
83
|
+
limit: wire.limit,
|
|
84
|
+
total: wire.total,
|
|
85
|
+
groups: wire.groups,
|
|
86
|
+
}))
|
package/src/session.ts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import type { Account } from './account.ts'
|
|
4
|
+
import { MEMBER_ROLES } from './values.ts'
|
|
5
|
+
import type { MemberRole } from './values.ts'
|
|
6
|
+
import { definedFields, idSchema } from './wire.ts'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Wire shapes for `/v1/auth/*` and `/v1/workspaces`: how a browser gets a
|
|
10
|
+
* session, and which workspace that session is in.
|
|
11
|
+
*
|
|
12
|
+
* `workspaceId` is nullable because signup creates an account and nothing else.
|
|
13
|
+
* Every CRM endpoint answers `403` until a workspace exists, so the shell has to
|
|
14
|
+
* be able to tell "signed out" from "signed in, no workspace yet".
|
|
15
|
+
*
|
|
16
|
+
* The password-reset pair belongs here rather than in `account.ts` for the same
|
|
17
|
+
* reason sign-in does: a caller running them has no account context yet. What
|
|
18
|
+
* `account.ts` holds is the signed-in person.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export interface Session {
|
|
22
|
+
readonly userId: string
|
|
23
|
+
readonly sessionId: string
|
|
24
|
+
readonly workspaceId: string | null
|
|
25
|
+
readonly role: MemberRole | null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const sessionSchema: z.ZodType<Session, unknown> = z
|
|
29
|
+
.object({
|
|
30
|
+
user_id: idSchema,
|
|
31
|
+
session_id: idSchema,
|
|
32
|
+
workspace_id: idSchema.nullable(),
|
|
33
|
+
role: z.enum(MEMBER_ROLES).nullable(),
|
|
34
|
+
})
|
|
35
|
+
.transform(
|
|
36
|
+
(wire): Session => ({
|
|
37
|
+
userId: wire.user_id,
|
|
38
|
+
sessionId: wire.session_id,
|
|
39
|
+
workspaceId: wire.workspace_id,
|
|
40
|
+
role: wire.role,
|
|
41
|
+
}),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
/** What `POST /v1/auth/login` and `/v1/auth/signup` answer with. */
|
|
45
|
+
export interface SignedInAccount {
|
|
46
|
+
readonly account: Account
|
|
47
|
+
readonly activeWorkspaceId: string | null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const signedInAccountSchema: z.ZodType<SignedInAccount, unknown> = z
|
|
51
|
+
.object({
|
|
52
|
+
account: z.object({ id: idSchema, email: z.string(), name: z.string() }),
|
|
53
|
+
active_workspace_id: idSchema.nullable(),
|
|
54
|
+
})
|
|
55
|
+
.transform(
|
|
56
|
+
(wire): SignedInAccount => ({
|
|
57
|
+
account: wire.account,
|
|
58
|
+
activeWorkspaceId: wire.active_workspace_id,
|
|
59
|
+
}),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
export interface Workspace {
|
|
63
|
+
readonly id: string
|
|
64
|
+
readonly name: string
|
|
65
|
+
readonly slug: string
|
|
66
|
+
readonly timezone: string
|
|
67
|
+
readonly tagline: string | null
|
|
68
|
+
readonly oneLiner: string | null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const workspaceSchema: z.ZodType<Workspace, unknown> = z
|
|
72
|
+
.object({
|
|
73
|
+
id: idSchema,
|
|
74
|
+
name: z.string(),
|
|
75
|
+
slug: z.string(),
|
|
76
|
+
timezone: z.string(),
|
|
77
|
+
tagline: z.string().nullable(),
|
|
78
|
+
one_liner: z.string().nullable(),
|
|
79
|
+
})
|
|
80
|
+
.transform(
|
|
81
|
+
(wire): Workspace => ({
|
|
82
|
+
id: wire.id,
|
|
83
|
+
name: wire.name,
|
|
84
|
+
slug: wire.slug,
|
|
85
|
+
timezone: wire.timezone,
|
|
86
|
+
tagline: wire.tagline,
|
|
87
|
+
oneLiner: wire.one_liner,
|
|
88
|
+
}),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
export interface CreateWorkspaceInput {
|
|
92
|
+
readonly name: string
|
|
93
|
+
readonly slug: string
|
|
94
|
+
readonly timezone: string
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function createWorkspaceBody(input: CreateWorkspaceInput): Record<string, unknown> {
|
|
98
|
+
return { name: input.name, slug: input.slug, timezone: input.timezone }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Settings a workspace admin can change.
|
|
103
|
+
*
|
|
104
|
+
* `tagline` and `oneLiner` are the two strings an agent reads to say who this
|
|
105
|
+
* company is, so `null` clears them and `undefined` leaves them alone, per
|
|
106
|
+
* `api.md`. `definedFields` is what keeps those two apart on the wire.
|
|
107
|
+
*/
|
|
108
|
+
export interface UpdateWorkspaceInput {
|
|
109
|
+
readonly name?: string
|
|
110
|
+
readonly slug?: string
|
|
111
|
+
readonly timezone?: string
|
|
112
|
+
readonly tagline?: string | null
|
|
113
|
+
readonly oneLiner?: string | null
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function updateWorkspaceBody(input: UpdateWorkspaceInput): Record<string, unknown> {
|
|
117
|
+
return definedFields({
|
|
118
|
+
name: input.name,
|
|
119
|
+
slug: input.slug,
|
|
120
|
+
timezone: input.timezone,
|
|
121
|
+
tagline: input.tagline,
|
|
122
|
+
one_liner: input.oneLiner,
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface LogInInput {
|
|
127
|
+
readonly email: string
|
|
128
|
+
readonly password: string
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function logInBody(input: LogInInput): Record<string, unknown> {
|
|
132
|
+
return { email: input.email, password: input.password }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Creating an account. It answers the same shape sign-in does, with
|
|
137
|
+
* `activeWorkspaceId` null: signup makes the user and stops there.
|
|
138
|
+
*/
|
|
139
|
+
export interface SignUpInput {
|
|
140
|
+
readonly name: string
|
|
141
|
+
readonly email: string
|
|
142
|
+
readonly password: string
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function signUpBody(input: SignUpInput): Record<string, unknown> {
|
|
146
|
+
return { name: input.name, email: input.email, password: input.password }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Asking for a reset link.
|
|
151
|
+
*
|
|
152
|
+
* `resetUrlTemplate` carries the literal `{token}`, which the email replaces.
|
|
153
|
+
* The caller supplies it for the same reason an invitation does: the service
|
|
154
|
+
* sends the mail and has no idea what address the browser reached it at.
|
|
155
|
+
*/
|
|
156
|
+
export interface RequestPasswordResetInput {
|
|
157
|
+
readonly email: string
|
|
158
|
+
readonly resetUrlTemplate: string
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function requestPasswordResetBody(
|
|
162
|
+
input: RequestPasswordResetInput,
|
|
163
|
+
): Record<string, unknown> {
|
|
164
|
+
return { email: input.email, reset_url_template: input.resetUrlTemplate }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Spending the token from that email. Answers `204`; it does not sign anyone in. */
|
|
168
|
+
export interface ConfirmPasswordResetInput {
|
|
169
|
+
readonly token: string
|
|
170
|
+
readonly password: string
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function confirmPasswordResetBody(
|
|
174
|
+
input: ConfirmPasswordResetInput,
|
|
175
|
+
): Record<string, unknown> {
|
|
176
|
+
return { token: input.token, password: input.password }
|
|
177
|
+
}
|