@kelpie/schemas 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/company.d.ts +8 -0
- package/dist/company.d.ts.map +1 -1
- package/dist/company.js +4 -0
- package/dist/company.js.map +1 -1
- package/dist/consentPurpose.d.ts +49 -0
- package/dist/consentPurpose.d.ts.map +1 -0
- package/dist/consentPurpose.js +40 -0
- package/dist/consentPurpose.js.map +1 -0
- package/dist/customField.d.ts +99 -0
- package/dist/customField.d.ts.map +1 -0
- package/dist/customField.js +133 -0
- package/dist/customField.js.map +1 -0
- package/dist/deal.d.ts +8 -0
- package/dist/deal.d.ts.map +1 -1
- package/dist/deal.js +4 -0
- package/dist/deal.js.map +1 -1
- package/dist/enquiry.d.ts +43 -0
- package/dist/enquiry.d.ts.map +1 -0
- package/dist/enquiry.js +47 -0
- package/dist/enquiry.js.map +1 -0
- package/dist/form.d.ts +117 -2
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +102 -1
- package/dist/form.js.map +1 -1
- package/dist/formSubmission.d.ts +29 -0
- package/dist/formSubmission.d.ts.map +1 -1
- package/dist/formSubmission.js +34 -0
- package/dist/formSubmission.js.map +1 -1
- package/dist/importExport.d.ts +28 -4
- package/dist/importExport.d.ts.map +1 -1
- package/dist/importExport.js +24 -1
- package/dist/importExport.js.map +1 -1
- package/dist/index.d.ts +15 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/list.d.ts +10 -0
- package/dist/list.d.ts.map +1 -1
- package/dist/list.js +6 -0
- package/dist/list.js.map +1 -1
- package/dist/names.d.ts +43 -0
- package/dist/names.d.ts.map +1 -0
- package/dist/names.js +35 -0
- package/dist/names.js.map +1 -0
- package/dist/opportunity.d.ts +10 -0
- package/dist/opportunity.d.ts.map +1 -1
- package/dist/opportunity.js +7 -0
- package/dist/opportunity.js.map +1 -1
- package/dist/partnership.d.ts +8 -0
- package/dist/partnership.d.ts.map +1 -1
- package/dist/partnership.js +4 -0
- package/dist/partnership.js.map +1 -1
- package/dist/person.d.ts +42 -1
- package/dist/person.d.ts.map +1 -1
- package/dist/person.js +23 -0
- package/dist/person.js.map +1 -1
- package/dist/personConsent.d.ts +31 -0
- package/dist/personConsent.d.ts.map +1 -0
- package/dist/personConsent.js +27 -0
- package/dist/personConsent.js.map +1 -0
- package/dist/raise.d.ts +8 -0
- package/dist/raise.d.ts.map +1 -1
- package/dist/raise.js +4 -0
- package/dist/raise.js.map +1 -1
- package/dist/sampleData.d.ts +1 -0
- package/dist/sampleData.d.ts.map +1 -1
- package/dist/sampleData.js +2 -0
- package/dist/sampleData.js.map +1 -1
- package/dist/values.d.ts +86 -8
- package/dist/values.d.ts.map +1 -1
- package/dist/values.js +140 -3
- package/dist/values.js.map +1 -1
- package/package.json +1 -1
- package/src/company.ts +13 -0
- package/src/consentPurpose.ts +95 -0
- package/src/customField.ts +211 -0
- package/src/deal.ts +13 -0
- package/src/enquiry.ts +94 -0
- package/src/form.ts +230 -2
- package/src/formSubmission.ts +63 -0
- package/src/importExport.ts +44 -5
- package/src/index.ts +70 -2
- package/src/list.ts +16 -0
- package/src/names.ts +49 -0
- package/src/opportunity.ts +18 -0
- package/src/partnership.ts +13 -0
- package/src/person.ts +69 -1
- package/src/personConsent.ts +60 -0
- package/src/raise.ts +13 -0
- package/src/sampleData.ts +3 -0
- package/src/values.ts +169 -3
package/dist/values.js
CHANGED
|
@@ -74,6 +74,7 @@ export const EXTENSIBLE_RECORD_TYPES = [
|
|
|
74
74
|
'opportunity',
|
|
75
75
|
'partnership',
|
|
76
76
|
'raise',
|
|
77
|
+
'enquiry',
|
|
77
78
|
'role',
|
|
78
79
|
'candidate',
|
|
79
80
|
];
|
|
@@ -91,6 +92,7 @@ export const RECORD_TARGET_TYPES = [
|
|
|
91
92
|
'opportunity',
|
|
92
93
|
'partnership',
|
|
93
94
|
'raise',
|
|
95
|
+
'enquiry',
|
|
94
96
|
'candidate',
|
|
95
97
|
];
|
|
96
98
|
/** Human labels for `RECORD_TARGET_TYPES`. Used by any picker that names a type. */
|
|
@@ -101,6 +103,7 @@ export const RECORD_TARGET_TYPE_LABELS = {
|
|
|
101
103
|
opportunity: 'Opportunity',
|
|
102
104
|
partnership: 'Partnership',
|
|
103
105
|
raise: 'Raise',
|
|
106
|
+
enquiry: 'Enquiry',
|
|
104
107
|
candidate: 'Candidate',
|
|
105
108
|
};
|
|
106
109
|
/**
|
|
@@ -119,6 +122,7 @@ export const SEARCH_COLLECTIONS = [
|
|
|
119
122
|
'person',
|
|
120
123
|
'role',
|
|
121
124
|
'company',
|
|
125
|
+
'enquiry',
|
|
122
126
|
'deal',
|
|
123
127
|
'opportunity',
|
|
124
128
|
'raise',
|
|
@@ -167,13 +171,20 @@ export const INTERVIEW_STAGE_LABELS = {
|
|
|
167
171
|
/** Where a candidate enters the process when no stage is named. */
|
|
168
172
|
export const FIRST_INTERVIEW_STAGE = INTERVIEW_STAGES[0];
|
|
169
173
|
/**
|
|
170
|
-
* The
|
|
174
|
+
* The five pipelines whose board columns live in `pipeline_stages`. A Deal moves
|
|
171
175
|
* through `deal` stages and so on; the kinds are fixed even though the stages
|
|
172
176
|
* within each are workspace-configurable.
|
|
173
177
|
*/
|
|
174
|
-
export const PIPELINE_KINDS = [
|
|
178
|
+
export const PIPELINE_KINDS = [
|
|
179
|
+
'enquiry',
|
|
180
|
+
'deal',
|
|
181
|
+
'opportunity',
|
|
182
|
+
'raise',
|
|
183
|
+
'partnership',
|
|
184
|
+
];
|
|
175
185
|
/** Display names for `PIPELINE_KINDS`. "Fundraising" is what the nav calls a Raise. */
|
|
176
186
|
export const PIPELINE_KIND_LABELS = {
|
|
187
|
+
enquiry: 'Enquiry',
|
|
177
188
|
deal: 'Deal',
|
|
178
189
|
opportunity: 'Opportunity',
|
|
179
190
|
raise: 'Fundraising',
|
|
@@ -243,32 +254,59 @@ export const FORM_STATUSES = ['active', 'paused'];
|
|
|
243
254
|
* uploads, multi-page forms and branching out of scope, and every type here is
|
|
244
255
|
* one `<input>`, `<textarea>` or `<select>`.
|
|
245
256
|
*/
|
|
246
|
-
export const FORM_FIELD_TYPES = ['text', 'email', 'textarea', 'select'];
|
|
257
|
+
export const FORM_FIELD_TYPES = ['text', 'email', 'textarea', 'select', 'consent', 'notice'];
|
|
258
|
+
/**
|
|
259
|
+
* The field types whose answer is stored as a boolean, not a string. A
|
|
260
|
+
* `consent` field renders as a checkbox whose label carries the consent
|
|
261
|
+
* statement; the answer records whether the visitor ticked it.
|
|
262
|
+
*/
|
|
263
|
+
export const FORM_BOOLEAN_FIELD_TYPES = ['consent'];
|
|
247
264
|
/**
|
|
248
265
|
* Where a field's answer lands on submit.
|
|
249
266
|
*
|
|
250
267
|
* `position.title` rather than a person field, because a job title belongs to the
|
|
251
268
|
* Person↔Company link and nowhere else. `submission` stores the answer without
|
|
252
269
|
* writing any CRM record, which is what a free-text "How can we help?" wants.
|
|
270
|
+
*
|
|
271
|
+
* A form asks for a name the way its author wants it asked: one `person.name`
|
|
272
|
+
* box, or the `person.first_name` / `person.last_name` pair most sign-up forms
|
|
273
|
+
* use. Mapping the pair and no `person.name` composes the display name from the
|
|
274
|
+
* two, so neither arrangement makes a nameless person. `salutation` and `suffix`
|
|
275
|
+
* are storable on a Person but are not offered here — a public form asking for
|
|
276
|
+
* them is rare enough that the choice is not worth the room in this list.
|
|
253
277
|
*/
|
|
254
278
|
export const FORM_FIELD_MAP_TARGETS = [
|
|
255
279
|
'person.name',
|
|
280
|
+
'person.first_name',
|
|
281
|
+
'person.last_name',
|
|
256
282
|
'person.email',
|
|
283
|
+
'person.consent',
|
|
257
284
|
'company.name',
|
|
258
285
|
'company.domain',
|
|
259
286
|
'position.title',
|
|
287
|
+
'enquiry.name',
|
|
260
288
|
'deal.name',
|
|
289
|
+
'opportunity.name',
|
|
290
|
+
'partnership.name',
|
|
261
291
|
'submission',
|
|
262
292
|
];
|
|
263
293
|
export const FORM_FIELD_MAP_TARGET_LABELS = {
|
|
264
294
|
'person.name': 'Person · name',
|
|
295
|
+
'person.first_name': 'Person · first name',
|
|
296
|
+
'person.last_name': 'Person · last name',
|
|
265
297
|
'person.email': 'Person · email',
|
|
298
|
+
'person.consent': 'Person · consent',
|
|
266
299
|
'company.name': 'Company · name',
|
|
267
300
|
'company.domain': 'Company · domain',
|
|
268
301
|
'position.title': 'Position · title',
|
|
302
|
+
'enquiry.name': 'Enquiry · name',
|
|
269
303
|
'deal.name': 'Deal · name',
|
|
304
|
+
'opportunity.name': 'Opportunity · name',
|
|
305
|
+
'partnership.name': 'Partnership · name',
|
|
270
306
|
submission: 'Submission only',
|
|
271
307
|
};
|
|
308
|
+
/** The map target for a consent field. Repeats per purpose, unlike `person.email`. */
|
|
309
|
+
export const PERSON_CONSENT_TARGET = 'person.consent';
|
|
272
310
|
/** The one mapping a form cannot process without, and may carry at most once. */
|
|
273
311
|
export const PERSON_EMAIL_TARGET = 'person.email';
|
|
274
312
|
/**
|
|
@@ -278,6 +316,13 @@ export const PERSON_EMAIL_TARGET = 'person.email';
|
|
|
278
316
|
* the string means, so a consumer knows `"true"` was a checkbox and not a word.
|
|
279
317
|
*/
|
|
280
318
|
export const FORM_OPTION_VALUE_TYPES = ['string', 'number', 'boolean'];
|
|
319
|
+
/**
|
|
320
|
+
* Each post-submit action lands one of these on the submission's `action_log`.
|
|
321
|
+
* `ok` = the action ran; `skipped` = its precondition was absent (a company
|
|
322
|
+
* list on a submit that never resolved one); `error` = the savepoint rolled
|
|
323
|
+
* back so the rest of the submit could continue.
|
|
324
|
+
*/
|
|
325
|
+
export const FORM_ACTION_STATUSES = ['ok', 'skipped', 'error'];
|
|
281
326
|
/**
|
|
282
327
|
* The domain events a webhook can subscribe to.
|
|
283
328
|
*
|
|
@@ -321,6 +366,7 @@ export const AGENT_TASK_TARGET_TYPES = [
|
|
|
321
366
|
'opportunity',
|
|
322
367
|
'partnership',
|
|
323
368
|
'raise',
|
|
369
|
+
'enquiry',
|
|
324
370
|
'candidate',
|
|
325
371
|
'role',
|
|
326
372
|
'handbook',
|
|
@@ -341,4 +387,95 @@ export const AGENT_RUN_STATUS_LABELS = {
|
|
|
341
387
|
succeeded: 'Succeeded',
|
|
342
388
|
failed: 'Failed',
|
|
343
389
|
};
|
|
390
|
+
/**
|
|
391
|
+
* The seven record types a workspace may attach custom field definitions to.
|
|
392
|
+
*
|
|
393
|
+
* The ones that already carry `tags` and agent-oriented fields on the record
|
|
394
|
+
* itself. Role and Candidate are absent on purpose: hiring state hangs off the
|
|
395
|
+
* Candidate link and a Role is a header rather than a rich record, so the demand
|
|
396
|
+
* signal for custom fields on either has not landed. Kept separate from
|
|
397
|
+
* `RECORD_TARGET_TYPES` and `EXTENSIBLE_RECORD_TYPES` so a later addition here
|
|
398
|
+
* cannot silently widen either of those.
|
|
399
|
+
*/
|
|
400
|
+
export const CUSTOM_FIELD_OBJECT_TYPES = [
|
|
401
|
+
'person',
|
|
402
|
+
'company',
|
|
403
|
+
'deal',
|
|
404
|
+
'opportunity',
|
|
405
|
+
'partnership',
|
|
406
|
+
'raise',
|
|
407
|
+
'enquiry',
|
|
408
|
+
];
|
|
409
|
+
export const CUSTOM_FIELD_OBJECT_TYPE_LABELS = {
|
|
410
|
+
person: 'Person',
|
|
411
|
+
company: 'Company',
|
|
412
|
+
deal: 'Deal',
|
|
413
|
+
opportunity: 'Opportunity',
|
|
414
|
+
partnership: 'Partnership',
|
|
415
|
+
raise: 'Raise',
|
|
416
|
+
enquiry: 'Enquiry',
|
|
417
|
+
};
|
|
418
|
+
/**
|
|
419
|
+
* The nine editor types a custom field can carry.
|
|
420
|
+
*
|
|
421
|
+
* Deliberately distinct from `FORM_FIELD_TYPES`: a form asks a stranger for four
|
|
422
|
+
* kinds of text over one submit; a custom field is a first-class attribute on a
|
|
423
|
+
* record with numeric, date, boolean and currency answers. Sharing one list
|
|
424
|
+
* would drag either surface toward the other's shape.
|
|
425
|
+
*
|
|
426
|
+
* `record_reference` is intentionally absent from v1 — it needs the polymorphic
|
|
427
|
+
* existence check and delete-transaction cleanup that Phase 2 owns.
|
|
428
|
+
*/
|
|
429
|
+
export const CUSTOM_FIELD_TYPES = [
|
|
430
|
+
'text',
|
|
431
|
+
'long_text',
|
|
432
|
+
'number',
|
|
433
|
+
'currency',
|
|
434
|
+
'date',
|
|
435
|
+
'checkbox',
|
|
436
|
+
'select',
|
|
437
|
+
'multi_select',
|
|
438
|
+
'url',
|
|
439
|
+
];
|
|
440
|
+
export const CUSTOM_FIELD_TYPE_LABELS = {
|
|
441
|
+
text: 'Text',
|
|
442
|
+
long_text: 'Long text',
|
|
443
|
+
number: 'Number',
|
|
444
|
+
currency: 'Currency',
|
|
445
|
+
date: 'Date',
|
|
446
|
+
checkbox: 'Checkbox',
|
|
447
|
+
select: 'Select',
|
|
448
|
+
multi_select: 'Multi-select',
|
|
449
|
+
url: 'URL',
|
|
450
|
+
};
|
|
451
|
+
/**
|
|
452
|
+
* The custom-field types whose definition carries an `options` list. Every
|
|
453
|
+
* other type stores its `options` as `[]` and any non-empty write is `422`.
|
|
454
|
+
*/
|
|
455
|
+
export const CUSTOM_FIELD_TYPES_WITH_OPTIONS = ['select', 'multi_select'];
|
|
456
|
+
/**
|
|
457
|
+
* The default a consent purpose starts with, and what a person without an
|
|
458
|
+
* explicit `person_consents` row inherits for that purpose. `unknown` is the
|
|
459
|
+
* safest default — silence about a person's wishes is not a grant.
|
|
460
|
+
*/
|
|
461
|
+
export const CONSENT_PURPOSE_STATUSES = ['unknown', 'granted', 'withdrawn'];
|
|
462
|
+
export const CONSENT_PURPOSE_STATUS_LABELS = {
|
|
463
|
+
unknown: 'Unknown',
|
|
464
|
+
granted: 'Granted',
|
|
465
|
+
withdrawn: 'Withdrawn',
|
|
466
|
+
};
|
|
467
|
+
/**
|
|
468
|
+
* The explicit status on a `person_consents` row. `unknown` is deliberately
|
|
469
|
+
* absent: no row is the unknown, so a stored row always carries a decision.
|
|
470
|
+
*/
|
|
471
|
+
export const CONSENT_STATUSES = ['granted', 'withdrawn'];
|
|
472
|
+
export const CONSENT_STATUS_LABELS = {
|
|
473
|
+
granted: 'Granted',
|
|
474
|
+
withdrawn: 'Withdrawn',
|
|
475
|
+
};
|
|
476
|
+
/**
|
|
477
|
+
* Where a `person_consents` row came from. `form:<form_id>` and `list:<list_id>`
|
|
478
|
+
* are prefixed so the origin is inspectable; `import` and `manual` need no id.
|
|
479
|
+
*/
|
|
480
|
+
export const CONSENT_SOURCE_KINDS = ['form', 'list', 'import', 'manual'];
|
|
344
481
|
//# sourceMappingURL=values.js.map
|
package/dist/values.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"values.js","sourceRoot":"","sources":["../src/values.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"values.js","sourceRoot":"","sources":["../src/values.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/company.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
|
|
3
|
+
import { customFieldValuesBody, customFieldValuesSchema } from './customField.ts'
|
|
4
|
+
import type { CustomFieldValue, CustomFieldValues } from './customField.ts'
|
|
3
5
|
import { ACCOUNT_TYPES, COMPANY_STAGES, ICP_FITS, SIZE_BANDS } from './values.ts'
|
|
4
6
|
import type { AccountType, CompanyStage, IcpFit, SizeBand } from './values.ts'
|
|
5
7
|
import { definedFields, idSchema, recordTimestamps } from './wire.ts'
|
|
@@ -24,6 +26,8 @@ export interface Company extends RecordTimestamps {
|
|
|
24
26
|
readonly tags: readonly string[]
|
|
25
27
|
/** True when this Company represents the workspace itself ("us"). */
|
|
26
28
|
readonly isOwn: boolean
|
|
29
|
+
/** Workspace-defined fields, keyed by definition key. Always present (default `{}`). */
|
|
30
|
+
readonly customFields: CustomFieldValues
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
export const companySchema: z.ZodType<Company, unknown> = z
|
|
@@ -43,6 +47,7 @@ export const companySchema: z.ZodType<Company, unknown> = z
|
|
|
43
47
|
summary: z.string(),
|
|
44
48
|
tags: z.array(z.string()),
|
|
45
49
|
is_own: z.boolean(),
|
|
50
|
+
custom_fields: customFieldValuesSchema,
|
|
46
51
|
...recordTimestamps,
|
|
47
52
|
})
|
|
48
53
|
.transform(
|
|
@@ -62,6 +67,7 @@ export const companySchema: z.ZodType<Company, unknown> = z
|
|
|
62
67
|
summary: wire.summary,
|
|
63
68
|
tags: wire.tags,
|
|
64
69
|
isOwn: wire.is_own,
|
|
70
|
+
customFields: wire.custom_fields,
|
|
65
71
|
createdAt: wire.created_at,
|
|
66
72
|
updatedAt: wire.updated_at,
|
|
67
73
|
}),
|
|
@@ -82,6 +88,11 @@ export interface CompanyInput {
|
|
|
82
88
|
readonly summary?: string
|
|
83
89
|
readonly tags?: readonly string[]
|
|
84
90
|
readonly isOwn?: boolean
|
|
91
|
+
/**
|
|
92
|
+
* Partial merge patch: sent keys change, `null` clears a key, absent keys are
|
|
93
|
+
* left alone. Unknown keys are rejected at `422`.
|
|
94
|
+
*/
|
|
95
|
+
readonly customFields?: Readonly<Record<string, CustomFieldValue | null>>
|
|
85
96
|
}
|
|
86
97
|
|
|
87
98
|
export function companyBody(input: CompanyInput): Record<string, unknown> {
|
|
@@ -100,5 +111,7 @@ export function companyBody(input: CompanyInput): Record<string, unknown> {
|
|
|
100
111
|
summary: input.summary,
|
|
101
112
|
tags: input.tags,
|
|
102
113
|
is_own: input.isOwn,
|
|
114
|
+
custom_fields:
|
|
115
|
+
input.customFields === undefined ? undefined : customFieldValuesBody(input.customFields),
|
|
103
116
|
})
|
|
104
117
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import { CONSENT_PURPOSE_STATUSES } from './values.ts'
|
|
4
|
+
import type { ConsentPurposeStatus } 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/consent_purposes`.
|
|
10
|
+
*
|
|
11
|
+
* A workspace defines its own purposes (`contact`, `marketing`, …). Every
|
|
12
|
+
* capture site — forms, lists, imports, the manual override on a Person —
|
|
13
|
+
* names one. Modelled on `custom_field_definitions`: `slug` is immutable and
|
|
14
|
+
* behaves as the key, `default_status` is the workspace default a person
|
|
15
|
+
* without an explicit `person_consents` row inherits, and `sort_order` runs
|
|
16
|
+
* 0..N-1 with the service renumbering on reorder and delete.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** A workspace's definition of one consent purpose. */
|
|
20
|
+
export interface ConsentPurpose extends RecordTimestamps {
|
|
21
|
+
readonly id: string
|
|
22
|
+
/** Immutable after create. Lowercase snake_case, matches `/^[a-z][a-z0-9_]*$/`. */
|
|
23
|
+
readonly slug: string
|
|
24
|
+
readonly label: string
|
|
25
|
+
readonly description: string
|
|
26
|
+
/** The workspace default a person without an explicit row inherits. */
|
|
27
|
+
readonly defaultStatus: ConsentPurposeStatus
|
|
28
|
+
readonly sortOrder: number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const consentPurposeSchema: z.ZodType<ConsentPurpose, unknown> = z
|
|
32
|
+
.object({
|
|
33
|
+
id: idSchema,
|
|
34
|
+
slug: z.string(),
|
|
35
|
+
label: z.string(),
|
|
36
|
+
description: z.string(),
|
|
37
|
+
default_status: z.enum(CONSENT_PURPOSE_STATUSES),
|
|
38
|
+
sort_order: z.number().int(),
|
|
39
|
+
...recordTimestamps,
|
|
40
|
+
})
|
|
41
|
+
.transform(
|
|
42
|
+
(wire): ConsentPurpose => ({
|
|
43
|
+
id: wire.id,
|
|
44
|
+
slug: wire.slug,
|
|
45
|
+
label: wire.label,
|
|
46
|
+
description: wire.description,
|
|
47
|
+
defaultStatus: wire.default_status,
|
|
48
|
+
sortOrder: wire.sort_order,
|
|
49
|
+
createdAt: wire.created_at,
|
|
50
|
+
updatedAt: wire.updated_at,
|
|
51
|
+
}),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The body a create takes. `slug` is only written here; a `PATCH` cannot
|
|
56
|
+
* change it — a rename would strand every stored `person_consents` row and
|
|
57
|
+
* every form/list/import that names the purpose.
|
|
58
|
+
*/
|
|
59
|
+
export interface CreateConsentPurposeInput {
|
|
60
|
+
readonly slug: string
|
|
61
|
+
readonly label: string
|
|
62
|
+
readonly description?: string
|
|
63
|
+
readonly defaultStatus?: ConsentPurposeStatus
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function createConsentPurposeBody(
|
|
67
|
+
input: CreateConsentPurposeInput,
|
|
68
|
+
): Record<string, unknown> {
|
|
69
|
+
return definedFields({
|
|
70
|
+
slug: input.slug,
|
|
71
|
+
label: input.label,
|
|
72
|
+
description: input.description,
|
|
73
|
+
default_status: input.defaultStatus,
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* What a `PATCH` may change. `slug` is fixed for the purpose's lifetime and
|
|
79
|
+
* the strict body naturally makes it a `422`.
|
|
80
|
+
*/
|
|
81
|
+
export interface ConsentPurposeInput {
|
|
82
|
+
readonly label?: string
|
|
83
|
+
readonly description?: string
|
|
84
|
+
readonly defaultStatus?: ConsentPurposeStatus
|
|
85
|
+
readonly sortOrder?: number
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function consentPurposeBody(input: ConsentPurposeInput): Record<string, unknown> {
|
|
89
|
+
return definedFields({
|
|
90
|
+
label: input.label,
|
|
91
|
+
description: input.description,
|
|
92
|
+
default_status: input.defaultStatus,
|
|
93
|
+
sort_order: input.sortOrder,
|
|
94
|
+
})
|
|
95
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CUSTOM_FIELD_OBJECT_TYPES,
|
|
5
|
+
CUSTOM_FIELD_TYPES,
|
|
6
|
+
} from './values.ts'
|
|
7
|
+
import type { CustomFieldObjectType, CustomFieldType } from './values.ts'
|
|
8
|
+
import { definedFields, idSchema, recordTimestamps } from './wire.ts'
|
|
9
|
+
import type { RecordTimestamps } from './wire.ts'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Wire and write shapes for `/v1/custom_fields`.
|
|
13
|
+
*
|
|
14
|
+
* The definition is workspace-scoped. Every record on the six object types
|
|
15
|
+
* carries a `custom_fields` object keyed by definition key; the values are the
|
|
16
|
+
* shape this file describes. The Zod layer here only checks structure — that a
|
|
17
|
+
* value looks like text / number / boolean / a string list / a money object —
|
|
18
|
+
* because the workspace's definitions are read from the database, not the
|
|
19
|
+
* request. Definition-aware checks (this key exists on this object type, this
|
|
20
|
+
* select value is one of the options) run in the service.
|
|
21
|
+
*
|
|
22
|
+
* Merge semantics on the record's `custom_fields` (documented in `api.md`): a
|
|
23
|
+
* `PATCH` sends the keys that change; `null` clears a key; an unknown key is
|
|
24
|
+
* `422` naming `custom_fields.<key>`.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The value union a custom field can carry on the wire. Structural only — the
|
|
29
|
+
* validator in `@kelpie/server` reads the workspace's definitions and refuses a
|
|
30
|
+
* `number` written into a `text` field, an unknown `select` option, and so on.
|
|
31
|
+
*
|
|
32
|
+
* `text`, `long_text`, `url`, `date` and `select` all serialise as strings; a
|
|
33
|
+
* `multi_select` is a `string[]`; a `currency` is a small object; the rest are
|
|
34
|
+
* primitives. Nothing here reads a definition, so the caps below are the
|
|
35
|
+
* absolute ceilings meant to reject a runaway payload before it reaches the
|
|
36
|
+
* database, not the per-type limits (also enforced by the validator).
|
|
37
|
+
*/
|
|
38
|
+
export const customFieldWireValue = z.union([
|
|
39
|
+
z.string().max(65536),
|
|
40
|
+
z.number(),
|
|
41
|
+
z.boolean(),
|
|
42
|
+
z.array(z.string().min(1).max(120)).max(100),
|
|
43
|
+
z.strictObject({
|
|
44
|
+
amount_cents: z.number().int(),
|
|
45
|
+
currency: z.string().regex(/^[A-Z]{3}$/u),
|
|
46
|
+
}),
|
|
47
|
+
])
|
|
48
|
+
|
|
49
|
+
export type CustomFieldWireValue = z.infer<typeof customFieldWireValue>
|
|
50
|
+
|
|
51
|
+
/** A value already parsed and rebuilt as the record the UI holds. */
|
|
52
|
+
export type CustomFieldValue =
|
|
53
|
+
| string
|
|
54
|
+
| number
|
|
55
|
+
| boolean
|
|
56
|
+
| readonly string[]
|
|
57
|
+
| { readonly amountCents: number; readonly currency: string }
|
|
58
|
+
|
|
59
|
+
/** The `custom_fields` object every record on the six object types carries. */
|
|
60
|
+
export type CustomFieldValues = Readonly<Record<string, CustomFieldValue>>
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Parses the wire object into the record shape the UI works with. Currency is
|
|
64
|
+
* the one type that changes shape (`amount_cents` → `amountCents`); everything
|
|
65
|
+
* else is the same value.
|
|
66
|
+
*/
|
|
67
|
+
export const customFieldValuesSchema: z.ZodType<CustomFieldValues, unknown> = z
|
|
68
|
+
.record(z.string(), customFieldWireValue)
|
|
69
|
+
.transform((wire): CustomFieldValues => {
|
|
70
|
+
const out: Record<string, CustomFieldValue> = {}
|
|
71
|
+
for (const [key, value] of Object.entries(wire)) {
|
|
72
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
73
|
+
out[key] = { amountCents: value.amount_cents, currency: value.currency }
|
|
74
|
+
} else {
|
|
75
|
+
out[key] = value
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return out
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The shape a record's `PATCH` accepts for `custom_fields`. A key's value is
|
|
83
|
+
* the wire value, or `null` to clear that key; unknown keys fail at the service
|
|
84
|
+
* layer with `422 custom_fields.<key>`.
|
|
85
|
+
*/
|
|
86
|
+
export const customFieldsPatchShape: z.ZodType<
|
|
87
|
+
Readonly<Record<string, CustomFieldWireValue | null>>,
|
|
88
|
+
unknown
|
|
89
|
+
> = z
|
|
90
|
+
.record(z.string().min(1).max(64), customFieldWireValue.nullable())
|
|
91
|
+
.describe(
|
|
92
|
+
'Workspace-defined fields keyed by definition key. List the workspace\'s definitions with the custom_fields_list tool before writing. A key set to null clears the value; an unknown key is refused.',
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Turns the UI's record shape back into the wire object for a `PATCH` body.
|
|
97
|
+
* The inverse of `customFieldValuesSchema`.
|
|
98
|
+
*/
|
|
99
|
+
export function customFieldValuesBody(
|
|
100
|
+
values: Readonly<Record<string, CustomFieldValue | null>>,
|
|
101
|
+
): Record<string, CustomFieldWireValue | null> {
|
|
102
|
+
const out: Record<string, CustomFieldWireValue | null> = {}
|
|
103
|
+
for (const [key, value] of Object.entries(values)) {
|
|
104
|
+
out[key] = wireCustomFieldValue(value)
|
|
105
|
+
}
|
|
106
|
+
return out
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function wireCustomFieldValue(value: CustomFieldValue | null): CustomFieldWireValue | null {
|
|
110
|
+
if (value === null) return null
|
|
111
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
112
|
+
return value
|
|
113
|
+
}
|
|
114
|
+
// Array.isArray does not narrow a `readonly` array literal, so branch on the
|
|
115
|
+
// currency object's shape and treat everything else as the string list.
|
|
116
|
+
if ('amountCents' in value && 'currency' in value) {
|
|
117
|
+
return { amount_cents: value.amountCents, currency: value.currency }
|
|
118
|
+
}
|
|
119
|
+
return [...value]
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** A workspace's definition of one custom field, for one object type. */
|
|
123
|
+
export interface CustomFieldDefinition extends RecordTimestamps {
|
|
124
|
+
readonly id: string
|
|
125
|
+
readonly objectType: CustomFieldObjectType
|
|
126
|
+
/** Immutable after create. Lowercase snake_case, matches `/^[a-z][a-z0-9_]*$/`. */
|
|
127
|
+
readonly key: string
|
|
128
|
+
readonly label: string
|
|
129
|
+
/** Immutable after create. */
|
|
130
|
+
readonly type: CustomFieldType
|
|
131
|
+
/** Non-empty only for `select` and `multi_select`. */
|
|
132
|
+
readonly options: readonly string[]
|
|
133
|
+
readonly description: string
|
|
134
|
+
readonly sortOrder: number
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export const customFieldDefinitionSchema: z.ZodType<CustomFieldDefinition, unknown> = z
|
|
138
|
+
.object({
|
|
139
|
+
id: idSchema,
|
|
140
|
+
object_type: z.enum(CUSTOM_FIELD_OBJECT_TYPES),
|
|
141
|
+
key: z.string(),
|
|
142
|
+
label: z.string(),
|
|
143
|
+
type: z.enum(CUSTOM_FIELD_TYPES),
|
|
144
|
+
options: z.array(z.string()),
|
|
145
|
+
description: z.string(),
|
|
146
|
+
sort_order: z.number().int(),
|
|
147
|
+
...recordTimestamps,
|
|
148
|
+
})
|
|
149
|
+
.transform(
|
|
150
|
+
(wire): CustomFieldDefinition => ({
|
|
151
|
+
id: wire.id,
|
|
152
|
+
objectType: wire.object_type,
|
|
153
|
+
key: wire.key,
|
|
154
|
+
label: wire.label,
|
|
155
|
+
type: wire.type,
|
|
156
|
+
options: wire.options,
|
|
157
|
+
description: wire.description,
|
|
158
|
+
sortOrder: wire.sort_order,
|
|
159
|
+
createdAt: wire.created_at,
|
|
160
|
+
updatedAt: wire.updated_at,
|
|
161
|
+
}),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* The body a create takes. `object_type`, `key`, and `type` are only written
|
|
166
|
+
* here; a `PATCH` cannot change any of them (see `CustomFieldDefinitionInput`).
|
|
167
|
+
*/
|
|
168
|
+
export interface CreateCustomFieldDefinitionInput {
|
|
169
|
+
readonly objectType: CustomFieldObjectType
|
|
170
|
+
readonly key: string
|
|
171
|
+
readonly label: string
|
|
172
|
+
readonly type: CustomFieldType
|
|
173
|
+
readonly options?: readonly string[]
|
|
174
|
+
readonly description?: string
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function createCustomFieldDefinitionBody(
|
|
178
|
+
input: CreateCustomFieldDefinitionInput,
|
|
179
|
+
): Record<string, unknown> {
|
|
180
|
+
return definedFields({
|
|
181
|
+
object_type: input.objectType,
|
|
182
|
+
key: input.key,
|
|
183
|
+
label: input.label,
|
|
184
|
+
type: input.type,
|
|
185
|
+
options: input.options,
|
|
186
|
+
description: input.description,
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* What a `PATCH` may change. `key` and `type` are fixed for the definition's
|
|
192
|
+
* lifetime — a change would strand records with the wrong value shape — and
|
|
193
|
+
* the strict body naturally makes either a `422`.
|
|
194
|
+
*/
|
|
195
|
+
export interface CustomFieldDefinitionInput {
|
|
196
|
+
readonly label?: string
|
|
197
|
+
readonly options?: readonly string[]
|
|
198
|
+
readonly description?: string
|
|
199
|
+
readonly sortOrder?: number
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function customFieldDefinitionBody(
|
|
203
|
+
input: CustomFieldDefinitionInput,
|
|
204
|
+
): Record<string, unknown> {
|
|
205
|
+
return definedFields({
|
|
206
|
+
label: input.label,
|
|
207
|
+
options: input.options,
|
|
208
|
+
description: input.description,
|
|
209
|
+
sort_order: input.sortOrder,
|
|
210
|
+
})
|
|
211
|
+
}
|
package/src/deal.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
|
|
3
|
+
import { customFieldValuesBody, customFieldValuesSchema } from './customField.ts'
|
|
4
|
+
import type { CustomFieldValue, CustomFieldValues } from './customField.ts'
|
|
3
5
|
import { definedFields, idSchema, recordTimestamps } from './wire.ts'
|
|
4
6
|
import type { RecordTimestamps } from './wire.ts'
|
|
5
7
|
|
|
@@ -22,6 +24,8 @@ export interface Deal extends RecordTimestamps {
|
|
|
22
24
|
readonly summary: string
|
|
23
25
|
readonly tags: readonly string[]
|
|
24
26
|
readonly externalId: string | null
|
|
27
|
+
/** Workspace-defined fields, keyed by definition key. Always present (default `{}`). */
|
|
28
|
+
readonly customFields: CustomFieldValues
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
export const dealSchema: z.ZodType<Deal, unknown> = z
|
|
@@ -41,6 +45,7 @@ export const dealSchema: z.ZodType<Deal, unknown> = z
|
|
|
41
45
|
summary: z.string(),
|
|
42
46
|
tags: z.array(z.string()),
|
|
43
47
|
external_id: z.string().nullable(),
|
|
48
|
+
custom_fields: customFieldValuesSchema,
|
|
44
49
|
...recordTimestamps,
|
|
45
50
|
})
|
|
46
51
|
.transform(
|
|
@@ -60,6 +65,7 @@ export const dealSchema: z.ZodType<Deal, unknown> = z
|
|
|
60
65
|
summary: wire.summary,
|
|
61
66
|
tags: wire.tags,
|
|
62
67
|
externalId: wire.external_id,
|
|
68
|
+
customFields: wire.custom_fields,
|
|
63
69
|
createdAt: wire.created_at,
|
|
64
70
|
updatedAt: wire.updated_at,
|
|
65
71
|
}),
|
|
@@ -80,6 +86,11 @@ export interface DealInput {
|
|
|
80
86
|
readonly summary?: string
|
|
81
87
|
readonly tags?: readonly string[]
|
|
82
88
|
readonly externalId?: string | null
|
|
89
|
+
/**
|
|
90
|
+
* Partial merge patch: sent keys change, `null` clears a key, absent keys are
|
|
91
|
+
* left alone. Unknown keys are rejected at `422`.
|
|
92
|
+
*/
|
|
93
|
+
readonly customFields?: Readonly<Record<string, CustomFieldValue | null>>
|
|
83
94
|
}
|
|
84
95
|
|
|
85
96
|
export function dealBody(input: DealInput): Record<string, unknown> {
|
|
@@ -98,5 +109,7 @@ export function dealBody(input: DealInput): Record<string, unknown> {
|
|
|
98
109
|
summary: input.summary,
|
|
99
110
|
tags: input.tags,
|
|
100
111
|
external_id: input.externalId,
|
|
112
|
+
custom_fields:
|
|
113
|
+
input.customFields === undefined ? undefined : customFieldValuesBody(input.customFields),
|
|
101
114
|
})
|
|
102
115
|
}
|