@open-mercato/core 0.10.1-develop.7768.1.90fa052c11 → 0.10.1-develop.7770.1.970b396ebb
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/modules/customer_accounts/search.js +5 -9
- package/dist/modules/customer_accounts/search.js.map +2 -2
- package/dist/modules/customers/search.js +71 -50
- package/dist/modules/customers/search.js.map +2 -2
- package/dist/modules/notifications/lib/notificationService.js +19 -1
- package/dist/modules/notifications/lib/notificationService.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/customer_accounts/search.ts +4 -10
- package/src/modules/customers/search.ts +82 -52
- package/src/modules/notifications/lib/notificationService.ts +24 -2
|
@@ -25,12 +25,12 @@ function formatSubtitle(...parts) {
|
|
|
25
25
|
if (text.length === 0) return void 0;
|
|
26
26
|
return text.join(" \xB7 ");
|
|
27
27
|
}
|
|
28
|
-
function buildIndexSource(
|
|
28
|
+
function buildIndexSource(presenter, lines) {
|
|
29
29
|
if (!lines.length) return null;
|
|
30
30
|
return {
|
|
31
31
|
text: lines,
|
|
32
32
|
presenter,
|
|
33
|
-
checksumSource: {
|
|
33
|
+
checksumSource: { text: lines, presenter }
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
const searchConfig = {
|
|
@@ -44,13 +44,10 @@ const searchConfig = {
|
|
|
44
44
|
const { t } = await resolveTranslations();
|
|
45
45
|
const record = ctx.record;
|
|
46
46
|
const lines = [];
|
|
47
|
-
appendLine(lines, "
|
|
48
|
-
appendLine(lines, "Email", record.email);
|
|
47
|
+
appendLine(lines, "Customer user ID", record.id ?? "customer_accounts:customer_user");
|
|
49
48
|
return buildIndexSource(
|
|
50
|
-
ctx,
|
|
51
49
|
{
|
|
52
50
|
title: pickString(record.display_name, record.displayName) ?? String(record.id),
|
|
53
|
-
subtitle: formatSubtitle(record.email),
|
|
54
51
|
icon: "user",
|
|
55
52
|
badge: t("customer_accounts.search.badge.customerUser", "Customer User")
|
|
56
53
|
},
|
|
@@ -62,14 +59,14 @@ const searchConfig = {
|
|
|
62
59
|
const record = ctx.record;
|
|
63
60
|
return {
|
|
64
61
|
title: pickString(record.display_name, record.displayName) ?? String(record.id),
|
|
65
|
-
subtitle: formatSubtitle(record.email),
|
|
66
62
|
icon: "user",
|
|
67
63
|
badge: t("customer_accounts.search.badge.customerUser", "Customer User")
|
|
68
64
|
};
|
|
69
65
|
},
|
|
70
66
|
resolveUrl: async (ctx) => `/backend/customer-accounts/users/${encodeURIComponent(String(ctx.record.id))}`,
|
|
71
67
|
fieldPolicy: {
|
|
72
|
-
searchable: ["display_name"
|
|
68
|
+
searchable: ["display_name"],
|
|
69
|
+
hashOnly: ["email"],
|
|
73
70
|
excluded: ["password_hash", "email_hash"]
|
|
74
71
|
}
|
|
75
72
|
},
|
|
@@ -85,7 +82,6 @@ const searchConfig = {
|
|
|
85
82
|
appendLine(lines, "Name", record.name);
|
|
86
83
|
appendLine(lines, "Description", record.description);
|
|
87
84
|
return buildIndexSource(
|
|
88
|
-
ctx,
|
|
89
85
|
{
|
|
90
86
|
title: pickString(record.name) ?? String(record.id),
|
|
91
87
|
subtitle: formatSubtitle(snippet(record.description)),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/customer_accounts/search.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n SearchModuleConfig,\n
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type {\n SearchModuleConfig,\n SearchResultPresenter,\n SearchIndexSource,\n} from '@open-mercato/shared/modules/search'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\n\nfunction pickString(...candidates: Array<unknown>): string | null {\n for (const candidate of candidates) {\n if (typeof candidate !== 'string') continue\n const trimmed = candidate.trim()\n if (trimmed.length > 0) return trimmed\n }\n return null\n}\n\nfunction snippet(value: unknown, max = 140): string | undefined {\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n if (!trimmed.length) return undefined\n if (trimmed.length <= max) return trimmed\n return `${trimmed.slice(0, max - 3)}...`\n}\n\nfunction appendLine(lines: string[], label: string, value: unknown) {\n if (value === null || value === undefined) return\n const text = typeof value === 'object' ? JSON.stringify(value) : String(value)\n if (!text.trim()) return\n lines.push(`${label}: ${text}`)\n}\n\nfunction formatSubtitle(...parts: Array<unknown>): string | undefined {\n const text = parts\n .map((part) => (part === null || part === undefined ? '' : String(part)))\n .map((part) => part.trim())\n .filter(Boolean)\n if (text.length === 0) return undefined\n return text.join(' \u00B7 ')\n}\n\nfunction buildIndexSource(\n presenter: SearchResultPresenter,\n lines: string[],\n): SearchIndexSource | null {\n if (!lines.length) return null\n return {\n text: lines,\n presenter,\n checksumSource: { text: lines, presenter },\n }\n}\n\nexport const searchConfig: SearchModuleConfig = {\n entities: [\n {\n entityId: 'customer_accounts:customer_user',\n aclFeatures: ['customer_accounts.view'],\n enabled: true,\n priority: 6,\n buildSource: async (ctx) => {\n const { t } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Customer user ID', record.id ?? 'customer_accounts:customer_user')\n return buildIndexSource(\n {\n title: pickString(record.display_name, record.displayName) ?? String(record.id),\n icon: 'user',\n badge: t('customer_accounts.search.badge.customerUser', 'Customer User'),\n },\n lines,\n )\n },\n formatResult: async (ctx) => {\n const { t } = await resolveTranslations()\n const record = ctx.record\n return {\n title: pickString(record.display_name, record.displayName) ?? String(record.id),\n icon: 'user',\n badge: t('customer_accounts.search.badge.customerUser', 'Customer User'),\n }\n },\n resolveUrl: async (ctx) => `/backend/customer-accounts/users/${encodeURIComponent(String(ctx.record.id))}`,\n fieldPolicy: {\n searchable: ['display_name'],\n hashOnly: ['email'],\n excluded: ['password_hash', 'email_hash'],\n },\n },\n {\n entityId: 'customer_accounts:customer_role',\n aclFeatures: ['customer_accounts.view'],\n enabled: true,\n priority: 6,\n buildSource: async (ctx) => {\n const { t } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Name', record.name)\n appendLine(lines, 'Description', record.description)\n return buildIndexSource(\n {\n title: pickString(record.name) ?? String(record.id),\n subtitle: formatSubtitle(snippet(record.description)),\n icon: 'shield',\n badge: t('customer_accounts.search.badge.customerRole', 'Customer Role'),\n },\n lines,\n )\n },\n formatResult: async (ctx) => {\n const { t } = await resolveTranslations()\n const record = ctx.record\n return {\n title: pickString(record.name) ?? String(record.id),\n subtitle: formatSubtitle(snippet(record.description)),\n icon: 'shield',\n badge: t('customer_accounts.search.badge.customerRole', 'Customer Role'),\n }\n },\n resolveUrl: async (ctx) => `/backend/customer-accounts/roles/${encodeURIComponent(String(ctx.record.id))}/edit`,\n fieldPolicy: {\n searchable: ['name', 'description'],\n },\n },\n ],\n}\n\nexport default searchConfig\nexport const config = searchConfig\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
2
2
|
import { CUSTOMER_INTERACTION_TASK_SOURCE, EXAMPLE_TODO_SOURCE } from "./lib/interactionCompatibility.js";
|
|
3
3
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
4
|
+
import { defaultEncryptionMaps } from "./encryption.js";
|
|
5
|
+
import { isTenantDataEncryptionEnabled } from "@open-mercato/shared/lib/encryption/toggles";
|
|
4
6
|
const logger = createLogger("customers");
|
|
5
7
|
function assertTenantContext(ctx) {
|
|
6
8
|
if (typeof ctx.tenantId !== "string" || ctx.tenantId.length === 0) {
|
|
@@ -310,6 +312,52 @@ function appendCustomFieldLines(lines, customFields, prefix) {
|
|
|
310
312
|
appendLine(lines, label, value);
|
|
311
313
|
}
|
|
312
314
|
}
|
|
315
|
+
function normalizeSearchFieldKey(key) {
|
|
316
|
+
return key.replace(/^cf[:_]/, "").replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
317
|
+
}
|
|
318
|
+
async function resolveProfileEncryptedFields(ctx, kind, entity) {
|
|
319
|
+
if (!isTenantDataEncryptionEnabled()) {
|
|
320
|
+
throw new Error("[internal] Customer vector indexing requires available encryption declarations");
|
|
321
|
+
}
|
|
322
|
+
const container = ctx.container;
|
|
323
|
+
if (typeof container?.resolve !== "function") {
|
|
324
|
+
throw new Error("[internal] Customer search encryption policy container is unavailable");
|
|
325
|
+
}
|
|
326
|
+
const service = container.resolve("tenantEncryptionService");
|
|
327
|
+
if (typeof service?.getEncryptedFieldNames !== "function") {
|
|
328
|
+
throw new Error("[internal] Customer search encryption policy service is unavailable");
|
|
329
|
+
}
|
|
330
|
+
const organizationIds = /* @__PURE__ */ new Set([ctx.organizationId]);
|
|
331
|
+
for (const record of [ctx.record, entity]) {
|
|
332
|
+
const organizationId = record?.organization_id ?? record?.organizationId;
|
|
333
|
+
if (typeof organizationId === "string" && organizationId) organizationIds.add(organizationId);
|
|
334
|
+
}
|
|
335
|
+
const fields = await Promise.all(Array.from(organizationIds).flatMap((organizationId) => [
|
|
336
|
+
`customers:customer_${kind}_profile`,
|
|
337
|
+
"customers:customer_entity"
|
|
338
|
+
].map((entityId) => service.getEncryptedFieldNames(entityId, ctx.tenantId, organizationId, { ignoreRuntimeHealth: true }))));
|
|
339
|
+
return fields.flat();
|
|
340
|
+
}
|
|
341
|
+
function selectProfileSearchFields(source, kind, options = {}) {
|
|
342
|
+
const entityId = `customers:customer_${kind}_profile`;
|
|
343
|
+
const policy = searchConfig.entities.find((entry) => entry.entityId === entityId)?.fieldPolicy;
|
|
344
|
+
const allowed = new Set((options.customerEntity ? ["display_name", "description", "status", "lifecycle_stage"] : policy?.searchable ?? []).map(normalizeSearchFieldKey));
|
|
345
|
+
const excluded = new Set([...policy?.hashOnly ?? [], ...policy?.excluded ?? []].map(normalizeSearchFieldKey));
|
|
346
|
+
if (options.forEmbedding) {
|
|
347
|
+
for (const field of options.encryptedFields ?? []) excluded.add(normalizeSearchFieldKey(field));
|
|
348
|
+
for (const map of defaultEncryptionMaps) {
|
|
349
|
+
if (map.entityId !== entityId && map.entityId !== "customers:customer_entity") continue;
|
|
350
|
+
for (const entry of map.fields) excluded.add(normalizeSearchFieldKey(entry.field));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
const fields = {};
|
|
354
|
+
for (const [key, value] of Object.entries(source ?? {})) {
|
|
355
|
+
if (value !== null && typeof value === "object") continue;
|
|
356
|
+
const field = normalizeSearchFieldKey(key);
|
|
357
|
+
if (allowed.has(field) && !excluded.has(field)) fields[field] = value;
|
|
358
|
+
}
|
|
359
|
+
return fields;
|
|
360
|
+
}
|
|
313
361
|
function pickValue(source, ...keys) {
|
|
314
362
|
if (!source) return void 0;
|
|
315
363
|
for (const key of keys) {
|
|
@@ -334,11 +382,8 @@ function pickLabel(...candidates) {
|
|
|
334
382
|
}
|
|
335
383
|
return null;
|
|
336
384
|
}
|
|
337
|
-
function appendCustomerEntityLines(lines, entity
|
|
385
|
+
function appendCustomerEntityLines(lines, entity) {
|
|
338
386
|
if (!entity) return;
|
|
339
|
-
appendLine(lines, "Customer", pickValue(entity, "display_name", "displayName") ?? entity.id);
|
|
340
|
-
appendLine(lines, `${contactLabel} email`, pickValue(entity, "primary_email", "primaryEmail"));
|
|
341
|
-
appendLine(lines, `${contactLabel} phone`, pickValue(entity, "primary_phone", "primaryPhone"));
|
|
342
387
|
appendLine(lines, "Lifecycle stage", pickValue(entity, "lifecycle_stage", "lifecycleStage"));
|
|
343
388
|
appendLine(lines, "Status", pickValue(entity, "status"));
|
|
344
389
|
}
|
|
@@ -360,6 +405,9 @@ function ensureFallbackLines(lines, record, options = {}) {
|
|
|
360
405
|
}
|
|
361
406
|
function resolvePersonPresenter(translate, record, entity, customFields) {
|
|
362
407
|
const fallbackEntityId = resolveCustomerEntityId(record);
|
|
408
|
+
record = { id: record.id, ...selectProfileSearchFields(record, "person") };
|
|
409
|
+
entity = entity ? { id: entity.id, ...selectProfileSearchFields(entity, "person", { customerEntity: true }) } : null;
|
|
410
|
+
customFields = selectProfileSearchFields(customFields, "person");
|
|
363
411
|
const firstName = record.first_name ?? record.firstName ?? customFields.first_name ?? customFields.firstName ?? "";
|
|
364
412
|
const lastName = record.last_name ?? record.lastName ?? customFields.last_name ?? customFields.lastName ?? "";
|
|
365
413
|
const nameParts = [firstName, lastName].filter(Boolean).join(" ");
|
|
@@ -369,10 +417,6 @@ function resolvePersonPresenter(translate, record, entity, customFields) {
|
|
|
369
417
|
if (jobTitle) subtitlePieces.push(String(jobTitle));
|
|
370
418
|
const department = record.department ?? customFields.department;
|
|
371
419
|
if (department) subtitlePieces.push(String(department));
|
|
372
|
-
const primaryEmail = pickValue(entity, "primary_email", "primaryEmail");
|
|
373
|
-
if (primaryEmail) subtitlePieces.push(String(primaryEmail));
|
|
374
|
-
const primaryPhone = pickValue(entity, "primary_phone", "primaryPhone");
|
|
375
|
-
if (primaryPhone) subtitlePieces.push(String(primaryPhone));
|
|
376
420
|
const summary = snippet(
|
|
377
421
|
pickValue(entity, "description") ?? customFields.summary ?? customFields.description
|
|
378
422
|
);
|
|
@@ -386,16 +430,15 @@ function resolvePersonPresenter(translate, record, entity, customFields) {
|
|
|
386
430
|
}
|
|
387
431
|
function resolveCompanyPresenter(translate, record, entity, customFields) {
|
|
388
432
|
const fallbackEntityId = resolveCustomerEntityId(record);
|
|
433
|
+
record = { id: record.id, ...selectProfileSearchFields(record, "company") };
|
|
434
|
+
entity = entity ? { id: entity.id, ...selectProfileSearchFields(entity, "company", { customerEntity: true }) } : null;
|
|
435
|
+
customFields = selectProfileSearchFields(customFields, "company");
|
|
389
436
|
const title = pickValue(entity, "display_name", "displayName") ?? customFields.display_name ?? customFields.displayName ?? record.brand_name ?? record.legal_name ?? record.domain ?? record.brandName ?? record.legalName ?? (entity?.id && entity?.display_name ? entity.display_name : void 0) ?? fallbackEntityId ?? record.id ?? translate("customers.search.fallback.company", "Company");
|
|
390
437
|
const subtitlePieces = [];
|
|
391
438
|
const industry = record.industry;
|
|
392
439
|
if (industry) subtitlePieces.push(String(industry));
|
|
393
440
|
const sizeBucket = record.size_bucket ?? record.sizeBucket;
|
|
394
441
|
if (sizeBucket) subtitlePieces.push(String(sizeBucket));
|
|
395
|
-
if (entity) {
|
|
396
|
-
const primaryEmail = pickValue(entity, "primary_email", "primaryEmail");
|
|
397
|
-
if (primaryEmail) subtitlePieces.push(String(primaryEmail));
|
|
398
|
-
}
|
|
399
442
|
const summary = snippet(
|
|
400
443
|
pickValue(entity, "description") ?? customFields.summary ?? customFields.description ?? record.summary ?? record.description
|
|
401
444
|
);
|
|
@@ -441,19 +484,12 @@ const searchConfig = {
|
|
|
441
484
|
const { t } = await resolveTranslations();
|
|
442
485
|
const lines = [];
|
|
443
486
|
const record = ctx.record;
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
appendLine(lines, "Department", record.department ?? record.department_name ?? record.departmentName ?? ctx.customFields.department);
|
|
449
|
-
appendLine(lines, "Seniority", record.seniority ?? record.seniority_level ?? record.seniorityLevel ?? ctx.customFields.seniority);
|
|
450
|
-
appendLine(lines, "Timezone", record.timezone ?? record.time_zone ?? record.timeZone ?? ctx.customFields.timezone);
|
|
451
|
-
appendLine(lines, "LinkedIn", record.linked_in_url ?? record.linkedInUrl ?? ctx.customFields.linked_in_url);
|
|
452
|
-
appendLine(lines, "Twitter", record.twitter_url ?? record.twitterUrl ?? ctx.customFields.twitter_url);
|
|
453
|
-
const { entity, entityId, profileCustomFields, entityCustomFields, entityOnlyCustomFields } = await hydrateProfileContext(ctx, "person");
|
|
454
|
-
appendCustomFieldLines(lines, profileCustomFields, "Person custom");
|
|
487
|
+
const { entity, entityId, profileCustomFields, entityOnlyCustomFields } = await hydrateProfileContext(ctx, "person");
|
|
488
|
+
const embeddingOptions = { forEmbedding: true, encryptedFields: await resolveProfileEncryptedFields(ctx, "person", entity) };
|
|
489
|
+
appendCustomFieldLines(lines, selectProfileSearchFields(record, "person", embeddingOptions), "");
|
|
490
|
+
appendCustomFieldLines(lines, selectProfileSearchFields(profileCustomFields, "person", embeddingOptions), "Person custom");
|
|
455
491
|
if (Object.keys(entityOnlyCustomFields).length) {
|
|
456
|
-
appendCustomFieldLines(lines, entityOnlyCustomFields, "Customer custom");
|
|
492
|
+
appendCustomFieldLines(lines, selectProfileSearchFields(entityOnlyCustomFields, "person", embeddingOptions), "Customer custom");
|
|
457
493
|
}
|
|
458
494
|
if (!entity) {
|
|
459
495
|
logger.warn("Failed to load customer entity for person profile", {
|
|
@@ -463,8 +499,8 @@ const searchConfig = {
|
|
|
463
499
|
recordKeys: Object.keys(record)
|
|
464
500
|
});
|
|
465
501
|
}
|
|
466
|
-
appendCustomerEntityLines(lines, entity, "
|
|
467
|
-
ensureFallbackLines(lines, record);
|
|
502
|
+
appendCustomerEntityLines(lines, selectProfileSearchFields(entity, "person", { ...embeddingOptions, customerEntity: true }));
|
|
503
|
+
ensureFallbackLines(lines, { id: record.id ?? entityId ?? "customers:customer_person_profile" });
|
|
468
504
|
if (!lines.length) return null;
|
|
469
505
|
if (!entityId) {
|
|
470
506
|
logger.warn("Person profile missing entity id", {
|
|
@@ -487,12 +523,7 @@ const searchConfig = {
|
|
|
487
523
|
text: lines,
|
|
488
524
|
presenter,
|
|
489
525
|
links,
|
|
490
|
-
checksumSource: {
|
|
491
|
-
record: ctx.record,
|
|
492
|
-
customFields: profileCustomFields,
|
|
493
|
-
entity,
|
|
494
|
-
entityCustomFields
|
|
495
|
-
}
|
|
526
|
+
checksumSource: { text: lines, presenter, links }
|
|
496
527
|
};
|
|
497
528
|
},
|
|
498
529
|
formatResult: async (ctx) => {
|
|
@@ -542,20 +573,15 @@ const searchConfig = {
|
|
|
542
573
|
const { t } = await resolveTranslations();
|
|
543
574
|
const lines = [];
|
|
544
575
|
const record = ctx.record;
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
appendLine(lines, "Industry", record.industry ?? ctx.customFields.industry);
|
|
550
|
-
appendLine(lines, "Company size", record.size_bucket ?? record.sizeBucket ?? ctx.customFields.size_bucket);
|
|
551
|
-
appendLine(lines, "Annual revenue", record.annual_revenue ?? record.annualRevenue ?? ctx.customFields.annual_revenue);
|
|
552
|
-
const { entity, entityId, profileCustomFields, entityCustomFields, entityOnlyCustomFields } = await hydrateProfileContext(ctx, "company");
|
|
553
|
-
appendCustomFieldLines(lines, profileCustomFields, "Company custom");
|
|
576
|
+
const { entity, entityId, profileCustomFields, entityOnlyCustomFields } = await hydrateProfileContext(ctx, "company");
|
|
577
|
+
const embeddingOptions = { forEmbedding: true, encryptedFields: await resolveProfileEncryptedFields(ctx, "company", entity) };
|
|
578
|
+
appendCustomFieldLines(lines, selectProfileSearchFields(record, "company", embeddingOptions), "");
|
|
579
|
+
appendCustomFieldLines(lines, selectProfileSearchFields(profileCustomFields, "company", embeddingOptions), "Company custom");
|
|
554
580
|
if (Object.keys(entityOnlyCustomFields).length) {
|
|
555
|
-
appendCustomFieldLines(lines, entityOnlyCustomFields, "Customer custom");
|
|
581
|
+
appendCustomFieldLines(lines, selectProfileSearchFields(entityOnlyCustomFields, "company", embeddingOptions), "Customer custom");
|
|
556
582
|
}
|
|
557
|
-
appendCustomerEntityLines(lines, entity, "
|
|
558
|
-
ensureFallbackLines(lines, record);
|
|
583
|
+
appendCustomerEntityLines(lines, selectProfileSearchFields(entity, "company", { ...embeddingOptions, customerEntity: true }));
|
|
584
|
+
ensureFallbackLines(lines, { id: record.id ?? entityId ?? "customers:customer_company_profile" });
|
|
559
585
|
if (!lines.length) return null;
|
|
560
586
|
const presenter = resolveCompanyPresenter(t, record, entity, ctx.customFields);
|
|
561
587
|
logMissingPresenterTitle("company", record, entity, presenter);
|
|
@@ -571,12 +597,7 @@ const searchConfig = {
|
|
|
571
597
|
text: lines,
|
|
572
598
|
presenter,
|
|
573
599
|
links,
|
|
574
|
-
checksumSource: {
|
|
575
|
-
record: ctx.record,
|
|
576
|
-
customFields: profileCustomFields,
|
|
577
|
-
entity,
|
|
578
|
-
entityCustomFields
|
|
579
|
-
}
|
|
600
|
+
checksumSource: { text: lines, presenter, links }
|
|
580
601
|
};
|
|
581
602
|
},
|
|
582
603
|
formatResult: async (ctx) => {
|