@kelpie/schemas 0.10.0 → 0.12.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 (81) hide show
  1. package/README.md +16 -0
  2. package/dist/account.d.ts +5 -0
  3. package/dist/account.d.ts.map +1 -1
  4. package/dist/account.js +2 -0
  5. package/dist/account.js.map +1 -1
  6. package/dist/apiKey.d.ts +5 -0
  7. package/dist/apiKey.d.ts.map +1 -1
  8. package/dist/apiKey.js +8 -1
  9. package/dist/apiKey.js.map +1 -1
  10. package/dist/apiKeyScopes.d.ts +36 -0
  11. package/dist/apiKeyScopes.d.ts.map +1 -0
  12. package/dist/apiKeyScopes.js +217 -0
  13. package/dist/apiKeyScopes.js.map +1 -0
  14. package/dist/conversion.d.ts +58 -0
  15. package/dist/conversion.d.ts.map +1 -0
  16. package/dist/conversion.js +63 -0
  17. package/dist/conversion.js.map +1 -0
  18. package/dist/deal.d.ts +3 -0
  19. package/dist/deal.d.ts.map +1 -1
  20. package/dist/deal.js +5 -0
  21. package/dist/deal.js.map +1 -1
  22. package/dist/enquiry.d.ts +3 -0
  23. package/dist/enquiry.d.ts.map +1 -1
  24. package/dist/enquiry.js +5 -0
  25. package/dist/enquiry.js.map +1 -1
  26. package/dist/form.d.ts.map +1 -1
  27. package/dist/form.js +2 -2
  28. package/dist/form.js.map +1 -1
  29. package/dist/formMapTargets.d.ts +72 -0
  30. package/dist/formMapTargets.d.ts.map +1 -0
  31. package/dist/formMapTargets.js +385 -0
  32. package/dist/formMapTargets.js.map +1 -0
  33. package/dist/importExport.d.ts +14 -1
  34. package/dist/importExport.d.ts.map +1 -1
  35. package/dist/importExport.js +161 -1
  36. package/dist/importExport.js.map +1 -1
  37. package/dist/index.d.ts +8 -2
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +4 -1
  40. package/dist/index.js.map +1 -1
  41. package/dist/list.d.ts +0 -10
  42. package/dist/list.d.ts.map +1 -1
  43. package/dist/list.js +0 -6
  44. package/dist/list.js.map +1 -1
  45. package/dist/opportunity.d.ts +3 -0
  46. package/dist/opportunity.d.ts.map +1 -1
  47. package/dist/opportunity.js +5 -0
  48. package/dist/opportunity.js.map +1 -1
  49. package/dist/partnership.d.ts +3 -0
  50. package/dist/partnership.d.ts.map +1 -1
  51. package/dist/partnership.js +5 -0
  52. package/dist/partnership.js.map +1 -1
  53. package/dist/raise.d.ts +3 -0
  54. package/dist/raise.d.ts.map +1 -1
  55. package/dist/raise.js +5 -0
  56. package/dist/raise.js.map +1 -1
  57. package/dist/session.d.ts +1 -11
  58. package/dist/session.d.ts.map +1 -1
  59. package/dist/session.js +0 -6
  60. package/dist/session.js.map +1 -1
  61. package/dist/values.d.ts +4 -3
  62. package/dist/values.d.ts.map +1 -1
  63. package/dist/values.js +2 -0
  64. package/dist/values.js.map +1 -1
  65. package/package.json +1 -1
  66. package/src/account.ts +7 -0
  67. package/src/apiKey.ts +13 -1
  68. package/src/apiKeyScopes.ts +264 -0
  69. package/src/conversion.ts +99 -0
  70. package/src/deal.ts +9 -0
  71. package/src/enquiry.ts +9 -0
  72. package/src/form.ts +1 -2
  73. package/src/formMapTargets.ts +548 -0
  74. package/src/importExport.ts +174 -1
  75. package/src/index.ts +61 -0
  76. package/src/list.ts +0 -16
  77. package/src/opportunity.ts +9 -0
  78. package/src/partnership.ts +9 -0
  79. package/src/raise.ts +9 -0
  80. package/src/session.ts +1 -17
  81. package/src/values.ts +7 -2
@@ -0,0 +1,548 @@
1
+ import type { CustomFieldObjectType, CustomFieldType, FormFieldType } from './values.ts'
2
+ import {
3
+ ACCOUNT_TYPES,
4
+ COMPANY_STAGES,
5
+ CUSTOM_FIELD_OBJECT_TYPE_LABELS,
6
+ CUSTOM_FIELD_TYPE_LABELS,
7
+ FORM_FIELD_MAP_TARGET_LABELS,
8
+ ICP_FITS,
9
+ INFLUENCE_LEVELS,
10
+ PERSON_CONSENT_TARGET,
11
+ PERSON_EMAIL_TARGET,
12
+ PREFERRED_CHANNELS,
13
+ RELATIONSHIP_LEVELS,
14
+ SIZE_BANDS,
15
+ } from './values.ts'
16
+
17
+ /**
18
+ * Where a form field's answer may land on submit.
19
+ *
20
+ * Any string matching the catalog (standard fields, workspace custom fields,
21
+ * `submission`, or `person.consent`). The legacy `FORM_FIELD_MAP_TARGETS` enum
22
+ * is a starter subset; validation uses this module instead.
23
+ */
24
+ export type FormFieldMapTarget = string
25
+
26
+ /** Object types that carry standard writable fields in the catalog. */
27
+ export const FORM_MAP_OBJECT_TYPES = [
28
+ 'person',
29
+ 'company',
30
+ 'position',
31
+ 'deal',
32
+ 'opportunity',
33
+ 'partnership',
34
+ 'enquiry',
35
+ 'raise',
36
+ ] as const
37
+
38
+ export type FormMapObjectType = (typeof FORM_MAP_OBJECT_TYPES)[number]
39
+
40
+ export const FORM_MAP_OBJECT_TYPE_LABELS: Readonly<Record<FormMapObjectType, string>> = {
41
+ person: 'Person',
42
+ company: 'Company',
43
+ position: 'Position',
44
+ deal: 'Deal',
45
+ opportunity: 'Opportunity',
46
+ partnership: 'Partnership',
47
+ enquiry: 'Enquiry',
48
+ raise: 'Raise',
49
+ }
50
+
51
+ /** The value shape a mapped target expects from a submit answer. */
52
+ export type FormMapValueType =
53
+ | 'text'
54
+ | 'email'
55
+ | 'phones'
56
+ | 'tags'
57
+ | 'string_array'
58
+ | 'enum'
59
+ | 'boolean'
60
+ | 'date'
61
+ | 'number'
62
+ | 'currency'
63
+ | 'url'
64
+ | 'long_text'
65
+ | 'consent'
66
+
67
+ export interface FormMapStandardField {
68
+ readonly field: string
69
+ readonly label: string
70
+ readonly valueType: FormMapValueType
71
+ /** Present for enum targets — the allowed wire values. */
72
+ readonly enumValues?: readonly string[]
73
+ }
74
+
75
+ export interface FormMapTargetEntry {
76
+ readonly target: string
77
+ readonly objectType: FormMapObjectType | 'submission' | 'consent'
78
+ readonly label: string
79
+ readonly fieldKind: 'standard' | 'custom' | 'special'
80
+ readonly valueType: FormMapValueType
81
+ readonly enumValues?: readonly string[] | undefined
82
+ }
83
+
84
+ /** Minimal custom-field definition shape for building and labelling targets. */
85
+ export interface CustomFieldDefinitionRef {
86
+ readonly objectType: CustomFieldObjectType
87
+ readonly key: string
88
+ readonly label: string
89
+ readonly type: CustomFieldType
90
+ }
91
+
92
+ const PERSON_STANDARD_FIELDS: readonly FormMapStandardField[] = [
93
+ { field: 'name', label: 'name', valueType: 'text' },
94
+ { field: 'salutation', label: 'salutation', valueType: 'text' },
95
+ { field: 'first_name', label: 'first name', valueType: 'text' },
96
+ { field: 'last_name', label: 'last name', valueType: 'text' },
97
+ { field: 'suffix', label: 'suffix', valueType: 'text' },
98
+ { field: 'email', label: 'email', valueType: 'email' },
99
+ { field: 'phones', label: 'phone', valueType: 'phones' },
100
+ { field: 'timezone', label: 'timezone', valueType: 'text' },
101
+ { field: 'location', label: 'location', valueType: 'text' },
102
+ {
103
+ field: 'preferred_channel',
104
+ label: 'preferred channel',
105
+ valueType: 'enum',
106
+ enumValues: PREFERRED_CHANNELS,
107
+ },
108
+ {
109
+ field: 'influence',
110
+ label: 'influence',
111
+ valueType: 'enum',
112
+ enumValues: INFLUENCE_LEVELS,
113
+ },
114
+ {
115
+ field: 'relationship',
116
+ label: 'relationship',
117
+ valueType: 'enum',
118
+ enumValues: RELATIONSHIP_LEVELS,
119
+ },
120
+ { field: 'summary', label: 'summary', valueType: 'long_text' },
121
+ { field: 'tags', label: 'tags', valueType: 'tags' },
122
+ { field: 'do_not_contact', label: 'do not contact', valueType: 'boolean' },
123
+ ]
124
+
125
+ const COMPANY_STANDARD_FIELDS: readonly FormMapStandardField[] = [
126
+ { field: 'name', label: 'name', valueType: 'text' },
127
+ { field: 'domain', label: 'domain', valueType: 'text' },
128
+ { field: 'industry', label: 'industry', valueType: 'text' },
129
+ { field: 'description', label: 'description', valueType: 'long_text' },
130
+ {
131
+ field: 'stage',
132
+ label: 'stage',
133
+ valueType: 'enum',
134
+ enumValues: COMPANY_STAGES,
135
+ },
136
+ {
137
+ field: 'size_band',
138
+ label: 'size band',
139
+ valueType: 'enum',
140
+ enumValues: SIZE_BANDS,
141
+ },
142
+ { field: 'hq', label: 'hq', valueType: 'text' },
143
+ { field: 'website', label: 'website', valueType: 'url' },
144
+ {
145
+ field: 'account_type',
146
+ label: 'account type',
147
+ valueType: 'enum',
148
+ enumValues: ACCOUNT_TYPES,
149
+ },
150
+ {
151
+ field: 'icp_fit',
152
+ label: 'ICP fit',
153
+ valueType: 'enum',
154
+ enumValues: ICP_FITS,
155
+ },
156
+ { field: 'tech_stack', label: 'tech stack', valueType: 'string_array' },
157
+ { field: 'summary', label: 'summary', valueType: 'long_text' },
158
+ { field: 'tags', label: 'tags', valueType: 'tags' },
159
+ ]
160
+
161
+ const POSITION_STANDARD_FIELDS: readonly FormMapStandardField[] = [
162
+ { field: 'title', label: 'title', valueType: 'text' },
163
+ ]
164
+
165
+ const DEAL_STANDARD_FIELDS: readonly FormMapStandardField[] = [
166
+ { field: 'name', label: 'name', valueType: 'text' },
167
+ { field: 'value_cents', label: 'value', valueType: 'number' },
168
+ { field: 'currency', label: 'currency', valueType: 'text' },
169
+ { field: 'expected_close', label: 'expected close', valueType: 'date' },
170
+ { field: 'competitors', label: 'competitors', valueType: 'string_array' },
171
+ { field: 'risks', label: 'risks', valueType: 'long_text' },
172
+ { field: 'why_win', label: 'why win', valueType: 'long_text' },
173
+ { field: 'summary', label: 'summary', valueType: 'long_text' },
174
+ { field: 'tags', label: 'tags', valueType: 'tags' },
175
+ { field: 'external_id', label: 'external id', valueType: 'text' },
176
+ ]
177
+
178
+ const OPPORTUNITY_STANDARD_FIELDS: readonly FormMapStandardField[] = [
179
+ { field: 'name', label: 'name', valueType: 'text' },
180
+ { field: 'kind', label: 'kind', valueType: 'text' },
181
+ { field: 'expected_close', label: 'expected close', valueType: 'date' },
182
+ { field: 'summary', label: 'summary', valueType: 'long_text' },
183
+ { field: 'tags', label: 'tags', valueType: 'tags' },
184
+ ]
185
+
186
+ const PARTNERSHIP_STANDARD_FIELDS: readonly FormMapStandardField[] = [
187
+ { field: 'name', label: 'name', valueType: 'text' },
188
+ { field: 'kind', label: 'kind', valueType: 'text' },
189
+ { field: 'next_touchpoint', label: 'next touchpoint', valueType: 'date' },
190
+ { field: 'goals', label: 'goals', valueType: 'long_text' },
191
+ { field: 'success_looks_like', label: 'success looks like', valueType: 'long_text' },
192
+ { field: 'summary', label: 'summary', valueType: 'long_text' },
193
+ { field: 'tags', label: 'tags', valueType: 'tags' },
194
+ ]
195
+
196
+ const ENQUIRY_STANDARD_FIELDS: readonly FormMapStandardField[] = [
197
+ { field: 'name', label: 'name', valueType: 'text' },
198
+ { field: 'source', label: 'source', valueType: 'text' },
199
+ { field: 'summary', label: 'summary', valueType: 'long_text' },
200
+ { field: 'tags', label: 'tags', valueType: 'tags' },
201
+ ]
202
+
203
+ const RAISE_STANDARD_FIELDS: readonly FormMapStandardField[] = [
204
+ { field: 'name', label: 'name', valueType: 'text' },
205
+ { field: 'check_size_cents', label: 'check size', valueType: 'number' },
206
+ { field: 'currency', label: 'currency', valueType: 'text' },
207
+ { field: 'thesis_fit', label: 'thesis fit', valueType: 'long_text' },
208
+ { field: 'pass_reason', label: 'pass reason', valueType: 'long_text' },
209
+ { field: 'expected_close', label: 'expected close', valueType: 'date' },
210
+ { field: 'summary', label: 'summary', valueType: 'long_text' },
211
+ { field: 'tags', label: 'tags', valueType: 'tags' },
212
+ ]
213
+
214
+ /** Standard writable fields per object type, keyed by object. */
215
+ export const FORM_STANDARD_MAP_FIELDS: Readonly<
216
+ Record<FormMapObjectType, readonly FormMapStandardField[]>
217
+ > = {
218
+ person: PERSON_STANDARD_FIELDS,
219
+ company: COMPANY_STANDARD_FIELDS,
220
+ position: POSITION_STANDARD_FIELDS,
221
+ deal: DEAL_STANDARD_FIELDS,
222
+ opportunity: OPPORTUNITY_STANDARD_FIELDS,
223
+ partnership: PARTNERSHIP_STANDARD_FIELDS,
224
+ enquiry: ENQUIRY_STANDARD_FIELDS,
225
+ raise: RAISE_STANDARD_FIELDS,
226
+ }
227
+
228
+ function standardFieldTarget(objectType: FormMapObjectType, field: FormMapStandardField): string {
229
+ return `${objectType}.${field.field}`
230
+ }
231
+
232
+ function standardFieldLabel(objectType: FormMapObjectType, field: FormMapStandardField): string {
233
+ return `${FORM_MAP_OBJECT_TYPE_LABELS[objectType]} · ${field.label}`
234
+ }
235
+
236
+ /** Every built-in map target entry, excluding workspace custom fields. */
237
+ export function listStandardMapTargetEntries(): readonly FormMapTargetEntry[] {
238
+ const entries: FormMapTargetEntry[] = [
239
+ {
240
+ target: 'submission',
241
+ objectType: 'submission',
242
+ label: 'Submission only',
243
+ fieldKind: 'special',
244
+ valueType: 'text',
245
+ },
246
+ {
247
+ target: PERSON_CONSENT_TARGET,
248
+ objectType: 'consent',
249
+ label: 'Person · consent',
250
+ fieldKind: 'special',
251
+ valueType: 'consent',
252
+ },
253
+ ]
254
+
255
+ for (const objectType of FORM_MAP_OBJECT_TYPES) {
256
+ for (const field of FORM_STANDARD_MAP_FIELDS[objectType]) {
257
+ const entry: FormMapTargetEntry = {
258
+ target: standardFieldTarget(objectType, field),
259
+ objectType,
260
+ label: standardFieldLabel(objectType, field),
261
+ fieldKind: 'standard',
262
+ valueType: field.valueType,
263
+ ...(field.enumValues === undefined ? {} : { enumValues: field.enumValues }),
264
+ }
265
+ entries.push(entry)
266
+ }
267
+ }
268
+
269
+ return entries
270
+ }
271
+
272
+ /** The wire target string for a workspace custom field definition. */
273
+ export function buildCustomMapTarget(objectType: CustomFieldObjectType, key: string): string {
274
+ return `${objectType}.custom_fields.${key}`
275
+ }
276
+
277
+ /** A catalog entry for one workspace custom field definition. */
278
+ export function buildCustomMapTargetEntry(definition: CustomFieldDefinitionRef): FormMapTargetEntry {
279
+ return {
280
+ target: buildCustomMapTarget(definition.objectType, definition.key),
281
+ objectType: definition.objectType,
282
+ label: `${CUSTOM_FIELD_OBJECT_TYPE_LABELS[definition.objectType]} · ${definition.label} (custom)`,
283
+ fieldKind: 'custom',
284
+ valueType: customFieldTypeToMapValueType(definition.type),
285
+ }
286
+ }
287
+
288
+ function customFieldTypeToMapValueType(type: CustomFieldType): FormMapValueType {
289
+ switch (type) {
290
+ case 'text':
291
+ return 'text'
292
+ case 'long_text':
293
+ return 'long_text'
294
+ case 'number':
295
+ return 'number'
296
+ case 'currency':
297
+ return 'currency'
298
+ case 'date':
299
+ return 'date'
300
+ case 'checkbox':
301
+ return 'boolean'
302
+ case 'select':
303
+ return 'enum'
304
+ case 'multi_select':
305
+ return 'string_array'
306
+ case 'url':
307
+ return 'url'
308
+ }
309
+ }
310
+
311
+ export interface ParsedFormMapTarget {
312
+ readonly objectType: FormMapObjectType | 'submission' | 'consent'
313
+ readonly fieldPath: string
314
+ readonly isCustomField: boolean
315
+ readonly customFieldKey?: string | undefined
316
+ }
317
+
318
+ /** Parses a map target string. Returns undefined when the shape is not recognised. */
319
+ export function parseFormMapTarget(target: string): ParsedFormMapTarget | undefined {
320
+ if (target === 'submission') {
321
+ return { objectType: 'submission', fieldPath: 'submission', isCustomField: false }
322
+ }
323
+
324
+ if (target === PERSON_CONSENT_TARGET) {
325
+ return { objectType: 'consent', fieldPath: 'consent', isCustomField: false }
326
+ }
327
+
328
+ const customMatch = /^([a-z_]+)\.custom_fields\.([a-z][a-z0-9_]*)$/.exec(target)
329
+
330
+ if (customMatch !== null) {
331
+ const objectType = customMatch[1] as FormMapObjectType
332
+ const key = customMatch[2]
333
+
334
+ if (!FORM_MAP_OBJECT_TYPES.includes(objectType)) {
335
+ return undefined
336
+ }
337
+
338
+ return {
339
+ objectType,
340
+ fieldPath: `custom_fields.${key}`,
341
+ isCustomField: true,
342
+ customFieldKey: key,
343
+ }
344
+ }
345
+
346
+ const standardMatch = /^([a-z_]+)\.([a-z_]+)$/.exec(target)
347
+
348
+ if (standardMatch === null) {
349
+ return undefined
350
+ }
351
+
352
+ const objectType = standardMatch[1] as FormMapObjectType
353
+ const field = standardMatch[2]
354
+
355
+ if (field === undefined || !FORM_MAP_OBJECT_TYPES.includes(objectType)) {
356
+ return undefined
357
+ }
358
+
359
+ const known = FORM_STANDARD_MAP_FIELDS[objectType].some((entry) => entry.field === field)
360
+
361
+ if (!known) {
362
+ return undefined
363
+ }
364
+
365
+ return { objectType, fieldPath: field, isCustomField: false }
366
+ }
367
+
368
+ /** True when more than one form field may share this target. */
369
+ export function isRepeatableMapTarget(target: string): boolean {
370
+ return target === 'submission' || target === PERSON_CONSENT_TARGET
371
+ }
372
+
373
+ function findStandardEntry(target: string): FormMapTargetEntry | undefined {
374
+ return listStandardMapTargetEntries().find((entry) => entry.target === target)
375
+ }
376
+
377
+ function findCustomEntry(
378
+ target: string,
379
+ definitions: readonly CustomFieldDefinitionRef[],
380
+ ): FormMapTargetEntry | undefined {
381
+ const parsed = parseFormMapTarget(target)
382
+
383
+ if (parsed === undefined || !parsed.isCustomField || parsed.customFieldKey === undefined) {
384
+ return undefined
385
+ }
386
+
387
+ const definition = definitions.find(
388
+ (row) => row.objectType === parsed.objectType && row.key === parsed.customFieldKey,
389
+ )
390
+
391
+ return definition === undefined ? undefined : buildCustomMapTargetEntry(definition)
392
+ }
393
+
394
+ /** Resolves a target to its catalog entry, checking custom definitions when needed. */
395
+ export function resolveMapTargetEntry(
396
+ target: string,
397
+ customDefinitions: readonly CustomFieldDefinitionRef[] = [],
398
+ ): FormMapTargetEntry | undefined {
399
+ const standard = findStandardEntry(target)
400
+
401
+ if (standard !== undefined) {
402
+ return standard
403
+ }
404
+
405
+ return findCustomEntry(target, customDefinitions)
406
+ }
407
+
408
+ /** Display label for a map target. Falls back to the raw target string. */
409
+ export function labelForMapTarget(
410
+ target: string,
411
+ customDefinitions: readonly CustomFieldDefinitionRef[] = [],
412
+ ): string {
413
+ const legacy =
414
+ FORM_FIELD_MAP_TARGET_LABELS[target as keyof typeof FORM_FIELD_MAP_TARGET_LABELS]
415
+
416
+ if (legacy !== undefined) {
417
+ return legacy
418
+ }
419
+
420
+ return resolveMapTargetEntry(target, customDefinitions)?.label ?? target
421
+ }
422
+
423
+ /** Merges the static catalog with workspace custom field definitions. */
424
+ export function listMapTargetEntries(
425
+ customDefinitions: readonly CustomFieldDefinitionRef[] = [],
426
+ ): readonly FormMapTargetEntry[] {
427
+ return [
428
+ ...listStandardMapTargetEntries(),
429
+ ...customDefinitions.map((definition) => buildCustomMapTargetEntry(definition)),
430
+ ]
431
+ }
432
+
433
+ /** True when the target is known in the catalog (standard or custom). */
434
+ export function isKnownMapTarget(
435
+ target: string,
436
+ customDefinitions: readonly CustomFieldDefinitionRef[] = [],
437
+ ): boolean {
438
+ return resolveMapTargetEntry(target, customDefinitions) !== undefined
439
+ }
440
+
441
+ /** Suggested form field control type for a map target. */
442
+ export function suggestedFormFieldType(
443
+ target: string,
444
+ customDefinitions: readonly CustomFieldDefinitionRef[] = [],
445
+ current: FormFieldType = 'text',
446
+ ): FormFieldType {
447
+ if (target === PERSON_EMAIL_TARGET) {
448
+ return 'email'
449
+ }
450
+
451
+ if (target === PERSON_CONSENT_TARGET) {
452
+ return current === 'notice' ? 'notice' : 'consent'
453
+ }
454
+
455
+ const entry = resolveMapTargetEntry(target, customDefinitions)
456
+
457
+ if (entry === undefined) {
458
+ return current === 'consent' || current === 'notice' ? 'text' : current
459
+ }
460
+
461
+ switch (entry.valueType) {
462
+ case 'email':
463
+ return 'email'
464
+ case 'long_text':
465
+ case 'consent':
466
+ return entry.valueType === 'consent'
467
+ ? current === 'notice'
468
+ ? 'notice'
469
+ : 'consent'
470
+ : 'textarea'
471
+ case 'enum':
472
+ return 'select'
473
+ case 'boolean':
474
+ return 'select'
475
+ default:
476
+ return current === 'consent' || current === 'notice' ? 'text' : current
477
+ }
478
+ }
479
+
480
+ /** True when a form field type can reasonably collect an answer for this target. */
481
+ export function isCompatibleFormFieldType(
482
+ formType: FormFieldType,
483
+ target: string,
484
+ customDefinitions: readonly CustomFieldDefinitionRef[] = [],
485
+ ): boolean {
486
+ if (target === PERSON_CONSENT_TARGET) {
487
+ return formType === 'consent' || formType === 'notice'
488
+ }
489
+
490
+ if (formType === 'consent' || formType === 'notice') {
491
+ return target === PERSON_CONSENT_TARGET
492
+ }
493
+
494
+ const entry = resolveMapTargetEntry(target, customDefinitions)
495
+
496
+ if (entry === undefined) {
497
+ return false
498
+ }
499
+
500
+ if (entry.objectType === 'submission') {
501
+ return true
502
+ }
503
+
504
+ switch (entry.valueType) {
505
+ case 'email':
506
+ return formType === 'email' || formType === 'text'
507
+ case 'boolean':
508
+ return formType === 'select' || formType === 'text'
509
+ case 'enum':
510
+ return formType === 'select' || formType === 'text'
511
+ case 'number':
512
+ case 'currency':
513
+ case 'date':
514
+ return formType === 'text' || formType === 'select'
515
+ case 'tags':
516
+ case 'string_array':
517
+ case 'phones':
518
+ return formType === 'text' || formType === 'textarea' || formType === 'select'
519
+ case 'url':
520
+ return formType === 'text' || formType === 'email'
521
+ case 'long_text':
522
+ return formType === 'textarea' || formType === 'text'
523
+ case 'consent':
524
+ return false
525
+ default:
526
+ return formType === 'text' || formType === 'textarea' || formType === 'email' || formType === 'select'
527
+ }
528
+ }
529
+
530
+ /** Meta line for the map-target search picker. */
531
+ export function metaForMapTargetEntry(
532
+ entry: FormMapTargetEntry,
533
+ customType?: CustomFieldType,
534
+ ): string | undefined {
535
+ if (entry.fieldKind === 'custom') {
536
+ return customType === undefined
537
+ ? 'custom'
538
+ : `custom · ${CUSTOM_FIELD_TYPE_LABELS[customType]}`
539
+ }
540
+
541
+ if (entry.fieldKind === 'special') {
542
+ return undefined
543
+ }
544
+
545
+ return entry.valueType
546
+ }
547
+
548
+ export { PERSON_CONSENT_TARGET, PERSON_EMAIL_TARGET }