@open-mercato/shared 0.6.4-develop.4217.1.c9aa050183 → 0.6.4-develop.4236.1.9fa6806b34

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.
@@ -1,2 +1,2 @@
1
- [build:shared] found 214 entry points
1
+ [build:shared] found 215 entry points
2
2
  [build:shared] built successfully
@@ -0,0 +1,146 @@
1
+ function normalizeDefinitionKey(key) {
2
+ if (typeof key !== "string") return "";
3
+ const trimmed = key.trim();
4
+ return trimmed.length ? trimmed.toLowerCase() : "";
5
+ }
6
+ function normalizeDefinitionConfig(raw) {
7
+ if (!raw) return {};
8
+ if (typeof raw === "string") {
9
+ try {
10
+ const parsed = JSON.parse(raw);
11
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
12
+ return { ...parsed };
13
+ }
14
+ return {};
15
+ } catch {
16
+ return {};
17
+ }
18
+ }
19
+ if (typeof raw === "object" && !Array.isArray(raw)) {
20
+ return { ...raw };
21
+ }
22
+ return {};
23
+ }
24
+ function normalizeFieldsetFilter(input) {
25
+ if (input == null) return null;
26
+ const values = Array.isArray(input) ? input : [input];
27
+ const normalized = /* @__PURE__ */ new Set();
28
+ for (const raw of values) {
29
+ if (raw == null) continue;
30
+ const trimmed = String(raw).trim();
31
+ if (!trimmed) {
32
+ normalized.add(null);
33
+ } else {
34
+ normalized.add(trimmed);
35
+ }
36
+ }
37
+ return normalized.size ? normalized : null;
38
+ }
39
+ function toTimeMs(value) {
40
+ if (value == null) return 0;
41
+ if (value instanceof Date) return value.getTime();
42
+ const parsed = new Date(value).getTime();
43
+ return Number.isNaN(parsed) ? 0 : parsed;
44
+ }
45
+ function summarizeDefinitionRow(row) {
46
+ const normalizedKey = normalizeDefinitionKey(row.key);
47
+ if (!normalizedKey) return null;
48
+ const cfg = normalizeDefinitionConfig(row.configJson);
49
+ const label = typeof cfg.label === "string" && cfg.label.trim().length ? cfg.label.trim() : row.key;
50
+ const dictionaryId = typeof cfg.dictionaryId === "string" && cfg.dictionaryId.trim().length ? cfg.dictionaryId.trim() : null;
51
+ const multi = cfg.multi !== void 0 ? Boolean(cfg.multi) : false;
52
+ const priority = typeof cfg.priority === "number" ? cfg.priority : 0;
53
+ return {
54
+ key: row.key,
55
+ label,
56
+ kind: typeof row.kind === "string" ? row.kind : null,
57
+ multi,
58
+ dictionaryId,
59
+ organizationId: row.organizationId ?? null,
60
+ tenantId: row.tenantId ?? null,
61
+ priority,
62
+ updatedAt: toTimeMs(row.updatedAt)
63
+ };
64
+ }
65
+ function sortDefinitionSummaries(defs) {
66
+ return [...defs].sort((a, b) => {
67
+ const priorityDiff = (a.priority ?? 0) - (b.priority ?? 0);
68
+ if (priorityDiff !== 0) return priorityDiff;
69
+ const updatedDiff = (b.updatedAt ?? 0) - (a.updatedAt ?? 0);
70
+ if (updatedDiff !== 0) return updatedDiff;
71
+ return a.key.localeCompare(b.key);
72
+ });
73
+ }
74
+ function selectDefinitionForRecord(defs, organizationId, tenantId) {
75
+ if (!defs.length) return null;
76
+ const prioritizedForOrg = defs.filter(
77
+ (def) => def.organizationId && organizationId && def.organizationId === organizationId
78
+ );
79
+ if (prioritizedForOrg.length) return sortDefinitionSummaries(prioritizedForOrg)[0];
80
+ const prioritizedForTenant = defs.filter(
81
+ (def) => def.tenantId && tenantId && def.tenantId === tenantId && !def.organizationId
82
+ );
83
+ if (prioritizedForTenant.length) return sortDefinitionSummaries(prioritizedForTenant)[0];
84
+ const global = defs.filter((def) => !def.organizationId);
85
+ if (global.length) return sortDefinitionSummaries(global)[0];
86
+ return sortDefinitionSummaries(defs)[0] ?? null;
87
+ }
88
+ function resolveCfDefIndexOrgCandidates(organizationIds, fallbackOrganizationId) {
89
+ const source = Array.isArray(organizationIds) && organizationIds.length ? organizationIds : [fallbackOrganizationId ?? null];
90
+ return source.map((id) => typeof id === "string" ? id.trim() : id).filter((id) => typeof id === "string" && id.length > 0);
91
+ }
92
+ function matchesFieldset(configJson, fieldsetFilter) {
93
+ const config = normalizeDefinitionConfig(configJson);
94
+ const fieldsets = Array.isArray(config.fieldsets) ? config.fieldsets.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter((entry) => entry.length > 0) : [];
95
+ const fieldset = typeof config.fieldset === "string" && config.fieldset.trim().length > 0 ? config.fieldset.trim() : null;
96
+ return fieldsets.length > 0 ? fieldsets.some((entry) => fieldsetFilter.has(entry)) : fieldsetFilter.has(fieldset);
97
+ }
98
+ function buildCustomFieldDefinitionIndexFromRows(rows, opts = {}) {
99
+ const orgCandidates = opts.organizationIds ?? [];
100
+ const fieldsetFilter = normalizeFieldsetFilter(opts.fieldset);
101
+ const index = /* @__PURE__ */ new Map();
102
+ for (const row of rows) {
103
+ if (row.deletedAt != null) continue;
104
+ const org = row.organizationId ?? null;
105
+ if (org !== null && !(orgCandidates.length > 0 && orgCandidates.includes(org))) continue;
106
+ if (fieldsetFilter && !matchesFieldset(row.configJson, fieldsetFilter)) continue;
107
+ const summary = summarizeDefinitionRow(row);
108
+ if (!summary) continue;
109
+ const normalizedKey = normalizeDefinitionKey(summary.key);
110
+ if (!normalizedKey) continue;
111
+ if (!index.has(normalizedKey)) index.set(normalizedKey, []);
112
+ index.get(normalizedKey).push(summary);
113
+ }
114
+ index.forEach((entries, key) => {
115
+ index.set(key, sortDefinitionSummaries(entries));
116
+ });
117
+ return index;
118
+ }
119
+ function sameStringSet(a, b) {
120
+ const left = new Set(a);
121
+ const right = new Set(b);
122
+ if (left.size !== right.size) return false;
123
+ for (const value of left) {
124
+ if (!right.has(value)) return false;
125
+ }
126
+ return true;
127
+ }
128
+ function canReuseCustomFieldDefinitions(resolved, request) {
129
+ if (!resolved) return false;
130
+ if ((resolved.tenantId ?? null) !== (request.tenantId ?? null)) return false;
131
+ if (!sameStringSet(resolved.entityIds.map(String), request.entityIds.map(String))) return false;
132
+ if (!sameStringSet(resolved.organizationIds, request.organizationIds)) return false;
133
+ return true;
134
+ }
135
+ export {
136
+ buildCustomFieldDefinitionIndexFromRows,
137
+ canReuseCustomFieldDefinitions,
138
+ normalizeDefinitionConfig,
139
+ normalizeDefinitionKey,
140
+ normalizeFieldsetFilter,
141
+ resolveCfDefIndexOrgCandidates,
142
+ selectDefinitionForRecord,
143
+ sortDefinitionSummaries,
144
+ summarizeDefinitionRow
145
+ };
146
+ //# sourceMappingURL=custom-field-definition-index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/crud/custom-field-definition-index.ts"],
4
+ "sourcesContent": ["// Core-free building blocks for the custom-field definition index.\n//\n// This module intentionally has ZERO dependency on `@open-mercato/core` (no ORM\n// entity imports) so that infrastructure code such as the query engine can build\n// the same definition index that `custom-fields.ts` produces via MikroORM, without\n// pulling a domain package into the query layer.\n\nexport type CustomFieldDefinitionSummary = {\n key: string\n label: string | null\n kind: string | null\n multi: boolean\n dictionaryId?: string | null\n organizationId?: string | null\n tenantId?: string | null\n priority: number\n updatedAt: number\n}\n\nexport type CustomFieldDefinitionIndex = Map<string, CustomFieldDefinitionSummary[]>\n\n// Plain-row representation of a `custom_field_defs` record. Both the ORM-backed\n// loader (`custom-fields.ts`) and the Kysely-backed query engine map their native\n// rows into this shape before building an index, so the two paths stay in lockstep.\nexport type CustomFieldDefinitionRow = {\n key: string\n entityId: string\n kind: string | null\n configJson: unknown\n organizationId: string | null\n tenantId: string | null\n deletedAt: Date | string | number | null\n updatedAt: Date | string | number | null\n}\n\n// The resolved definition index the query engine threads onto its result so the\n// CRUD factory can decorate list rows without reloading definitions (issue #2133).\nexport type ResolvedCustomFieldDefinitions = {\n index: CustomFieldDefinitionIndex\n entityIds: string[]\n tenantId: string | null\n organizationIds: string[]\n}\n\nexport function normalizeDefinitionKey(key: unknown): string {\n if (typeof key !== 'string') return ''\n const trimmed = key.trim()\n return trimmed.length ? trimmed.toLowerCase() : ''\n}\n\nexport function normalizeDefinitionConfig(raw: unknown): Record<string, any> {\n if (!raw) return {}\n if (typeof raw === 'string') {\n try {\n const parsed = JSON.parse(raw)\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return { ...(parsed as Record<string, any>) }\n }\n return {}\n } catch {\n return {}\n }\n }\n if (typeof raw === 'object' && !Array.isArray(raw)) {\n return { ...(raw as Record<string, any>) }\n }\n return {}\n}\n\nexport function normalizeFieldsetFilter(input?: string | string[] | null): Set<string | null> | null {\n if (input == null) return null\n const values = Array.isArray(input) ? input : [input]\n const normalized = new Set<string | null>()\n for (const raw of values) {\n if (raw == null) continue\n const trimmed = String(raw).trim()\n if (!trimmed) {\n normalized.add(null)\n } else {\n normalized.add(trimmed)\n }\n }\n return normalized.size ? normalized : null\n}\n\nfunction toTimeMs(value: Date | string | number | null | undefined): number {\n if (value == null) return 0\n if (value instanceof Date) return value.getTime()\n const parsed = new Date(value as any).getTime()\n return Number.isNaN(parsed) ? 0 : parsed\n}\n\nexport function summarizeDefinitionRow(row: CustomFieldDefinitionRow): CustomFieldDefinitionSummary | null {\n const normalizedKey = normalizeDefinitionKey(row.key)\n if (!normalizedKey) return null\n const cfg = normalizeDefinitionConfig(row.configJson)\n const label =\n typeof cfg.label === 'string' && cfg.label.trim().length\n ? cfg.label.trim()\n : row.key\n const dictionaryId =\n typeof cfg.dictionaryId === 'string' && cfg.dictionaryId.trim().length\n ? cfg.dictionaryId.trim()\n : null\n const multi = cfg.multi !== undefined ? Boolean(cfg.multi) : false\n const priority = typeof cfg.priority === 'number' ? cfg.priority : 0\n return {\n key: row.key,\n label,\n kind: typeof row.kind === 'string' ? row.kind : null,\n multi,\n dictionaryId,\n organizationId: row.organizationId ?? null,\n tenantId: row.tenantId ?? null,\n priority,\n updatedAt: toTimeMs(row.updatedAt),\n }\n}\n\nexport function sortDefinitionSummaries(defs: CustomFieldDefinitionSummary[]): CustomFieldDefinitionSummary[] {\n return [...defs].sort((a, b) => {\n const priorityDiff = (a.priority ?? 0) - (b.priority ?? 0)\n if (priorityDiff !== 0) return priorityDiff\n const updatedDiff = (b.updatedAt ?? 0) - (a.updatedAt ?? 0)\n if (updatedDiff !== 0) return updatedDiff\n return a.key.localeCompare(b.key)\n })\n}\n\nexport function selectDefinitionForRecord(\n defs: CustomFieldDefinitionSummary[],\n organizationId: string | null,\n tenantId: string | null,\n): CustomFieldDefinitionSummary | null {\n if (!defs.length) return null\n const prioritizedForOrg = defs.filter(\n (def) => def.organizationId && organizationId && def.organizationId === organizationId,\n )\n if (prioritizedForOrg.length) return sortDefinitionSummaries(prioritizedForOrg)[0]\n const prioritizedForTenant = defs.filter(\n (def) => def.tenantId && tenantId && def.tenantId === tenantId && !def.organizationId,\n )\n if (prioritizedForTenant.length) return sortDefinitionSummaries(prioritizedForTenant)[0]\n const global = defs.filter((def) => !def.organizationId)\n if (global.length) return sortDefinitionSummaries(global)[0]\n return sortDefinitionSummaries(defs)[0] ?? null\n}\n\n// Resolve the effective list of organization candidates for a definition index\n// lookup, mirroring `loadCustomFieldDefinitionIndex`: when explicit org ids are\n// present they win, otherwise the fallback (selected org) is used. Null/empty\n// entries are dropped \u2014 the null-org branch is always allowed by the index filter.\nexport function resolveCfDefIndexOrgCandidates(\n organizationIds: Array<string | null | undefined> | null | undefined,\n fallbackOrganizationId: string | null | undefined,\n): string[] {\n const source = Array.isArray(organizationIds) && organizationIds.length\n ? organizationIds\n : [fallbackOrganizationId ?? null]\n return source\n .map((id) => (typeof id === 'string' ? id.trim() : id))\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n}\n\nfunction matchesFieldset(configJson: unknown, fieldsetFilter: Set<string | null>): boolean {\n const config = normalizeDefinitionConfig(configJson)\n const fieldsets = Array.isArray(config.fieldsets)\n ? config.fieldsets\n .filter((entry: unknown): entry is string => typeof entry === 'string')\n .map((entry: string) => entry.trim())\n .filter((entry: string) => entry.length > 0)\n : []\n const fieldset = typeof config.fieldset === 'string' && config.fieldset.trim().length > 0\n ? config.fieldset.trim()\n : null\n return fieldsets.length > 0\n ? fieldsets.some((entry: string) => fieldsetFilter.has(entry))\n : fieldsetFilter.has(fieldset)\n}\n\n// Build the grouped + sorted definition index from plain rows. Callers must\n// pre-filter rows by tenant + is_active (both the ORM loader and the query engine\n// already do so in SQL); this function additionally applies the org candidate\n// filter, the soft-delete guard, and the optional fieldset filter so its output is\n// byte-for-byte identical to the ORM-backed loader for the same logical scope.\nexport function buildCustomFieldDefinitionIndexFromRows(\n rows: CustomFieldDefinitionRow[],\n opts: { organizationIds?: string[] | null; fieldset?: string | string[] | null } = {},\n): CustomFieldDefinitionIndex {\n const orgCandidates = opts.organizationIds ?? []\n const fieldsetFilter = normalizeFieldsetFilter(opts.fieldset)\n const index: CustomFieldDefinitionIndex = new Map()\n for (const row of rows) {\n if (row.deletedAt != null) continue\n const org = row.organizationId ?? null\n if (org !== null && !(orgCandidates.length > 0 && orgCandidates.includes(org))) continue\n if (fieldsetFilter && !matchesFieldset(row.configJson, fieldsetFilter)) continue\n const summary = summarizeDefinitionRow(row)\n if (!summary) continue\n const normalizedKey = normalizeDefinitionKey(summary.key)\n if (!normalizedKey) continue\n if (!index.has(normalizedKey)) index.set(normalizedKey, [])\n index.get(normalizedKey)!.push(summary)\n }\n index.forEach((entries, key) => {\n index.set(key, sortDefinitionSummaries(entries))\n })\n return index\n}\n\nfunction sameStringSet(a: readonly string[], b: readonly string[]): boolean {\n const left = new Set(a)\n const right = new Set(b)\n if (left.size !== right.size) return false\n for (const value of left) {\n if (!right.has(value)) return false\n }\n return true\n}\n\n// Decide whether a precomputed definition index from a QueryEngine result can be\n// reused for a decoration request. Reuse is only safe when the engine resolved\n// definitions for exactly the same entity-id set, tenant, and org candidates.\nexport function canReuseCustomFieldDefinitions(\n resolved: ResolvedCustomFieldDefinitions | null | undefined,\n request: { entityIds: string[]; tenantId: string | null; organizationIds: string[] },\n): boolean {\n if (!resolved) return false\n if ((resolved.tenantId ?? null) !== (request.tenantId ?? null)) return false\n if (!sameStringSet(resolved.entityIds.map(String), request.entityIds.map(String))) return false\n if (!sameStringSet(resolved.organizationIds, request.organizationIds)) return false\n return true\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -2,6 +2,12 @@ import { CustomFieldDef, CustomFieldValue } from "@open-mercato/core/modules/ent
2
2
  import { decryptCustomFieldValue, resolveTenantEncryptionService } from "../encryption/customFieldValues.js";
3
3
  import { parseBooleanToken } from "../boolean.js";
4
4
  import { extractCustomFieldEntries } from "./custom-fields-client.js";
5
+ import {
6
+ buildCustomFieldDefinitionIndexFromRows,
7
+ normalizeDefinitionKey,
8
+ normalizeFieldsetFilter,
9
+ selectDefinitionForRecord
10
+ } from "./custom-field-definition-index.js";
5
11
  function buildCustomFieldSelectorsForEntity(entityId, sets) {
6
12
  const keys = Array.from(new Set(
7
13
  (sets || []).filter((s) => s.entity === entityId).flatMap((s) => (s.fields || []).map((f) => f.key))
@@ -36,21 +42,6 @@ function extractCustomFieldsFromItem(item, keys) {
36
42
  function extractAllCustomFieldEntries(item) {
37
43
  return extractCustomFieldEntries(item);
38
44
  }
39
- function normalizeFieldsetFilter(input) {
40
- if (input == null) return null;
41
- const values = Array.isArray(input) ? input : [input];
42
- const normalized = /* @__PURE__ */ new Set();
43
- for (const raw of values) {
44
- if (raw == null) continue;
45
- const trimmed = String(raw).trim();
46
- if (!trimmed) {
47
- normalized.add(null);
48
- } else {
49
- normalized.add(trimmed);
50
- }
51
- }
52
- return normalized.size ? normalized : null;
53
- }
54
45
  async function buildCustomFieldFiltersFromQuery(opts) {
55
46
  const out = {};
56
47
  const entries = Object.entries(opts.query).filter(([k]) => k.startsWith("cf_"));
@@ -165,73 +156,6 @@ function splitCustomFieldPayload(raw) {
165
156
  function extractCustomFieldValuesFromPayload(raw) {
166
157
  return splitCustomFieldPayload(raw).custom;
167
158
  }
168
- function normalizeDefinitionKey(key) {
169
- if (typeof key !== "string") return "";
170
- const trimmed = key.trim();
171
- return trimmed.length ? trimmed.toLowerCase() : "";
172
- }
173
- function normalizeDefinitionConfig(raw) {
174
- if (!raw) return {};
175
- if (typeof raw === "string") {
176
- try {
177
- const parsed = JSON.parse(raw);
178
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
179
- return { ...parsed };
180
- }
181
- return {};
182
- } catch {
183
- return {};
184
- }
185
- }
186
- if (typeof raw === "object" && !Array.isArray(raw)) {
187
- return { ...raw };
188
- }
189
- return {};
190
- }
191
- function summarizeDefinition(def) {
192
- const normalizedKey = normalizeDefinitionKey(def.key);
193
- if (!normalizedKey) return null;
194
- const cfg = normalizeDefinitionConfig(def.configJson);
195
- const label = typeof cfg.label === "string" && cfg.label.trim().length ? cfg.label.trim() : def.key;
196
- const dictionaryId = typeof cfg.dictionaryId === "string" && cfg.dictionaryId.trim().length ? cfg.dictionaryId.trim() : null;
197
- const multi = cfg.multi !== void 0 ? Boolean(cfg.multi) : false;
198
- const priority = typeof cfg.priority === "number" ? cfg.priority : 0;
199
- const updatedAt = def.updatedAt instanceof Date ? def.updatedAt.getTime() : new Date(def.updatedAt).getTime();
200
- return {
201
- key: def.key,
202
- label,
203
- kind: typeof def.kind === "string" ? def.kind : null,
204
- multi,
205
- dictionaryId,
206
- organizationId: def.organizationId ?? null,
207
- tenantId: def.tenantId ?? null,
208
- priority,
209
- updatedAt: Number.isNaN(updatedAt) ? 0 : updatedAt
210
- };
211
- }
212
- function sortDefinitionSummaries(defs) {
213
- return [...defs].sort((a, b) => {
214
- const priorityDiff = (a.priority ?? 0) - (b.priority ?? 0);
215
- if (priorityDiff !== 0) return priorityDiff;
216
- const updatedDiff = (b.updatedAt ?? 0) - (a.updatedAt ?? 0);
217
- if (updatedDiff !== 0) return updatedDiff;
218
- return a.key.localeCompare(b.key);
219
- });
220
- }
221
- function selectDefinitionForRecord(defs, organizationId, tenantId) {
222
- if (!defs.length) return null;
223
- const prioritizedForOrg = defs.filter(
224
- (def) => def.organizationId && organizationId && def.organizationId === organizationId
225
- );
226
- if (prioritizedForOrg.length) return sortDefinitionSummaries(prioritizedForOrg)[0];
227
- const prioritizedForTenant = defs.filter(
228
- (def) => def.tenantId && tenantId && def.tenantId === tenantId && !def.organizationId
229
- );
230
- if (prioritizedForTenant.length) return sortDefinitionSummaries(prioritizedForTenant)[0];
231
- const global = defs.filter((def) => !def.organizationId);
232
- if (global.length) return sortDefinitionSummaries(global)[0];
233
- return sortDefinitionSummaries(defs)[0] ?? null;
234
- }
235
159
  const CF_DEF_INDEX_CACHE_KEY_PREFIX = "crud:cf-def-index";
236
160
  const CF_DEF_INDEX_DEFAULT_TTL_MS = 0;
237
161
  function resolveCfDefIndexCacheTtlMs() {
@@ -311,27 +235,20 @@ async function loadCustomFieldDefinitionIndexFresh(opts) {
311
235
  $and: scopeClauses
312
236
  };
313
237
  const defs = await em.find(CustomFieldDef, where);
314
- const fieldsetFilter = normalizeFieldsetFilter(opts.fieldset);
315
- const index = /* @__PURE__ */ new Map();
316
- defs.forEach((def) => {
317
- if (fieldsetFilter) {
318
- const config = normalizeDefinitionConfig(def.configJson);
319
- const fieldsets = Array.isArray(config.fieldsets) ? config.fieldsets.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter((entry) => entry.length > 0) : [];
320
- const fieldset = typeof config.fieldset === "string" && config.fieldset.trim().length > 0 ? config.fieldset.trim() : null;
321
- const matches = fieldsets.length > 0 ? fieldsets.some((entry) => fieldsetFilter.has(entry)) : fieldsetFilter.has(fieldset);
322
- if (!matches) return;
323
- }
324
- const summary = summarizeDefinition(def);
325
- if (!summary) return;
326
- const normalizedKey = normalizeDefinitionKey(summary.key);
327
- if (!normalizedKey) return;
328
- if (!index.has(normalizedKey)) index.set(normalizedKey, []);
329
- index.get(normalizedKey).push(summary);
330
- });
331
- index.forEach((entries, key) => {
332
- index.set(key, sortDefinitionSummaries(entries));
238
+ const rows = defs.map((def) => ({
239
+ key: def.key,
240
+ entityId: String(def.entityId),
241
+ kind: typeof def.kind === "string" ? def.kind : null,
242
+ configJson: def.configJson,
243
+ organizationId: def.organizationId ?? null,
244
+ tenantId: def.tenantId ?? null,
245
+ deletedAt: def.deletedAt ?? null,
246
+ updatedAt: def.updatedAt ?? null
247
+ }));
248
+ return buildCustomFieldDefinitionIndexFromRows(rows, {
249
+ organizationIds: orgCandidates,
250
+ fieldset: opts.fieldset
333
251
  });
334
- return index;
335
252
  }
336
253
  async function loadCustomFieldDefinitionIndex(opts) {
337
254
  const list = Array.isArray(opts.entityIds) ? opts.entityIds : [opts.entityIds];
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/crud/custom-fields.ts"],
4
- "sourcesContent": ["import type { CustomFieldSet, EntityId } from '@open-mercato/shared/modules/entities'\nimport type { EntityManager } from '@mikro-orm/core'\nimport { CustomFieldDef, CustomFieldValue } from '@open-mercato/core/modules/entities/data/entities'\nimport type { WhereValue } from '@open-mercato/shared/lib/query/types'\nimport type { TenantDataEncryptionService } from '../encryption/tenantDataEncryptionService'\nimport { decryptCustomFieldValue, resolveTenantEncryptionService } from '../encryption/customFieldValues'\nimport { parseBooleanToken } from '../boolean'\nimport { extractCustomFieldEntries } from './custom-fields-client'\n\nexport type CustomFieldSelectors = {\n keys: string[]\n selectors: string[] // e.g. ['cf:priority', 'cf:severity']\n outputKeys: string[] // e.g. ['cf_priority', 'cf_severity']\n}\n\nexport type SplitCustomFieldPayload = {\n base: Record<string, unknown>\n custom: Record<string, unknown>\n}\n\nexport type CustomFieldDefinitionSummary = {\n key: string\n label: string | null\n kind: string | null\n multi: boolean\n dictionaryId?: string | null\n organizationId?: string | null\n tenantId?: string | null\n priority: number\n updatedAt: number\n}\n\nexport type CustomFieldDefinitionIndex = Map<string, CustomFieldDefinitionSummary[]>\n\nexport type CustomFieldDisplayEntry = {\n key: string\n label: string | null\n value: unknown\n kind: string | null\n multi: boolean\n}\n\nexport type CustomFieldDisplayPayload = {\n customValues: Record<string, unknown> | null\n customFields: CustomFieldDisplayEntry[]\n}\n\nexport type CustomFieldSnapshot = {\n entries: Record<string, unknown>\n customValues: Record<string, unknown> | null\n customFields: CustomFieldDisplayEntry[]\n}\n\nexport function buildCustomFieldSelectorsForEntity(entityId: EntityId, sets: CustomFieldSet[]): CustomFieldSelectors {\n const keys = Array.from(new Set(\n (sets || [])\n .filter((s) => s.entity === entityId)\n .flatMap((s) => (s.fields || []).map((f) => f.key))\n ))\n const selectors = keys.map((k) => `cf:${k}`)\n const outputKeys = keys.map((k) => `cf_${k}`)\n return { keys, selectors, outputKeys }\n}\n\nexport function normalizeCustomFieldValue(val: unknown): unknown {\n if (Array.isArray(val)) return val\n if (typeof val === 'string') {\n const s = val.trim()\n // Parse Postgres array-like '{a,b,c}' to string[] when present\n if (s.startsWith('{') && s.endsWith('}')) {\n const inner = s.slice(1, -1).trim()\n if (!inner) return []\n return inner.split(/[\\s,]+/).map((x) => x.trim()).filter(Boolean)\n }\n return s\n }\n return val as any\n}\n\n// Extracts cf_* fields from a record that may contain both 'cf:<key>' and/or 'cf_<key>'\nexport function extractCustomFieldsFromItem(item: Record<string, unknown>, keys: string[]): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const key of keys) {\n const colon = item[`cf:${key}` as keyof typeof item]\n const snake = item[`cf_${key}` as keyof typeof item]\n const value = colon !== undefined ? colon : snake\n if (value !== undefined) out[`cf_${key}`] = normalizeCustomFieldValue(value)\n }\n return out\n}\n\nexport function extractAllCustomFieldEntries(item: Record<string, unknown>): Record<string, unknown> {\n return extractCustomFieldEntries(item)\n}\n\nfunction normalizeFieldsetFilter(input?: string | string[] | null): Set<string | null> | null {\n if (input == null) return null\n const values = Array.isArray(input) ? input : [input]\n const normalized = new Set<string | null>()\n for (const raw of values) {\n if (raw == null) continue\n const trimmed = String(raw).trim()\n if (!trimmed) {\n normalized.add(null)\n } else {\n normalized.add(trimmed)\n }\n }\n return normalized.size ? normalized : null\n}\n\nexport async function buildCustomFieldFiltersFromQuery(opts: {\n entityId?: EntityId\n entityIds?: EntityId[]\n query: Record<string, unknown>\n em: EntityManager\n tenantId: string | null | undefined\n fieldset?: string | string[] | null\n}): Promise<Record<string, WhereValue>> {\n const out: Record<string, WhereValue> = {}\n const entries = Object.entries(opts.query).filter(([k]) => k.startsWith('cf_'))\n if (!entries.length) return out\n\n const entityIdList = Array.isArray(opts.entityIds) && opts.entityIds.length\n ? opts.entityIds\n : opts.entityId\n ? [opts.entityId]\n : []\n if (!entityIdList.length) return out\n\n // Tenant-only scope: allow global (null) or tenant match; ignore organization here\n const defs = await opts.em.find(CustomFieldDef, {\n entityId: { $in: entityIdList as any },\n isActive: true,\n $and: [\n { $or: [ { tenantId: opts.tenantId as any }, { tenantId: null } ] },\n ],\n })\n const fieldsetFilter = normalizeFieldsetFilter(opts.fieldset)\n const order = new Map<string, number>()\n entityIdList.map(String).forEach((id, index) => order.set(id, index))\n const byKey: Record<string, { kind: string; multi?: boolean; entityId: string }> = {}\n for (const d of defs) {\n if (fieldsetFilter) {\n const fieldsets = Array.isArray(d.configJson?.fieldsets)\n ? d.configJson.fieldsets\n .filter((entry: unknown): entry is string => typeof entry === 'string')\n .map((entry: string) => entry.trim())\n .filter((entry: string) => entry.length > 0)\n : []\n const rawFieldset = typeof d.configJson?.fieldset === 'string' ? d.configJson.fieldset.trim() : ''\n const normalizedFieldset = rawFieldset.length ? rawFieldset : null\n const matches = fieldsets.length > 0\n ? fieldsets.some((entry: string) => fieldsetFilter.has(entry))\n : fieldsetFilter.has(normalizedFieldset)\n if (!matches) continue\n }\n const key = d.key\n const entityId = String(d.entityId)\n const current = byKey[key]\n const rankNew = order.get(entityId) ?? Number.MAX_SAFE_INTEGER\n if (!current) {\n byKey[key] = { kind: d.kind, multi: Boolean((d as any).configJson?.multi), entityId }\n continue\n }\n const rankOld = order.get(current.entityId) ?? Number.MAX_SAFE_INTEGER\n if (rankNew < rankOld) {\n byKey[key] = { kind: d.kind, multi: Boolean((d as any).configJson?.multi), entityId }\n }\n }\n\n const coerce = (kind: string, v: unknown) => {\n if (v == null) return v as undefined\n switch (kind) {\n case 'integer': return Number.parseInt(String(v), 10)\n case 'float': return Number.parseFloat(String(v))\n case 'boolean': return parseBooleanToken(String(v)) === true\n case 'date':\n case 'datetime': return String(v)\n default: return String(v)\n }\n }\n\n for (const [rawKey, rawVal] of entries) {\n const isIn = rawKey.endsWith('In')\n const key = isIn ? rawKey.replace(/^cf_/, '').replace(/In$/, '') : rawKey.replace(/^cf_/, '')\n const def = byKey[key]\n const fieldId = `cf:${key}`\n if (!def) continue\n if (isIn) {\n const list = Array.isArray(rawVal)\n ? (rawVal as unknown[])\n : String(rawVal)\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (list.length) out[fieldId] = { $in: list.map((x) => coerce(def.kind, x)) as (string[] | number[] | boolean[]) }\n } else {\n out[fieldId] = coerce(def.kind, rawVal)\n }\n }\n\n return out\n}\n\nexport function splitCustomFieldPayload(raw: unknown): SplitCustomFieldPayload {\n const base: Record<string, unknown> = {}\n const custom: Record<string, unknown> = {}\n if (!raw || typeof raw !== 'object') return { base, custom }\n for (const [key, value] of Object.entries(raw as Record<string, unknown>)) {\n if (key === 'customFields') {\n if (Array.isArray(value)) {\n value.forEach((entry) => {\n if (!entry || typeof entry !== 'object') return\n const entryKey = typeof (entry as any).key === 'string' ? (entry as any).key.trim() : ''\n if (!entryKey) return\n custom[entryKey] = (entry as any).value\n })\n continue\n }\n if (value && typeof value === 'object') {\n for (const [ck, cv] of Object.entries(value as Record<string, unknown>)) {\n const normalizedKey = typeof ck === 'string' ? ck.trim() : ''\n if (!normalizedKey) continue\n custom[normalizedKey] = cv\n }\n continue\n }\n }\n if (key === 'customValues' && value && typeof value === 'object' && !Array.isArray(value)) {\n for (const [ck, cv] of Object.entries(value as Record<string, unknown>)) {\n custom[String(ck)] = cv\n }\n continue\n }\n if (key.startsWith('cf_')) {\n custom[key.slice(3)] = value\n continue\n }\n if (key.startsWith('cf:')) {\n custom[key.slice(3)] = value\n continue\n }\n base[key] = value\n }\n return { base, custom }\n}\n\nexport function extractCustomFieldValuesFromPayload(raw: Record<string, unknown>): Record<string, unknown> {\n return splitCustomFieldPayload(raw).custom\n}\n\nfunction normalizeDefinitionKey(key: unknown): string {\n if (typeof key !== 'string') return ''\n const trimmed = key.trim()\n return trimmed.length ? trimmed.toLowerCase() : ''\n}\n\nfunction normalizeDefinitionConfig(raw: unknown): Record<string, any> {\n if (!raw) return {}\n if (typeof raw === 'string') {\n try {\n const parsed = JSON.parse(raw)\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return { ...(parsed as Record<string, any>) }\n }\n return {}\n } catch {\n return {}\n }\n }\n if (typeof raw === 'object' && !Array.isArray(raw)) {\n return { ...(raw as Record<string, any>) }\n }\n return {}\n}\n\nfunction summarizeDefinition(def: CustomFieldDef): CustomFieldDefinitionSummary | null {\n const normalizedKey = normalizeDefinitionKey(def.key)\n if (!normalizedKey) return null\n const cfg = normalizeDefinitionConfig((def as any).configJson)\n const label =\n typeof cfg.label === 'string' && cfg.label.trim().length\n ? cfg.label.trim()\n : def.key\n const dictionaryId =\n typeof cfg.dictionaryId === 'string' && cfg.dictionaryId.trim().length\n ? cfg.dictionaryId.trim()\n : null\n const multi =\n cfg.multi !== undefined ? Boolean(cfg.multi) : false\n const priority =\n typeof cfg.priority === 'number' ? cfg.priority : 0\n const updatedAt =\n def.updatedAt instanceof Date\n ? def.updatedAt.getTime()\n : new Date(def.updatedAt as any).getTime()\n return {\n key: def.key,\n label,\n kind: typeof def.kind === 'string' ? def.kind : null,\n multi,\n dictionaryId,\n organizationId: def.organizationId ?? null,\n tenantId: def.tenantId ?? null,\n priority,\n updatedAt: Number.isNaN(updatedAt) ? 0 : updatedAt,\n }\n}\n\nfunction sortDefinitionSummaries(defs: CustomFieldDefinitionSummary[]): CustomFieldDefinitionSummary[] {\n return [...defs].sort((a, b) => {\n const priorityDiff = (a.priority ?? 0) - (b.priority ?? 0)\n if (priorityDiff !== 0) return priorityDiff\n const updatedDiff = (b.updatedAt ?? 0) - (a.updatedAt ?? 0)\n if (updatedDiff !== 0) return updatedDiff\n return a.key.localeCompare(b.key)\n })\n}\n\nfunction selectDefinitionForRecord(\n defs: CustomFieldDefinitionSummary[],\n organizationId: string | null,\n tenantId: string | null,\n): CustomFieldDefinitionSummary | null {\n if (!defs.length) return null\n const prioritizedForOrg = defs.filter(\n (def) => def.organizationId && organizationId && def.organizationId === organizationId,\n )\n if (prioritizedForOrg.length) return sortDefinitionSummaries(prioritizedForOrg)[0]\n const prioritizedForTenant = defs.filter(\n (def) => def.tenantId && tenantId && def.tenantId === tenantId && !def.organizationId,\n )\n if (prioritizedForTenant.length) return sortDefinitionSummaries(prioritizedForTenant)[0]\n const global = defs.filter((def) => !def.organizationId)\n if (global.length) return sortDefinitionSummaries(global)[0]\n return sortDefinitionSummaries(defs)[0] ?? null\n}\n\ntype LoadCustomFieldDefinitionIndexOptions = {\n em: EntityManager\n entityIds: string | string[]\n tenantId?: string | null | undefined\n organizationIds?: Array<string | null | undefined> | null\n fieldset?: string | string[] | null\n}\n\ntype CustomFieldDefIndexCache = {\n get(key: string): Promise<unknown> | unknown\n set(key: string, value: unknown, opts?: { ttl?: number; tags?: string[] }): Promise<unknown> | unknown\n deleteByTags?(tags: string[]): Promise<number> | number\n}\n\nconst CF_DEF_INDEX_CACHE_KEY_PREFIX = 'crud:cf-def-index'\n// Phase 2 default-off: integration runs observed `/api/customers/people`\n// returning 500 with this cache path active, and the readiness-probe\n// timeout blocked artifact upload so the direct stack trace was lost.\n// Until cross-request safety of the SQLite-cache JSON round-trip is\n// re-verified, ship with the layer disabled. Set\n// `OM_CF_DEF_CACHE_TTL_MS=300000` (or any positive integer) to opt in.\nconst CF_DEF_INDEX_DEFAULT_TTL_MS = 0\n\nfunction resolveCfDefIndexCacheTtlMs(): number {\n const raw = process.env.OM_CF_DEF_CACHE_TTL_MS\n if (raw === undefined) return CF_DEF_INDEX_DEFAULT_TTL_MS\n const parsed = Number(raw)\n if (!Number.isFinite(parsed) || parsed < 0) return CF_DEF_INDEX_DEFAULT_TTL_MS\n return parsed\n}\n\nfunction buildCfDefIndexCacheKey(opts: {\n tenantId: string | null\n entityIds: string[]\n organizationIds: string[]\n fieldsetKey: string | null\n}): string {\n const tenant = opts.tenantId ?? 'global'\n const entities = opts.entityIds.slice().sort().join('|')\n const orgs = opts.organizationIds.length ? opts.organizationIds.slice().sort().join('|') : 'none'\n const fieldset = opts.fieldsetKey ?? 'all'\n return `${CF_DEF_INDEX_CACHE_KEY_PREFIX}:${tenant}:${entities}:${orgs}:${fieldset}`\n}\n\nfunction buildCfDefIndexCacheTags(opts: {\n tenantId: string | null\n entityIds: string[]\n}): string[] {\n const tenant = opts.tenantId ?? 'global'\n const tagBase = `entities:definitions:${tenant}`\n const tags = new Set<string>([tagBase])\n for (const entityId of opts.entityIds) {\n tags.add(`${tagBase}:entity:${entityId}`)\n }\n return Array.from(tags)\n}\n\nfunction normalizeFieldsetKey(value: string | string[] | null | undefined): string | null {\n if (!value) return null\n if (Array.isArray(value)) {\n const cleaned = value\n .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))\n .filter((entry) => entry.length > 0)\n if (!cleaned.length) return null\n return cleaned.sort().join(',')\n }\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\nfunction serializableIndexFromMap(index: CustomFieldDefinitionIndex): Array<[string, CustomFieldDefinitionSummary[]]> {\n return Array.from(index.entries())\n}\n\nfunction indexMapFromSerializable(value: unknown): CustomFieldDefinitionIndex | null {\n if (!Array.isArray(value)) return null\n const map: CustomFieldDefinitionIndex = new Map()\n for (const entry of value) {\n if (!Array.isArray(entry) || entry.length !== 2) return null\n const [key, summaries] = entry as [unknown, unknown]\n if (typeof key !== 'string' || !Array.isArray(summaries)) return null\n map.set(key, summaries as CustomFieldDefinitionSummary[])\n }\n return map\n}\n\n// Per-request micro-cache. Two CRUD calls within one HTTP request (rare but\n// possible via interceptors) share the same Map keyed by ctx-like objects.\nconst requestScopedCfDefIndexCache = new WeakMap<object, Map<string, CustomFieldDefinitionIndex>>()\n\nexport type CustomFieldDefinitionIndexCacheKey = string\n\nexport function getRequestScopedCfDefIndexCache(scope: object): Map<string, CustomFieldDefinitionIndex> {\n let bucket = requestScopedCfDefIndexCache.get(scope)\n if (!bucket) {\n bucket = new Map()\n requestScopedCfDefIndexCache.set(scope, bucket)\n }\n return bucket\n}\n\nasync function loadCustomFieldDefinitionIndexFresh(\n opts: LoadCustomFieldDefinitionIndexOptions & { entityIds: string[]; orgCandidates: string[] }\n): Promise<CustomFieldDefinitionIndex> {\n const { em, entityIds, orgCandidates } = opts\n const tenantId = opts.tenantId ?? null\n const scopeClauses: Record<string, unknown>[] = [\n tenantId\n ? { $or: [{ tenantId: tenantId as any }, { tenantId: null }] }\n : { tenantId: null },\n ]\n if (orgCandidates.length) {\n scopeClauses.push({\n $or: [{ organizationId: { $in: orgCandidates as any } }, { organizationId: null }],\n })\n } else {\n scopeClauses.push({ organizationId: null })\n }\n const where: Record<string, unknown> = {\n entityId: { $in: entityIds as any },\n deletedAt: null,\n isActive: true,\n $and: scopeClauses,\n }\n const defs = await em.find(CustomFieldDef, where as any)\n const fieldsetFilter = normalizeFieldsetFilter(opts.fieldset)\n const index: CustomFieldDefinitionIndex = new Map()\n defs.forEach((def) => {\n if (fieldsetFilter) {\n const config = normalizeDefinitionConfig((def as any).configJson)\n const fieldsets = Array.isArray(config.fieldsets)\n ? config.fieldsets\n .filter((entry: unknown): entry is string => typeof entry === 'string')\n .map((entry: string) => entry.trim())\n .filter((entry: string) => entry.length > 0)\n : []\n const fieldset = typeof config.fieldset === 'string' && config.fieldset.trim().length > 0\n ? config.fieldset.trim()\n : null\n const matches = fieldsets.length > 0\n ? fieldsets.some((entry: string) => fieldsetFilter.has(entry))\n : fieldsetFilter.has(fieldset)\n if (!matches) return\n }\n const summary = summarizeDefinition(def)\n if (!summary) return\n const normalizedKey = normalizeDefinitionKey(summary.key)\n if (!normalizedKey) return\n if (!index.has(normalizedKey)) index.set(normalizedKey, [])\n index.get(normalizedKey)!.push(summary)\n })\n index.forEach((entries, key) => {\n index.set(key, sortDefinitionSummaries(entries))\n })\n return index\n}\n\nexport async function loadCustomFieldDefinitionIndex(opts: LoadCustomFieldDefinitionIndexOptions & {\n cache?: CustomFieldDefIndexCache | null\n requestScope?: object | null\n}): Promise<CustomFieldDefinitionIndex> {\n const list = Array.isArray(opts.entityIds) ? opts.entityIds : [opts.entityIds]\n const entityIds = list\n .map((id) => (typeof id === 'string' ? id.trim() : String(id ?? '')))\n .filter((id) => id.length > 0)\n if (!entityIds.length) return new Map()\n const tenantId = opts.tenantId ?? null\n const orgCandidates = Array.isArray(opts.organizationIds)\n ? opts.organizationIds\n .map((id) => (typeof id === 'string' ? id.trim() : id))\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n : []\n\n const fieldsetKey = normalizeFieldsetKey(opts.fieldset)\n const ttlMs = resolveCfDefIndexCacheTtlMs()\n const cacheKey = buildCfDefIndexCacheKey({\n tenantId,\n entityIds,\n organizationIds: orgCandidates,\n fieldsetKey,\n })\n\n const requestBucket = opts.requestScope\n ? getRequestScopedCfDefIndexCache(opts.requestScope)\n : null\n if (requestBucket) {\n const cached = requestBucket.get(cacheKey)\n if (cached) return cached\n }\n\n const sharedCache = ttlMs > 0 ? opts.cache ?? null : null\n if (sharedCache && typeof sharedCache.get === 'function') {\n try {\n const cached = await sharedCache.get(cacheKey)\n const restored = indexMapFromSerializable(cached)\n if (restored) {\n if (requestBucket) requestBucket.set(cacheKey, restored)\n return restored\n }\n } catch (err) {\n console.warn('[crud:cf-def-cache] read failed', err)\n }\n }\n\n const index = await loadCustomFieldDefinitionIndexFresh({\n ...opts,\n entityIds,\n orgCandidates,\n })\n\n if (sharedCache && typeof sharedCache.set === 'function') {\n try {\n await sharedCache.set(cacheKey, serializableIndexFromMap(index), {\n ttl: ttlMs,\n tags: buildCfDefIndexCacheTags({ tenantId, entityIds }),\n })\n } catch (err) {\n console.warn('[crud:cf-def-cache] write failed', err)\n }\n }\n if (requestBucket) requestBucket.set(cacheKey, index)\n return index\n}\n\nexport type ApplyCustomFieldsNormalizationOptions = {\n /**\n * When true, removes raw `cf_*` and `cf:*` keys from the record once they\n * have been extracted into `customValues` / `customFields`. Produces a single\n * canonical response shape (issue #1769). Defaults to `false` to preserve the\n * existing wire format for callers that read `cf_*` from the top level.\n */\n stripPrefixedKeys?: boolean\n}\n\nexport function applyCustomFieldsNormalization(\n record: Record<string, unknown>,\n decorated: CustomFieldDisplayPayload,\n options: ApplyCustomFieldsNormalizationOptions = {},\n): Record<string, unknown> {\n const stripPrefixedKeys = options.stripPrefixedKeys === true\n const base: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(record)) {\n if (stripPrefixedKeys && (key.startsWith('cf_') || key.startsWith('cf:'))) continue\n base[key] = value\n }\n base.customValues = decorated.customValues\n base.customFields = decorated.customFields\n return base\n}\n\nexport function decorateRecordWithCustomFields(\n record: Record<string, unknown>,\n definitions: CustomFieldDefinitionIndex,\n context: {\n organizationId?: string | null\n tenantId?: string | null\n } = {},\n): CustomFieldDisplayPayload {\n const rawEntries = extractAllCustomFieldEntries(record)\n if (!Object.keys(rawEntries).length) {\n return { customValues: null, customFields: [] }\n }\n const values: Record<string, unknown> = {}\n const entries: Array<{ entry: CustomFieldDisplayEntry; priority: number; updatedAt: number }> = []\n const organizationId = context.organizationId ?? null\n const tenantId = context.tenantId ?? null\n\n Object.entries(rawEntries).forEach(([prefixedKey, value]) => {\n const bareKey = prefixedKey.replace(/^cf_/, '')\n const normalizedKey = normalizeDefinitionKey(bareKey)\n if (!normalizedKey) return\n const defsForKey = definitions.get(normalizedKey) ?? []\n const resolvedDef = selectDefinitionForRecord(defsForKey, organizationId, tenantId)\n // Skip custom field values without active definitions to prevent orphaned fields\n if (!resolvedDef) return\n values[bareKey] = value\n const entry: CustomFieldDisplayEntry = {\n key: bareKey,\n label: resolvedDef.label ?? bareKey,\n value,\n kind: resolvedDef.kind ?? null,\n multi: resolvedDef.multi ?? Array.isArray(value),\n }\n entries.push({\n entry,\n priority: resolvedDef.priority ?? Number.MAX_SAFE_INTEGER,\n updatedAt: resolvedDef.updatedAt ?? 0,\n })\n })\n\n const ordered = entries\n .sort((a, b) => {\n const priorityDiff = a.priority - b.priority\n if (priorityDiff !== 0) return priorityDiff\n const updatedDiff = b.updatedAt - a.updatedAt\n if (updatedDiff !== 0) return updatedDiff\n return a.entry.key.localeCompare(b.entry.key)\n })\n .map((item) => item.entry)\n\n return {\n customValues: Object.keys(values).length ? values : null,\n customFields: ordered,\n }\n}\n\nexport async function loadCustomFieldValues(opts: {\n em: EntityManager\n entityId: EntityId\n recordIds: string[]\n tenantIdByRecord?: Record<string, string | null | undefined>\n organizationIdByRecord?: Record<string, string | null | undefined>\n tenantFallbacks?: (string | null | undefined)[]\n encryptionService?: TenantDataEncryptionService | null\n}): Promise<Record<string, Record<string, unknown>>> {\n const { em, entityId, recordIds } = opts\n if (!Array.isArray(recordIds) || recordIds.length === 0) return {}\n\n const normalizedRecordIds = recordIds.map((id) => String(id))\n let encryptionService: TenantDataEncryptionService | null | undefined\n const encryptionCache = new Map<string | null, string | null>()\n const getEncryptionService = () => {\n if (encryptionService !== undefined) return encryptionService\n encryptionService = resolveTenantEncryptionService(em, opts.encryptionService)\n return encryptionService\n }\n const tenantCandidates = new Set<string | null>()\n tenantCandidates.add(null)\n if (opts.tenantIdByRecord) {\n for (const val of Object.values(opts.tenantIdByRecord)) {\n tenantCandidates.add(val ? String(val) : null)\n }\n }\n if (opts.tenantFallbacks) {\n for (const val of opts.tenantFallbacks) tenantCandidates.add(val ? String(val) : null)\n }\n const fallbackTenant = (opts.tenantFallbacks || []).find((t) => t != null) ?? null\n\n const tenantList = Array.from(tenantCandidates)\n const tenantNonNull = tenantList.filter((t): t is string => t !== null)\n const tenantFilter = tenantNonNull.length\n ? { tenantId: { $in: [...tenantNonNull, null] as any } }\n : { tenantId: null }\n const cfRows = await em.find(CustomFieldValue, {\n entityId: entityId as any,\n recordId: { $in: normalizedRecordIds as any },\n deletedAt: null,\n ...(tenantList.length ? tenantFilter : {}),\n })\n\n if (!cfRows.length) return {}\n\n const allKeys = Array.from(new Set(cfRows.map((row) => String(row.fieldKey))))\n const organizationCandidates = new Set<string | null>()\n organizationCandidates.add(null)\n if (opts.organizationIdByRecord) {\n for (const val of Object.values(opts.organizationIdByRecord)) {\n organizationCandidates.add(val ? String(val) : null)\n }\n }\n for (const row of cfRows) {\n organizationCandidates.add(row.organizationId ? String(row.organizationId) : null)\n }\n const orgList = Array.from(organizationCandidates)\n\n const defs = allKeys.length\n ? await em.find(CustomFieldDef, {\n entityId: entityId as any,\n key: { $in: allKeys as any },\n deletedAt: null,\n isActive: true,\n ...(tenantList.length ? { tenantId: tenantFilter.tenantId } : {}),\n organizationId: { $in: orgList as any },\n })\n : []\n\n const defsByKey = new Map<string, CustomFieldDef[]>()\n for (const def of defs) {\n const list = defsByKey.get(def.key) || []\n list.push(def)\n defsByKey.set(def.key, list)\n }\n\n const pickDefinition = (fieldKey: string, organizationId: string | null, tenantId: string | null) => {\n const candidates = defsByKey.get(fieldKey)\n if (!candidates || candidates.length === 0) return null\n const active = candidates.filter((opt) => opt.isActive !== false && !opt.deletedAt)\n const list = active.length ? active : candidates\n if (organizationId && tenantId) {\n const exact = list.find((opt) => opt.organizationId === organizationId && opt.tenantId === tenantId)\n if (exact) return exact\n }\n if (organizationId) {\n const orgMatch = list.find((opt) => opt.organizationId === organizationId && (!tenantId || opt.tenantId == null || opt.tenantId === tenantId))\n if (orgMatch) return orgMatch\n }\n if (tenantId) {\n const tenantMatch = list.find((opt) => opt.organizationId == null && opt.tenantId === tenantId)\n if (tenantMatch) return tenantMatch\n }\n const global = list.find((opt) => opt.organizationId == null && opt.tenantId == null)\n return global ?? list[0]\n }\n\n const valueFromRow = (row: CustomFieldValue): unknown => {\n if (row.valueMultiline !== null && row.valueMultiline !== undefined) return row.valueMultiline\n if (row.valueText !== null && row.valueText !== undefined) return row.valueText\n if (row.valueInt !== null && row.valueInt !== undefined) return row.valueInt\n if (row.valueFloat !== null && row.valueFloat !== undefined) return row.valueFloat\n if (row.valueBool !== null && row.valueBool !== undefined) return row.valueBool\n return null\n }\n\n type Bucket = { orgId: string | null; tenantId: string | null; values: unknown[]; def?: CustomFieldDef | null; encrypted?: boolean }\n const buckets = new Map<string, Bucket>()\n\n for (const row of cfRows) {\n const recordId = String(row.recordId)\n const key = String(row.fieldKey)\n const bucketKey = `${recordId}::${key}`\n const orgId = row.organizationId ? String(row.organizationId) : null\n const tenantId = row.tenantId ? String(row.tenantId) : null\n const resolvedOrgId = orgId ?? (opts.organizationIdByRecord?.[recordId] ?? null)\n const resolvedTenantId = tenantId ?? (opts.tenantIdByRecord?.[recordId] ?? fallbackTenant)\n const def = pickDefinition(key, resolvedOrgId, resolvedTenantId)\n const encrypted = Boolean(def?.configJson && (def as any).configJson?.encrypted)\n const value = valueFromRow(row)\n const decrypted = encrypted\n ? await decryptCustomFieldValue(\n value,\n resolvedTenantId ?? tenantId ?? null,\n getEncryptionService(),\n encryptionCache,\n { kind: def?.kind ?? null },\n )\n : value\n const existing = buckets.get(bucketKey)\n if (existing) {\n if (existing.orgId == null && resolvedOrgId) existing.orgId = resolvedOrgId\n if (existing.tenantId == null && resolvedTenantId) existing.tenantId = resolvedTenantId\n if (existing.def == null && def) existing.def = def\n existing.encrypted = existing.encrypted || encrypted\n existing.values.push(decrypted)\n } else {\n buckets.set(bucketKey, { orgId: resolvedOrgId, tenantId: resolvedTenantId, values: [decrypted], def: def ?? null, encrypted })\n }\n }\n\n const result: Record<string, Record<string, unknown>> = {}\n for (const [compoundKey, bucket] of buckets.entries()) {\n const [recordId, fieldKey] = compoundKey.split('::')\n if (!result[recordId]) result[recordId] = {}\n const prefixed = `cf_${fieldKey}`\n const def = bucket.def ?? pickDefinition(fieldKey, bucket.orgId ?? (opts.organizationIdByRecord?.[recordId] ?? null), bucket.tenantId ?? (opts.tenantIdByRecord?.[recordId] ?? null))\n if (def && def.configJson && typeof def.configJson === 'object' && (def.configJson as any).multi) {\n const cleaned = bucket.values.filter((v) => v !== undefined && v !== null)\n result[recordId][prefixed] = cleaned\n } else if (bucket.values.length > 1) {\n const cleaned = bucket.values.filter((v) => v !== undefined)\n result[recordId][prefixed] = cleaned\n } else {\n result[recordId][prefixed] = bucket.values[0] ?? null\n }\n }\n\n return result\n}\n\nexport function summarizeCustomFields(record: Record<string, unknown>): CustomFieldSnapshot {\n const entries = extractAllCustomFieldEntries(record)\n const values = Object.fromEntries(\n Object.entries(entries).map(([prefixedKey, value]) => [\n prefixedKey.replace(/^cf_/, ''),\n value,\n ]),\n )\n const customValues = Object.keys(values).length ? values : null\n const customFields = Object.entries(values).map(([key, value]) => ({\n key,\n label: key,\n value,\n kind: null,\n multi: Array.isArray(value),\n }))\n return { entries, customValues, customFields }\n}\n"],
5
- "mappings": "AAEA,SAAS,gBAAgB,wBAAwB;AAGjD,SAAS,yBAAyB,sCAAsC;AACxE,SAAS,yBAAyB;AAClC,SAAS,iCAAiC;AA8CnC,SAAS,mCAAmC,UAAoB,MAA8C;AACnH,QAAM,OAAO,MAAM,KAAK,IAAI;AAAA,KACzB,QAAQ,CAAC,GACP,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ,EACnC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC;AAAA,EACtD,CAAC;AACD,QAAM,YAAY,KAAK,IAAI,CAAC,MAAM,MAAM,CAAC,EAAE;AAC3C,QAAM,aAAa,KAAK,IAAI,CAAC,MAAM,MAAM,CAAC,EAAE;AAC5C,SAAO,EAAE,MAAM,WAAW,WAAW;AACvC;AAEO,SAAS,0BAA0B,KAAuB;AAC/D,MAAI,MAAM,QAAQ,GAAG,EAAG,QAAO;AAC/B,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI,IAAI,KAAK;AAEnB,QAAI,EAAE,WAAW,GAAG,KAAK,EAAE,SAAS,GAAG,GAAG;AACxC,YAAM,QAAQ,EAAE,MAAM,GAAG,EAAE,EAAE,KAAK;AAClC,UAAI,CAAC,MAAO,QAAO,CAAC;AACpB,aAAO,MAAM,MAAM,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AAAA,IAClE;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGO,SAAS,4BAA4B,MAA+B,MAAyC;AAClH,QAAM,MAA+B,CAAC;AACtC,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,KAAK,MAAM,GAAG,EAAuB;AACnD,UAAM,QAAQ,KAAK,MAAM,GAAG,EAAuB;AACnD,UAAM,QAAQ,UAAU,SAAY,QAAQ;AAC5C,QAAI,UAAU,OAAW,KAAI,MAAM,GAAG,EAAE,IAAI,0BAA0B,KAAK;AAAA,EAC7E;AACA,SAAO;AACT;AAEO,SAAS,6BAA6B,MAAwD;AACnG,SAAO,0BAA0B,IAAI;AACvC;AAEA,SAAS,wBAAwB,OAA6D;AAC5F,MAAI,SAAS,KAAM,QAAO;AAC1B,QAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AACpD,QAAM,aAAa,oBAAI,IAAmB;AAC1C,aAAW,OAAO,QAAQ;AACxB,QAAI,OAAO,KAAM;AACjB,UAAM,UAAU,OAAO,GAAG,EAAE,KAAK;AACjC,QAAI,CAAC,SAAS;AACZ,iBAAW,IAAI,IAAI;AAAA,IACrB,OAAO;AACL,iBAAW,IAAI,OAAO;AAAA,IACxB;AAAA,EACF;AACA,SAAO,WAAW,OAAO,aAAa;AACxC;AAEA,eAAsB,iCAAiC,MAOf;AACtC,QAAM,MAAkC,CAAC;AACzC,QAAM,UAAU,OAAO,QAAQ,KAAK,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,KAAK,CAAC;AAC9E,MAAI,CAAC,QAAQ,OAAQ,QAAO;AAE5B,QAAM,eAAe,MAAM,QAAQ,KAAK,SAAS,KAAK,KAAK,UAAU,SACjE,KAAK,YACL,KAAK,WACH,CAAC,KAAK,QAAQ,IACd,CAAC;AACP,MAAI,CAAC,aAAa,OAAQ,QAAO;AAGjC,QAAM,OAAO,MAAM,KAAK,GAAG,KAAK,gBAAgB;AAAA,IAC9C,UAAU,EAAE,KAAK,aAAoB;AAAA,IACrC,UAAU;AAAA,IACV,MAAM;AAAA,MACJ,EAAE,KAAK,CAAE,EAAE,UAAU,KAAK,SAAgB,GAAG,EAAE,UAAU,KAAK,CAAE,EAAE;AAAA,IACpE;AAAA,EACF,CAAC;AACD,QAAM,iBAAiB,wBAAwB,KAAK,QAAQ;AAC5D,QAAM,QAAQ,oBAAI,IAAoB;AACtC,eAAa,IAAI,MAAM,EAAE,QAAQ,CAAC,IAAI,UAAU,MAAM,IAAI,IAAI,KAAK,CAAC;AACpE,QAAM,QAA6E,CAAC;AACpF,aAAW,KAAK,MAAM;AACpB,QAAI,gBAAgB;AAClB,YAAM,YAAY,MAAM,QAAQ,EAAE,YAAY,SAAS,IACnD,EAAE,WAAW,UACV,OAAO,CAAC,UAAoC,OAAO,UAAU,QAAQ,EACrE,IAAI,CAAC,UAAkB,MAAM,KAAK,CAAC,EACnC,OAAO,CAAC,UAAkB,MAAM,SAAS,CAAC,IAC7C,CAAC;AACL,YAAM,cAAc,OAAO,EAAE,YAAY,aAAa,WAAW,EAAE,WAAW,SAAS,KAAK,IAAI;AAChG,YAAM,qBAAqB,YAAY,SAAS,cAAc;AAC9D,YAAM,UAAU,UAAU,SAAS,IAC/B,UAAU,KAAK,CAAC,UAAkB,eAAe,IAAI,KAAK,CAAC,IAC3D,eAAe,IAAI,kBAAkB;AACzC,UAAI,CAAC,QAAS;AAAA,IAChB;AACA,UAAM,MAAM,EAAE;AACd,UAAM,WAAW,OAAO,EAAE,QAAQ;AAClC,UAAM,UAAU,MAAM,GAAG;AACzB,UAAM,UAAU,MAAM,IAAI,QAAQ,KAAK,OAAO;AAC9C,QAAI,CAAC,SAAS;AACZ,YAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,OAAO,QAAS,EAAU,YAAY,KAAK,GAAG,SAAS;AACpF;AAAA,IACF;AACA,UAAM,UAAU,MAAM,IAAI,QAAQ,QAAQ,KAAK,OAAO;AACtD,QAAI,UAAU,SAAS;AACrB,YAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,OAAO,QAAS,EAAU,YAAY,KAAK,GAAG,SAAS;AAAA,IACtF;AAAA,EACF;AAEA,QAAM,SAAS,CAAC,MAAc,MAAe;AAC3C,QAAI,KAAK,KAAM,QAAO;AACtB,YAAQ,MAAM;AAAA,MACZ,KAAK;AAAW,eAAO,OAAO,SAAS,OAAO,CAAC,GAAG,EAAE;AAAA,MACpD,KAAK;AAAS,eAAO,OAAO,WAAW,OAAO,CAAC,CAAC;AAAA,MAChD,KAAK;AAAW,eAAO,kBAAkB,OAAO,CAAC,CAAC,MAAM;AAAA,MACxD,KAAK;AAAA,MACL,KAAK;AAAY,eAAO,OAAO,CAAC;AAAA,MAChC;AAAS,eAAO,OAAO,CAAC;AAAA,IAC1B;AAAA,EACF;AAEA,aAAW,CAAC,QAAQ,MAAM,KAAK,SAAS;AACtC,UAAM,OAAO,OAAO,SAAS,IAAI;AACjC,UAAM,MAAM,OAAO,OAAO,QAAQ,QAAQ,EAAE,EAAE,QAAQ,OAAO,EAAE,IAAI,OAAO,QAAQ,QAAQ,EAAE;AAC5F,UAAM,MAAM,MAAM,GAAG;AACrB,UAAM,UAAU,MAAM,GAAG;AACzB,QAAI,CAAC,IAAK;AACV,QAAI,MAAM;AACR,YAAM,OAAO,MAAM,QAAQ,MAAM,IAC5B,SACD,OAAO,MAAM,EACV,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AACrB,UAAI,KAAK,OAAQ,KAAI,OAAO,IAAI,EAAE,KAAK,KAAK,IAAI,CAAC,MAAM,OAAO,IAAI,MAAM,CAAC,CAAC,EAAuC;AAAA,IACnH,OAAO;AACL,UAAI,OAAO,IAAI,OAAO,IAAI,MAAM,MAAM;AAAA,IACxC;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,wBAAwB,KAAuC;AAC7E,QAAM,OAAgC,CAAC;AACvC,QAAM,SAAkC,CAAC;AACzC,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO,EAAE,MAAM,OAAO;AAC3D,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAA8B,GAAG;AACzE,QAAI,QAAQ,gBAAgB;AAC1B,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,cAAM,QAAQ,CAAC,UAAU;AACvB,cAAI,CAAC,SAAS,OAAO,UAAU,SAAU;AACzC,gBAAM,WAAW,OAAQ,MAAc,QAAQ,WAAY,MAAc,IAAI,KAAK,IAAI;AACtF,cAAI,CAAC,SAAU;AACf,iBAAO,QAAQ,IAAK,MAAc;AAAA,QACpC,CAAC;AACD;AAAA,MACF;AACA,UAAI,SAAS,OAAO,UAAU,UAAU;AACtC,mBAAW,CAAC,IAAI,EAAE,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACvE,gBAAM,gBAAgB,OAAO,OAAO,WAAW,GAAG,KAAK,IAAI;AAC3D,cAAI,CAAC,cAAe;AACpB,iBAAO,aAAa,IAAI;AAAA,QAC1B;AACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,QAAQ,kBAAkB,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AACzF,iBAAW,CAAC,IAAI,EAAE,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACvE,eAAO,OAAO,EAAE,CAAC,IAAI;AAAA,MACvB;AACA;AAAA,IACF;AACA,QAAI,IAAI,WAAW,KAAK,GAAG;AACzB,aAAO,IAAI,MAAM,CAAC,CAAC,IAAI;AACvB;AAAA,IACF;AACA,QAAI,IAAI,WAAW,KAAK,GAAG;AACzB,aAAO,IAAI,MAAM,CAAC,CAAC,IAAI;AACvB;AAAA,IACF;AACA,SAAK,GAAG,IAAI;AAAA,EACd;AACA,SAAO,EAAE,MAAM,OAAO;AACxB;AAEO,SAAS,oCAAoC,KAAuD;AACzG,SAAO,wBAAwB,GAAG,EAAE;AACtC;AAEA,SAAS,uBAAuB,KAAsB;AACpD,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,UAAU,IAAI,KAAK;AACzB,SAAO,QAAQ,SAAS,QAAQ,YAAY,IAAI;AAClD;AAEA,SAAS,0BAA0B,KAAmC;AACpE,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,MAAI,OAAO,QAAQ,UAAU;AAC3B,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAI,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GAAG;AAClE,eAAO,EAAE,GAAI,OAA+B;AAAA,MAC9C;AACA,aAAO,CAAC;AAAA,IACV,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AACA,MAAI,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,GAAG;AAClD,WAAO,EAAE,GAAI,IAA4B;AAAA,EAC3C;AACA,SAAO,CAAC;AACV;AAEA,SAAS,oBAAoB,KAA0D;AACrF,QAAM,gBAAgB,uBAAuB,IAAI,GAAG;AACpD,MAAI,CAAC,cAAe,QAAO;AAC3B,QAAM,MAAM,0BAA2B,IAAY,UAAU;AAC7D,QAAM,QACJ,OAAO,IAAI,UAAU,YAAY,IAAI,MAAM,KAAK,EAAE,SAC9C,IAAI,MAAM,KAAK,IACf,IAAI;AACV,QAAM,eACJ,OAAO,IAAI,iBAAiB,YAAY,IAAI,aAAa,KAAK,EAAE,SAC5D,IAAI,aAAa,KAAK,IACtB;AACN,QAAM,QACJ,IAAI,UAAU,SAAY,QAAQ,IAAI,KAAK,IAAI;AACjD,QAAM,WACJ,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW;AACpD,QAAM,YACJ,IAAI,qBAAqB,OACrB,IAAI,UAAU,QAAQ,IACtB,IAAI,KAAK,IAAI,SAAgB,EAAE,QAAQ;AAC7C,SAAO;AAAA,IACL,KAAK,IAAI;AAAA,IACT;AAAA,IACA,MAAM,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;AAAA,IAChD;AAAA,IACA;AAAA,IACA,gBAAgB,IAAI,kBAAkB;AAAA,IACtC,UAAU,IAAI,YAAY;AAAA,IAC1B;AAAA,IACA,WAAW,OAAO,MAAM,SAAS,IAAI,IAAI;AAAA,EAC3C;AACF;AAEA,SAAS,wBAAwB,MAAsE;AACrG,SAAO,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM;AAC9B,UAAM,gBAAgB,EAAE,YAAY,MAAM,EAAE,YAAY;AACxD,QAAI,iBAAiB,EAAG,QAAO;AAC/B,UAAM,eAAe,EAAE,aAAa,MAAM,EAAE,aAAa;AACzD,QAAI,gBAAgB,EAAG,QAAO;AAC9B,WAAO,EAAE,IAAI,cAAc,EAAE,GAAG;AAAA,EAClC,CAAC;AACH;AAEA,SAAS,0BACP,MACA,gBACA,UACqC;AACrC,MAAI,CAAC,KAAK,OAAQ,QAAO;AACzB,QAAM,oBAAoB,KAAK;AAAA,IAC7B,CAAC,QAAQ,IAAI,kBAAkB,kBAAkB,IAAI,mBAAmB;AAAA,EAC1E;AACA,MAAI,kBAAkB,OAAQ,QAAO,wBAAwB,iBAAiB,EAAE,CAAC;AACjF,QAAM,uBAAuB,KAAK;AAAA,IAChC,CAAC,QAAQ,IAAI,YAAY,YAAY,IAAI,aAAa,YAAY,CAAC,IAAI;AAAA,EACzE;AACA,MAAI,qBAAqB,OAAQ,QAAO,wBAAwB,oBAAoB,EAAE,CAAC;AACvF,QAAM,SAAS,KAAK,OAAO,CAAC,QAAQ,CAAC,IAAI,cAAc;AACvD,MAAI,OAAO,OAAQ,QAAO,wBAAwB,MAAM,EAAE,CAAC;AAC3D,SAAO,wBAAwB,IAAI,EAAE,CAAC,KAAK;AAC7C;AAgBA,MAAM,gCAAgC;AAOtC,MAAM,8BAA8B;AAEpC,SAAS,8BAAsC;AAC7C,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,QAAQ,OAAW,QAAO;AAC9B,QAAM,SAAS,OAAO,GAAG;AACzB,MAAI,CAAC,OAAO,SAAS,MAAM,KAAK,SAAS,EAAG,QAAO;AACnD,SAAO;AACT;AAEA,SAAS,wBAAwB,MAKtB;AACT,QAAM,SAAS,KAAK,YAAY;AAChC,QAAM,WAAW,KAAK,UAAU,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG;AACvD,QAAM,OAAO,KAAK,gBAAgB,SAAS,KAAK,gBAAgB,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;AAC3F,QAAM,WAAW,KAAK,eAAe;AACrC,SAAO,GAAG,6BAA6B,IAAI,MAAM,IAAI,QAAQ,IAAI,IAAI,IAAI,QAAQ;AACnF;AAEA,SAAS,yBAAyB,MAGrB;AACX,QAAM,SAAS,KAAK,YAAY;AAChC,QAAM,UAAU,wBAAwB,MAAM;AAC9C,QAAM,OAAO,oBAAI,IAAY,CAAC,OAAO,CAAC;AACtC,aAAW,YAAY,KAAK,WAAW;AACrC,SAAK,IAAI,GAAG,OAAO,WAAW,QAAQ,EAAE;AAAA,EAC1C;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,qBAAqB,OAA4D;AACxF,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAM,UAAU,MACb,IAAI,CAAC,UAAW,OAAO,UAAU,WAAW,MAAM,KAAK,IAAI,EAAG,EAC9D,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AACrC,QAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,WAAO,QAAQ,KAAK,EAAE,KAAK,GAAG;AAAA,EAChC;AACA,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;AAEA,SAAS,yBAAyB,OAAoF;AACpH,SAAO,MAAM,KAAK,MAAM,QAAQ,CAAC;AACnC;AAEA,SAAS,yBAAyB,OAAmD;AACnF,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO;AAClC,QAAM,MAAkC,oBAAI,IAAI;AAChD,aAAW,SAAS,OAAO;AACzB,QAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,EAAG,QAAO;AACxD,UAAM,CAAC,KAAK,SAAS,IAAI;AACzB,QAAI,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,SAAS,EAAG,QAAO;AACjE,QAAI,IAAI,KAAK,SAA2C;AAAA,EAC1D;AACA,SAAO;AACT;AAIA,MAAM,+BAA+B,oBAAI,QAAyD;AAI3F,SAAS,gCAAgC,OAAwD;AACtG,MAAI,SAAS,6BAA6B,IAAI,KAAK;AACnD,MAAI,CAAC,QAAQ;AACX,aAAS,oBAAI,IAAI;AACjB,iCAA6B,IAAI,OAAO,MAAM;AAAA,EAChD;AACA,SAAO;AACT;AAEA,eAAe,oCACb,MACqC;AACrC,QAAM,EAAE,IAAI,WAAW,cAAc,IAAI;AACzC,QAAM,WAAW,KAAK,YAAY;AAClC,QAAM,eAA0C;AAAA,IAC9C,WACI,EAAE,KAAK,CAAC,EAAE,SAA0B,GAAG,EAAE,UAAU,KAAK,CAAC,EAAE,IAC3D,EAAE,UAAU,KAAK;AAAA,EACvB;AACA,MAAI,cAAc,QAAQ;AACxB,iBAAa,KAAK;AAAA,MAChB,KAAK,CAAC,EAAE,gBAAgB,EAAE,KAAK,cAAqB,EAAE,GAAG,EAAE,gBAAgB,KAAK,CAAC;AAAA,IACnF,CAAC;AAAA,EACH,OAAO;AACL,iBAAa,KAAK,EAAE,gBAAgB,KAAK,CAAC;AAAA,EAC5C;AACA,QAAM,QAAiC;AAAA,IACrC,UAAU,EAAE,KAAK,UAAiB;AAAA,IAClC,WAAW;AAAA,IACX,UAAU;AAAA,IACV,MAAM;AAAA,EACR;AACA,QAAM,OAAO,MAAM,GAAG,KAAK,gBAAgB,KAAY;AACvD,QAAM,iBAAiB,wBAAwB,KAAK,QAAQ;AAC5D,QAAM,QAAoC,oBAAI,IAAI;AAClD,OAAK,QAAQ,CAAC,QAAQ;AACpB,QAAI,gBAAgB;AAClB,YAAM,SAAS,0BAA2B,IAAY,UAAU;AAChE,YAAM,YAAY,MAAM,QAAQ,OAAO,SAAS,IAC5C,OAAO,UACJ,OAAO,CAAC,UAAoC,OAAO,UAAU,QAAQ,EACrE,IAAI,CAAC,UAAkB,MAAM,KAAK,CAAC,EACnC,OAAO,CAAC,UAAkB,MAAM,SAAS,CAAC,IAC7C,CAAC;AACL,YAAM,WAAW,OAAO,OAAO,aAAa,YAAY,OAAO,SAAS,KAAK,EAAE,SAAS,IACpF,OAAO,SAAS,KAAK,IACrB;AACJ,YAAM,UAAU,UAAU,SAAS,IAC/B,UAAU,KAAK,CAAC,UAAkB,eAAe,IAAI,KAAK,CAAC,IAC3D,eAAe,IAAI,QAAQ;AAC/B,UAAI,CAAC,QAAS;AAAA,IAChB;AACA,UAAM,UAAU,oBAAoB,GAAG;AACvC,QAAI,CAAC,QAAS;AACd,UAAM,gBAAgB,uBAAuB,QAAQ,GAAG;AACxD,QAAI,CAAC,cAAe;AACpB,QAAI,CAAC,MAAM,IAAI,aAAa,EAAG,OAAM,IAAI,eAAe,CAAC,CAAC;AAC1D,UAAM,IAAI,aAAa,EAAG,KAAK,OAAO;AAAA,EACxC,CAAC;AACD,QAAM,QAAQ,CAAC,SAAS,QAAQ;AAC9B,UAAM,IAAI,KAAK,wBAAwB,OAAO,CAAC;AAAA,EACjD,CAAC;AACD,SAAO;AACT;AAEA,eAAsB,+BAA+B,MAGb;AACtC,QAAM,OAAO,MAAM,QAAQ,KAAK,SAAS,IAAI,KAAK,YAAY,CAAC,KAAK,SAAS;AAC7E,QAAM,YAAY,KACf,IAAI,CAAC,OAAQ,OAAO,OAAO,WAAW,GAAG,KAAK,IAAI,OAAO,MAAM,EAAE,CAAE,EACnE,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;AAC/B,MAAI,CAAC,UAAU,OAAQ,QAAO,oBAAI,IAAI;AACtC,QAAM,WAAW,KAAK,YAAY;AAClC,QAAM,gBAAgB,MAAM,QAAQ,KAAK,eAAe,IACpD,KAAK,gBACF,IAAI,CAAC,OAAQ,OAAO,OAAO,WAAW,GAAG,KAAK,IAAI,EAAG,EACrD,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC,IACvE,CAAC;AAEL,QAAM,cAAc,qBAAqB,KAAK,QAAQ;AACtD,QAAM,QAAQ,4BAA4B;AAC1C,QAAM,WAAW,wBAAwB;AAAA,IACvC;AAAA,IACA;AAAA,IACA,iBAAiB;AAAA,IACjB;AAAA,EACF,CAAC;AAED,QAAM,gBAAgB,KAAK,eACvB,gCAAgC,KAAK,YAAY,IACjD;AACJ,MAAI,eAAe;AACjB,UAAM,SAAS,cAAc,IAAI,QAAQ;AACzC,QAAI,OAAQ,QAAO;AAAA,EACrB;AAEA,QAAM,cAAc,QAAQ,IAAI,KAAK,SAAS,OAAO;AACrD,MAAI,eAAe,OAAO,YAAY,QAAQ,YAAY;AACxD,QAAI;AACF,YAAM,SAAS,MAAM,YAAY,IAAI,QAAQ;AAC7C,YAAM,WAAW,yBAAyB,MAAM;AAChD,UAAI,UAAU;AACZ,YAAI,cAAe,eAAc,IAAI,UAAU,QAAQ;AACvD,eAAO;AAAA,MACT;AAAA,IACF,SAAS,KAAK;AACZ,cAAQ,KAAK,mCAAmC,GAAG;AAAA,IACrD;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM,oCAAoC;AAAA,IACtD,GAAG;AAAA,IACH;AAAA,IACA;AAAA,EACF,CAAC;AAED,MAAI,eAAe,OAAO,YAAY,QAAQ,YAAY;AACxD,QAAI;AACF,YAAM,YAAY,IAAI,UAAU,yBAAyB,KAAK,GAAG;AAAA,QAC/D,KAAK;AAAA,QACL,MAAM,yBAAyB,EAAE,UAAU,UAAU,CAAC;AAAA,MACxD,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,cAAQ,KAAK,oCAAoC,GAAG;AAAA,IACtD;AAAA,EACF;AACA,MAAI,cAAe,eAAc,IAAI,UAAU,KAAK;AACpD,SAAO;AACT;AAYO,SAAS,+BACd,QACA,WACA,UAAiD,CAAC,GACzB;AACzB,QAAM,oBAAoB,QAAQ,sBAAsB;AACxD,QAAM,OAAgC,CAAC;AACvC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,QAAI,sBAAsB,IAAI,WAAW,KAAK,KAAK,IAAI,WAAW,KAAK,GAAI;AAC3E,SAAK,GAAG,IAAI;AAAA,EACd;AACA,OAAK,eAAe,UAAU;AAC9B,OAAK,eAAe,UAAU;AAC9B,SAAO;AACT;AAEO,SAAS,+BACd,QACA,aACA,UAGI,CAAC,GACsB;AAC3B,QAAM,aAAa,6BAA6B,MAAM;AACtD,MAAI,CAAC,OAAO,KAAK,UAAU,EAAE,QAAQ;AACnC,WAAO,EAAE,cAAc,MAAM,cAAc,CAAC,EAAE;AAAA,EAChD;AACA,QAAM,SAAkC,CAAC;AACzC,QAAM,UAA0F,CAAC;AACjG,QAAM,iBAAiB,QAAQ,kBAAkB;AACjD,QAAM,WAAW,QAAQ,YAAY;AAErC,SAAO,QAAQ,UAAU,EAAE,QAAQ,CAAC,CAAC,aAAa,KAAK,MAAM;AAC3D,UAAM,UAAU,YAAY,QAAQ,QAAQ,EAAE;AAC9C,UAAM,gBAAgB,uBAAuB,OAAO;AACpD,QAAI,CAAC,cAAe;AACpB,UAAM,aAAa,YAAY,IAAI,aAAa,KAAK,CAAC;AACtD,UAAM,cAAc,0BAA0B,YAAY,gBAAgB,QAAQ;AAElF,QAAI,CAAC,YAAa;AAClB,WAAO,OAAO,IAAI;AAClB,UAAM,QAAiC;AAAA,MACrC,KAAK;AAAA,MACL,OAAO,YAAY,SAAS;AAAA,MAC5B;AAAA,MACA,MAAM,YAAY,QAAQ;AAAA,MAC1B,OAAO,YAAY,SAAS,MAAM,QAAQ,KAAK;AAAA,IACjD;AACA,YAAQ,KAAK;AAAA,MACX;AAAA,MACA,UAAU,YAAY,YAAY,OAAO;AAAA,MACzC,WAAW,YAAY,aAAa;AAAA,IACtC,CAAC;AAAA,EACH,CAAC;AAED,QAAM,UAAU,QACb,KAAK,CAAC,GAAG,MAAM;AACd,UAAM,eAAe,EAAE,WAAW,EAAE;AACpC,QAAI,iBAAiB,EAAG,QAAO;AAC/B,UAAM,cAAc,EAAE,YAAY,EAAE;AACpC,QAAI,gBAAgB,EAAG,QAAO;AAC9B,WAAO,EAAE,MAAM,IAAI,cAAc,EAAE,MAAM,GAAG;AAAA,EAC9C,CAAC,EACA,IAAI,CAAC,SAAS,KAAK,KAAK;AAE3B,SAAO;AAAA,IACL,cAAc,OAAO,KAAK,MAAM,EAAE,SAAS,SAAS;AAAA,IACpD,cAAc;AAAA,EAChB;AACF;AAEA,eAAsB,sBAAsB,MAQS;AACnD,QAAM,EAAE,IAAI,UAAU,UAAU,IAAI;AACpC,MAAI,CAAC,MAAM,QAAQ,SAAS,KAAK,UAAU,WAAW,EAAG,QAAO,CAAC;AAEjE,QAAM,sBAAsB,UAAU,IAAI,CAAC,OAAO,OAAO,EAAE,CAAC;AAC5D,MAAI;AACJ,QAAM,kBAAkB,oBAAI,IAAkC;AAC9D,QAAM,uBAAuB,MAAM;AACjC,QAAI,sBAAsB,OAAW,QAAO;AAC5C,wBAAoB,+BAA+B,IAAI,KAAK,iBAAiB;AAC7E,WAAO;AAAA,EACT;AACA,QAAM,mBAAmB,oBAAI,IAAmB;AAChD,mBAAiB,IAAI,IAAI;AACzB,MAAI,KAAK,kBAAkB;AACzB,eAAW,OAAO,OAAO,OAAO,KAAK,gBAAgB,GAAG;AACtD,uBAAiB,IAAI,MAAM,OAAO,GAAG,IAAI,IAAI;AAAA,IAC/C;AAAA,EACF;AACA,MAAI,KAAK,iBAAiB;AACxB,eAAW,OAAO,KAAK,gBAAiB,kBAAiB,IAAI,MAAM,OAAO,GAAG,IAAI,IAAI;AAAA,EACvF;AACA,QAAM,kBAAkB,KAAK,mBAAmB,CAAC,GAAG,KAAK,CAAC,MAAM,KAAK,IAAI,KAAK;AAE9E,QAAM,aAAa,MAAM,KAAK,gBAAgB;AAC9C,QAAM,gBAAgB,WAAW,OAAO,CAAC,MAAmB,MAAM,IAAI;AACtE,QAAM,eAAe,cAAc,SAC/B,EAAE,UAAU,EAAE,KAAK,CAAC,GAAG,eAAe,IAAI,EAAS,EAAE,IACrD,EAAE,UAAU,KAAK;AACrB,QAAM,SAAS,MAAM,GAAG,KAAK,kBAAkB;AAAA,IAC7C;AAAA,IACA,UAAU,EAAE,KAAK,oBAA2B;AAAA,IAC5C,WAAW;AAAA,IACX,GAAI,WAAW,SAAS,eAAe,CAAC;AAAA,EAC1C,CAAC;AAED,MAAI,CAAC,OAAO,OAAQ,QAAO,CAAC;AAE5B,QAAM,UAAU,MAAM,KAAK,IAAI,IAAI,OAAO,IAAI,CAAC,QAAQ,OAAO,IAAI,QAAQ,CAAC,CAAC,CAAC;AAC7E,QAAM,yBAAyB,oBAAI,IAAmB;AACtD,yBAAuB,IAAI,IAAI;AAC/B,MAAI,KAAK,wBAAwB;AAC/B,eAAW,OAAO,OAAO,OAAO,KAAK,sBAAsB,GAAG;AAC5D,6BAAuB,IAAI,MAAM,OAAO,GAAG,IAAI,IAAI;AAAA,IACrD;AAAA,EACF;AACA,aAAW,OAAO,QAAQ;AACxB,2BAAuB,IAAI,IAAI,iBAAiB,OAAO,IAAI,cAAc,IAAI,IAAI;AAAA,EACnF;AACA,QAAM,UAAU,MAAM,KAAK,sBAAsB;AAEjD,QAAM,OAAO,QAAQ,SACjB,MAAM,GAAG,KAAK,gBAAgB;AAAA,IAC5B;AAAA,IACA,KAAK,EAAE,KAAK,QAAe;AAAA,IAC3B,WAAW;AAAA,IACX,UAAU;AAAA,IACV,GAAI,WAAW,SAAS,EAAE,UAAU,aAAa,SAAS,IAAI,CAAC;AAAA,IAC/D,gBAAgB,EAAE,KAAK,QAAe;AAAA,EACxC,CAAC,IACD,CAAC;AAEL,QAAM,YAAY,oBAAI,IAA8B;AACpD,aAAW,OAAO,MAAM;AACtB,UAAM,OAAO,UAAU,IAAI,IAAI,GAAG,KAAK,CAAC;AACxC,SAAK,KAAK,GAAG;AACb,cAAU,IAAI,IAAI,KAAK,IAAI;AAAA,EAC7B;AAEA,QAAM,iBAAiB,CAAC,UAAkB,gBAA+B,aAA4B;AACnG,UAAM,aAAa,UAAU,IAAI,QAAQ;AACzC,QAAI,CAAC,cAAc,WAAW,WAAW,EAAG,QAAO;AACnD,UAAM,SAAS,WAAW,OAAO,CAAC,QAAQ,IAAI,aAAa,SAAS,CAAC,IAAI,SAAS;AAClF,UAAM,OAAO,OAAO,SAAS,SAAS;AACtC,QAAI,kBAAkB,UAAU;AAC9B,YAAM,QAAQ,KAAK,KAAK,CAAC,QAAQ,IAAI,mBAAmB,kBAAkB,IAAI,aAAa,QAAQ;AACnG,UAAI,MAAO,QAAO;AAAA,IACpB;AACA,QAAI,gBAAgB;AAClB,YAAM,WAAW,KAAK,KAAK,CAAC,QAAQ,IAAI,mBAAmB,mBAAmB,CAAC,YAAY,IAAI,YAAY,QAAQ,IAAI,aAAa,SAAS;AAC7I,UAAI,SAAU,QAAO;AAAA,IACvB;AACA,QAAI,UAAU;AACZ,YAAM,cAAc,KAAK,KAAK,CAAC,QAAQ,IAAI,kBAAkB,QAAQ,IAAI,aAAa,QAAQ;AAC9F,UAAI,YAAa,QAAO;AAAA,IAC1B;AACA,UAAM,SAAS,KAAK,KAAK,CAAC,QAAQ,IAAI,kBAAkB,QAAQ,IAAI,YAAY,IAAI;AACpF,WAAO,UAAU,KAAK,CAAC;AAAA,EACzB;AAEA,QAAM,eAAe,CAAC,QAAmC;AACvD,QAAI,IAAI,mBAAmB,QAAQ,IAAI,mBAAmB,OAAW,QAAO,IAAI;AAChF,QAAI,IAAI,cAAc,QAAQ,IAAI,cAAc,OAAW,QAAO,IAAI;AACtE,QAAI,IAAI,aAAa,QAAQ,IAAI,aAAa,OAAW,QAAO,IAAI;AACpE,QAAI,IAAI,eAAe,QAAQ,IAAI,eAAe,OAAW,QAAO,IAAI;AACxE,QAAI,IAAI,cAAc,QAAQ,IAAI,cAAc,OAAW,QAAO,IAAI;AACtE,WAAO;AAAA,EACT;AAGA,QAAM,UAAU,oBAAI,IAAoB;AAExC,aAAW,OAAO,QAAQ;AACxB,UAAM,WAAW,OAAO,IAAI,QAAQ;AACpC,UAAM,MAAM,OAAO,IAAI,QAAQ;AAC/B,UAAM,YAAY,GAAG,QAAQ,KAAK,GAAG;AACrC,UAAM,QAAQ,IAAI,iBAAiB,OAAO,IAAI,cAAc,IAAI;AAChE,UAAM,WAAW,IAAI,WAAW,OAAO,IAAI,QAAQ,IAAI;AACvD,UAAM,gBAAgB,UAAU,KAAK,yBAAyB,QAAQ,KAAK;AAC3E,UAAM,mBAAmB,aAAa,KAAK,mBAAmB,QAAQ,KAAK;AAC3E,UAAM,MAAM,eAAe,KAAK,eAAe,gBAAgB;AAC/D,UAAM,YAAY,QAAQ,KAAK,cAAe,IAAY,YAAY,SAAS;AAC/E,UAAM,QAAQ,aAAa,GAAG;AAC9B,UAAM,YAAY,YACd,MAAM;AAAA,MACJ;AAAA,MACA,oBAAoB,YAAY;AAAA,MAChC,qBAAqB;AAAA,MACrB;AAAA,MACA,EAAE,MAAM,KAAK,QAAQ,KAAK;AAAA,IAC5B,IACA;AACJ,UAAM,WAAW,QAAQ,IAAI,SAAS;AACtC,QAAI,UAAU;AACZ,UAAI,SAAS,SAAS,QAAQ,cAAe,UAAS,QAAQ;AAC9D,UAAI,SAAS,YAAY,QAAQ,iBAAkB,UAAS,WAAW;AACvE,UAAI,SAAS,OAAO,QAAQ,IAAK,UAAS,MAAM;AAChD,eAAS,YAAY,SAAS,aAAa;AAC3C,eAAS,OAAO,KAAK,SAAS;AAAA,IAChC,OAAO;AACL,cAAQ,IAAI,WAAW,EAAE,OAAO,eAAe,UAAU,kBAAkB,QAAQ,CAAC,SAAS,GAAG,KAAK,OAAO,MAAM,UAAU,CAAC;AAAA,IAC/H;AAAA,EACF;AAEA,QAAM,SAAkD,CAAC;AACzD,aAAW,CAAC,aAAa,MAAM,KAAK,QAAQ,QAAQ,GAAG;AACrD,UAAM,CAAC,UAAU,QAAQ,IAAI,YAAY,MAAM,IAAI;AACnD,QAAI,CAAC,OAAO,QAAQ,EAAG,QAAO,QAAQ,IAAI,CAAC;AAC3C,UAAM,WAAW,MAAM,QAAQ;AAC/B,UAAM,MAAM,OAAO,OAAO,eAAe,UAAU,OAAO,UAAU,KAAK,yBAAyB,QAAQ,KAAK,OAAO,OAAO,aAAa,KAAK,mBAAmB,QAAQ,KAAK,KAAK;AACpL,QAAI,OAAO,IAAI,cAAc,OAAO,IAAI,eAAe,YAAa,IAAI,WAAmB,OAAO;AAChG,YAAM,UAAU,OAAO,OAAO,OAAO,CAAC,MAAM,MAAM,UAAa,MAAM,IAAI;AACzE,aAAO,QAAQ,EAAE,QAAQ,IAAI;AAAA,IAC/B,WAAW,OAAO,OAAO,SAAS,GAAG;AACnC,YAAM,UAAU,OAAO,OAAO,OAAO,CAAC,MAAM,MAAM,MAAS;AAC3D,aAAO,QAAQ,EAAE,QAAQ,IAAI;AAAA,IAC/B,OAAO;AACL,aAAO,QAAQ,EAAE,QAAQ,IAAI,OAAO,OAAO,CAAC,KAAK;AAAA,IACnD;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,sBAAsB,QAAsD;AAC1F,QAAM,UAAU,6BAA6B,MAAM;AACnD,QAAM,SAAS,OAAO;AAAA,IACpB,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,aAAa,KAAK,MAAM;AAAA,MACpD,YAAY,QAAQ,QAAQ,EAAE;AAAA,MAC9B;AAAA,IACF,CAAC;AAAA,EACH;AACA,QAAM,eAAe,OAAO,KAAK,MAAM,EAAE,SAAS,SAAS;AAC3D,QAAM,eAAe,OAAO,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO;AAAA,IACjE;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA,MAAM;AAAA,IACN,OAAO,MAAM,QAAQ,KAAK;AAAA,EAC5B,EAAE;AACF,SAAO,EAAE,SAAS,cAAc,aAAa;AAC/C;",
4
+ "sourcesContent": ["import type { CustomFieldSet, EntityId } from '@open-mercato/shared/modules/entities'\nimport type { EntityManager } from '@mikro-orm/core'\nimport { CustomFieldDef, CustomFieldValue } from '@open-mercato/core/modules/entities/data/entities'\nimport type { WhereValue } from '@open-mercato/shared/lib/query/types'\nimport type { TenantDataEncryptionService } from '../encryption/tenantDataEncryptionService'\nimport { decryptCustomFieldValue, resolveTenantEncryptionService } from '../encryption/customFieldValues'\nimport { parseBooleanToken } from '../boolean'\nimport { extractCustomFieldEntries } from './custom-fields-client'\nimport {\n buildCustomFieldDefinitionIndexFromRows,\n normalizeDefinitionKey,\n normalizeFieldsetFilter,\n selectDefinitionForRecord,\n type CustomFieldDefinitionIndex,\n type CustomFieldDefinitionRow,\n type CustomFieldDefinitionSummary,\n} from './custom-field-definition-index'\n\nexport type { CustomFieldDefinitionSummary, CustomFieldDefinitionIndex } from './custom-field-definition-index'\n\nexport type CustomFieldSelectors = {\n keys: string[]\n selectors: string[] // e.g. ['cf:priority', 'cf:severity']\n outputKeys: string[] // e.g. ['cf_priority', 'cf_severity']\n}\n\nexport type SplitCustomFieldPayload = {\n base: Record<string, unknown>\n custom: Record<string, unknown>\n}\n\nexport type CustomFieldDisplayEntry = {\n key: string\n label: string | null\n value: unknown\n kind: string | null\n multi: boolean\n}\n\nexport type CustomFieldDisplayPayload = {\n customValues: Record<string, unknown> | null\n customFields: CustomFieldDisplayEntry[]\n}\n\nexport type CustomFieldSnapshot = {\n entries: Record<string, unknown>\n customValues: Record<string, unknown> | null\n customFields: CustomFieldDisplayEntry[]\n}\n\nexport function buildCustomFieldSelectorsForEntity(entityId: EntityId, sets: CustomFieldSet[]): CustomFieldSelectors {\n const keys = Array.from(new Set(\n (sets || [])\n .filter((s) => s.entity === entityId)\n .flatMap((s) => (s.fields || []).map((f) => f.key))\n ))\n const selectors = keys.map((k) => `cf:${k}`)\n const outputKeys = keys.map((k) => `cf_${k}`)\n return { keys, selectors, outputKeys }\n}\n\nexport function normalizeCustomFieldValue(val: unknown): unknown {\n if (Array.isArray(val)) return val\n if (typeof val === 'string') {\n const s = val.trim()\n // Parse Postgres array-like '{a,b,c}' to string[] when present\n if (s.startsWith('{') && s.endsWith('}')) {\n const inner = s.slice(1, -1).trim()\n if (!inner) return []\n return inner.split(/[\\s,]+/).map((x) => x.trim()).filter(Boolean)\n }\n return s\n }\n return val as any\n}\n\n// Extracts cf_* fields from a record that may contain both 'cf:<key>' and/or 'cf_<key>'\nexport function extractCustomFieldsFromItem(item: Record<string, unknown>, keys: string[]): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const key of keys) {\n const colon = item[`cf:${key}` as keyof typeof item]\n const snake = item[`cf_${key}` as keyof typeof item]\n const value = colon !== undefined ? colon : snake\n if (value !== undefined) out[`cf_${key}`] = normalizeCustomFieldValue(value)\n }\n return out\n}\n\nexport function extractAllCustomFieldEntries(item: Record<string, unknown>): Record<string, unknown> {\n return extractCustomFieldEntries(item)\n}\n\nexport async function buildCustomFieldFiltersFromQuery(opts: {\n entityId?: EntityId\n entityIds?: EntityId[]\n query: Record<string, unknown>\n em: EntityManager\n tenantId: string | null | undefined\n fieldset?: string | string[] | null\n}): Promise<Record<string, WhereValue>> {\n const out: Record<string, WhereValue> = {}\n const entries = Object.entries(opts.query).filter(([k]) => k.startsWith('cf_'))\n if (!entries.length) return out\n\n const entityIdList = Array.isArray(opts.entityIds) && opts.entityIds.length\n ? opts.entityIds\n : opts.entityId\n ? [opts.entityId]\n : []\n if (!entityIdList.length) return out\n\n // Tenant-only scope: allow global (null) or tenant match; ignore organization here\n const defs = await opts.em.find(CustomFieldDef, {\n entityId: { $in: entityIdList as any },\n isActive: true,\n $and: [\n { $or: [ { tenantId: opts.tenantId as any }, { tenantId: null } ] },\n ],\n })\n const fieldsetFilter = normalizeFieldsetFilter(opts.fieldset)\n const order = new Map<string, number>()\n entityIdList.map(String).forEach((id, index) => order.set(id, index))\n const byKey: Record<string, { kind: string; multi?: boolean; entityId: string }> = {}\n for (const d of defs) {\n if (fieldsetFilter) {\n const fieldsets = Array.isArray(d.configJson?.fieldsets)\n ? d.configJson.fieldsets\n .filter((entry: unknown): entry is string => typeof entry === 'string')\n .map((entry: string) => entry.trim())\n .filter((entry: string) => entry.length > 0)\n : []\n const rawFieldset = typeof d.configJson?.fieldset === 'string' ? d.configJson.fieldset.trim() : ''\n const normalizedFieldset = rawFieldset.length ? rawFieldset : null\n const matches = fieldsets.length > 0\n ? fieldsets.some((entry: string) => fieldsetFilter.has(entry))\n : fieldsetFilter.has(normalizedFieldset)\n if (!matches) continue\n }\n const key = d.key\n const entityId = String(d.entityId)\n const current = byKey[key]\n const rankNew = order.get(entityId) ?? Number.MAX_SAFE_INTEGER\n if (!current) {\n byKey[key] = { kind: d.kind, multi: Boolean((d as any).configJson?.multi), entityId }\n continue\n }\n const rankOld = order.get(current.entityId) ?? Number.MAX_SAFE_INTEGER\n if (rankNew < rankOld) {\n byKey[key] = { kind: d.kind, multi: Boolean((d as any).configJson?.multi), entityId }\n }\n }\n\n const coerce = (kind: string, v: unknown) => {\n if (v == null) return v as undefined\n switch (kind) {\n case 'integer': return Number.parseInt(String(v), 10)\n case 'float': return Number.parseFloat(String(v))\n case 'boolean': return parseBooleanToken(String(v)) === true\n case 'date':\n case 'datetime': return String(v)\n default: return String(v)\n }\n }\n\n for (const [rawKey, rawVal] of entries) {\n const isIn = rawKey.endsWith('In')\n const key = isIn ? rawKey.replace(/^cf_/, '').replace(/In$/, '') : rawKey.replace(/^cf_/, '')\n const def = byKey[key]\n const fieldId = `cf:${key}`\n if (!def) continue\n if (isIn) {\n const list = Array.isArray(rawVal)\n ? (rawVal as unknown[])\n : String(rawVal)\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (list.length) out[fieldId] = { $in: list.map((x) => coerce(def.kind, x)) as (string[] | number[] | boolean[]) }\n } else {\n out[fieldId] = coerce(def.kind, rawVal)\n }\n }\n\n return out\n}\n\nexport function splitCustomFieldPayload(raw: unknown): SplitCustomFieldPayload {\n const base: Record<string, unknown> = {}\n const custom: Record<string, unknown> = {}\n if (!raw || typeof raw !== 'object') return { base, custom }\n for (const [key, value] of Object.entries(raw as Record<string, unknown>)) {\n if (key === 'customFields') {\n if (Array.isArray(value)) {\n value.forEach((entry) => {\n if (!entry || typeof entry !== 'object') return\n const entryKey = typeof (entry as any).key === 'string' ? (entry as any).key.trim() : ''\n if (!entryKey) return\n custom[entryKey] = (entry as any).value\n })\n continue\n }\n if (value && typeof value === 'object') {\n for (const [ck, cv] of Object.entries(value as Record<string, unknown>)) {\n const normalizedKey = typeof ck === 'string' ? ck.trim() : ''\n if (!normalizedKey) continue\n custom[normalizedKey] = cv\n }\n continue\n }\n }\n if (key === 'customValues' && value && typeof value === 'object' && !Array.isArray(value)) {\n for (const [ck, cv] of Object.entries(value as Record<string, unknown>)) {\n custom[String(ck)] = cv\n }\n continue\n }\n if (key.startsWith('cf_')) {\n custom[key.slice(3)] = value\n continue\n }\n if (key.startsWith('cf:')) {\n custom[key.slice(3)] = value\n continue\n }\n base[key] = value\n }\n return { base, custom }\n}\n\nexport function extractCustomFieldValuesFromPayload(raw: Record<string, unknown>): Record<string, unknown> {\n return splitCustomFieldPayload(raw).custom\n}\n\ntype LoadCustomFieldDefinitionIndexOptions = {\n em: EntityManager\n entityIds: string | string[]\n tenantId?: string | null | undefined\n organizationIds?: Array<string | null | undefined> | null\n fieldset?: string | string[] | null\n}\n\ntype CustomFieldDefIndexCache = {\n get(key: string): Promise<unknown> | unknown\n set(key: string, value: unknown, opts?: { ttl?: number; tags?: string[] }): Promise<unknown> | unknown\n deleteByTags?(tags: string[]): Promise<number> | number\n}\n\nconst CF_DEF_INDEX_CACHE_KEY_PREFIX = 'crud:cf-def-index'\n// Phase 2 default-off: integration runs observed `/api/customers/people`\n// returning 500 with this cache path active, and the readiness-probe\n// timeout blocked artifact upload so the direct stack trace was lost.\n// Until cross-request safety of the SQLite-cache JSON round-trip is\n// re-verified, ship with the layer disabled. Set\n// `OM_CF_DEF_CACHE_TTL_MS=300000` (or any positive integer) to opt in.\nconst CF_DEF_INDEX_DEFAULT_TTL_MS = 0\n\nfunction resolveCfDefIndexCacheTtlMs(): number {\n const raw = process.env.OM_CF_DEF_CACHE_TTL_MS\n if (raw === undefined) return CF_DEF_INDEX_DEFAULT_TTL_MS\n const parsed = Number(raw)\n if (!Number.isFinite(parsed) || parsed < 0) return CF_DEF_INDEX_DEFAULT_TTL_MS\n return parsed\n}\n\nfunction buildCfDefIndexCacheKey(opts: {\n tenantId: string | null\n entityIds: string[]\n organizationIds: string[]\n fieldsetKey: string | null\n}): string {\n const tenant = opts.tenantId ?? 'global'\n const entities = opts.entityIds.slice().sort().join('|')\n const orgs = opts.organizationIds.length ? opts.organizationIds.slice().sort().join('|') : 'none'\n const fieldset = opts.fieldsetKey ?? 'all'\n return `${CF_DEF_INDEX_CACHE_KEY_PREFIX}:${tenant}:${entities}:${orgs}:${fieldset}`\n}\n\nfunction buildCfDefIndexCacheTags(opts: {\n tenantId: string | null\n entityIds: string[]\n}): string[] {\n const tenant = opts.tenantId ?? 'global'\n const tagBase = `entities:definitions:${tenant}`\n const tags = new Set<string>([tagBase])\n for (const entityId of opts.entityIds) {\n tags.add(`${tagBase}:entity:${entityId}`)\n }\n return Array.from(tags)\n}\n\nfunction normalizeFieldsetKey(value: string | string[] | null | undefined): string | null {\n if (!value) return null\n if (Array.isArray(value)) {\n const cleaned = value\n .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))\n .filter((entry) => entry.length > 0)\n if (!cleaned.length) return null\n return cleaned.sort().join(',')\n }\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\nfunction serializableIndexFromMap(index: CustomFieldDefinitionIndex): Array<[string, CustomFieldDefinitionSummary[]]> {\n return Array.from(index.entries())\n}\n\nfunction indexMapFromSerializable(value: unknown): CustomFieldDefinitionIndex | null {\n if (!Array.isArray(value)) return null\n const map: CustomFieldDefinitionIndex = new Map()\n for (const entry of value) {\n if (!Array.isArray(entry) || entry.length !== 2) return null\n const [key, summaries] = entry as [unknown, unknown]\n if (typeof key !== 'string' || !Array.isArray(summaries)) return null\n map.set(key, summaries as CustomFieldDefinitionSummary[])\n }\n return map\n}\n\n// Per-request micro-cache. Two CRUD calls within one HTTP request (rare but\n// possible via interceptors) share the same Map keyed by ctx-like objects.\nconst requestScopedCfDefIndexCache = new WeakMap<object, Map<string, CustomFieldDefinitionIndex>>()\n\nexport type CustomFieldDefinitionIndexCacheKey = string\n\nexport function getRequestScopedCfDefIndexCache(scope: object): Map<string, CustomFieldDefinitionIndex> {\n let bucket = requestScopedCfDefIndexCache.get(scope)\n if (!bucket) {\n bucket = new Map()\n requestScopedCfDefIndexCache.set(scope, bucket)\n }\n return bucket\n}\n\nasync function loadCustomFieldDefinitionIndexFresh(\n opts: LoadCustomFieldDefinitionIndexOptions & { entityIds: string[]; orgCandidates: string[] }\n): Promise<CustomFieldDefinitionIndex> {\n const { em, entityIds, orgCandidates } = opts\n const tenantId = opts.tenantId ?? null\n const scopeClauses: Record<string, unknown>[] = [\n tenantId\n ? { $or: [{ tenantId: tenantId as any }, { tenantId: null }] }\n : { tenantId: null },\n ]\n if (orgCandidates.length) {\n scopeClauses.push({\n $or: [{ organizationId: { $in: orgCandidates as any } }, { organizationId: null }],\n })\n } else {\n scopeClauses.push({ organizationId: null })\n }\n const where: Record<string, unknown> = {\n entityId: { $in: entityIds as any },\n deletedAt: null,\n isActive: true,\n $and: scopeClauses,\n }\n const defs = await em.find(CustomFieldDef, where as any)\n const rows: CustomFieldDefinitionRow[] = defs.map((def) => ({\n key: def.key,\n entityId: String((def as any).entityId),\n kind: typeof def.kind === 'string' ? def.kind : null,\n configJson: (def as any).configJson,\n organizationId: def.organizationId ?? null,\n tenantId: def.tenantId ?? null,\n deletedAt: (def as any).deletedAt ?? null,\n updatedAt: (def as any).updatedAt ?? null,\n }))\n return buildCustomFieldDefinitionIndexFromRows(rows, {\n organizationIds: orgCandidates,\n fieldset: opts.fieldset,\n })\n}\n\nexport async function loadCustomFieldDefinitionIndex(opts: LoadCustomFieldDefinitionIndexOptions & {\n cache?: CustomFieldDefIndexCache | null\n requestScope?: object | null\n}): Promise<CustomFieldDefinitionIndex> {\n const list = Array.isArray(opts.entityIds) ? opts.entityIds : [opts.entityIds]\n const entityIds = list\n .map((id) => (typeof id === 'string' ? id.trim() : String(id ?? '')))\n .filter((id) => id.length > 0)\n if (!entityIds.length) return new Map()\n const tenantId = opts.tenantId ?? null\n const orgCandidates = Array.isArray(opts.organizationIds)\n ? opts.organizationIds\n .map((id) => (typeof id === 'string' ? id.trim() : id))\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n : []\n\n const fieldsetKey = normalizeFieldsetKey(opts.fieldset)\n const ttlMs = resolveCfDefIndexCacheTtlMs()\n const cacheKey = buildCfDefIndexCacheKey({\n tenantId,\n entityIds,\n organizationIds: orgCandidates,\n fieldsetKey,\n })\n\n const requestBucket = opts.requestScope\n ? getRequestScopedCfDefIndexCache(opts.requestScope)\n : null\n if (requestBucket) {\n const cached = requestBucket.get(cacheKey)\n if (cached) return cached\n }\n\n const sharedCache = ttlMs > 0 ? opts.cache ?? null : null\n if (sharedCache && typeof sharedCache.get === 'function') {\n try {\n const cached = await sharedCache.get(cacheKey)\n const restored = indexMapFromSerializable(cached)\n if (restored) {\n if (requestBucket) requestBucket.set(cacheKey, restored)\n return restored\n }\n } catch (err) {\n console.warn('[crud:cf-def-cache] read failed', err)\n }\n }\n\n const index = await loadCustomFieldDefinitionIndexFresh({\n ...opts,\n entityIds,\n orgCandidates,\n })\n\n if (sharedCache && typeof sharedCache.set === 'function') {\n try {\n await sharedCache.set(cacheKey, serializableIndexFromMap(index), {\n ttl: ttlMs,\n tags: buildCfDefIndexCacheTags({ tenantId, entityIds }),\n })\n } catch (err) {\n console.warn('[crud:cf-def-cache] write failed', err)\n }\n }\n if (requestBucket) requestBucket.set(cacheKey, index)\n return index\n}\n\nexport type ApplyCustomFieldsNormalizationOptions = {\n /**\n * When true, removes raw `cf_*` and `cf:*` keys from the record once they\n * have been extracted into `customValues` / `customFields`. Produces a single\n * canonical response shape (issue #1769). Defaults to `false` to preserve the\n * existing wire format for callers that read `cf_*` from the top level.\n */\n stripPrefixedKeys?: boolean\n}\n\nexport function applyCustomFieldsNormalization(\n record: Record<string, unknown>,\n decorated: CustomFieldDisplayPayload,\n options: ApplyCustomFieldsNormalizationOptions = {},\n): Record<string, unknown> {\n const stripPrefixedKeys = options.stripPrefixedKeys === true\n const base: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(record)) {\n if (stripPrefixedKeys && (key.startsWith('cf_') || key.startsWith('cf:'))) continue\n base[key] = value\n }\n base.customValues = decorated.customValues\n base.customFields = decorated.customFields\n return base\n}\n\nexport function decorateRecordWithCustomFields(\n record: Record<string, unknown>,\n definitions: CustomFieldDefinitionIndex,\n context: {\n organizationId?: string | null\n tenantId?: string | null\n } = {},\n): CustomFieldDisplayPayload {\n const rawEntries = extractAllCustomFieldEntries(record)\n if (!Object.keys(rawEntries).length) {\n return { customValues: null, customFields: [] }\n }\n const values: Record<string, unknown> = {}\n const entries: Array<{ entry: CustomFieldDisplayEntry; priority: number; updatedAt: number }> = []\n const organizationId = context.organizationId ?? null\n const tenantId = context.tenantId ?? null\n\n Object.entries(rawEntries).forEach(([prefixedKey, value]) => {\n const bareKey = prefixedKey.replace(/^cf_/, '')\n const normalizedKey = normalizeDefinitionKey(bareKey)\n if (!normalizedKey) return\n const defsForKey = definitions.get(normalizedKey) ?? []\n const resolvedDef = selectDefinitionForRecord(defsForKey, organizationId, tenantId)\n // Skip custom field values without active definitions to prevent orphaned fields\n if (!resolvedDef) return\n values[bareKey] = value\n const entry: CustomFieldDisplayEntry = {\n key: bareKey,\n label: resolvedDef.label ?? bareKey,\n value,\n kind: resolvedDef.kind ?? null,\n multi: resolvedDef.multi ?? Array.isArray(value),\n }\n entries.push({\n entry,\n priority: resolvedDef.priority ?? Number.MAX_SAFE_INTEGER,\n updatedAt: resolvedDef.updatedAt ?? 0,\n })\n })\n\n const ordered = entries\n .sort((a, b) => {\n const priorityDiff = a.priority - b.priority\n if (priorityDiff !== 0) return priorityDiff\n const updatedDiff = b.updatedAt - a.updatedAt\n if (updatedDiff !== 0) return updatedDiff\n return a.entry.key.localeCompare(b.entry.key)\n })\n .map((item) => item.entry)\n\n return {\n customValues: Object.keys(values).length ? values : null,\n customFields: ordered,\n }\n}\n\nexport async function loadCustomFieldValues(opts: {\n em: EntityManager\n entityId: EntityId\n recordIds: string[]\n tenantIdByRecord?: Record<string, string | null | undefined>\n organizationIdByRecord?: Record<string, string | null | undefined>\n tenantFallbacks?: (string | null | undefined)[]\n encryptionService?: TenantDataEncryptionService | null\n}): Promise<Record<string, Record<string, unknown>>> {\n const { em, entityId, recordIds } = opts\n if (!Array.isArray(recordIds) || recordIds.length === 0) return {}\n\n const normalizedRecordIds = recordIds.map((id) => String(id))\n let encryptionService: TenantDataEncryptionService | null | undefined\n const encryptionCache = new Map<string | null, string | null>()\n const getEncryptionService = () => {\n if (encryptionService !== undefined) return encryptionService\n encryptionService = resolveTenantEncryptionService(em, opts.encryptionService)\n return encryptionService\n }\n const tenantCandidates = new Set<string | null>()\n tenantCandidates.add(null)\n if (opts.tenantIdByRecord) {\n for (const val of Object.values(opts.tenantIdByRecord)) {\n tenantCandidates.add(val ? String(val) : null)\n }\n }\n if (opts.tenantFallbacks) {\n for (const val of opts.tenantFallbacks) tenantCandidates.add(val ? String(val) : null)\n }\n const fallbackTenant = (opts.tenantFallbacks || []).find((t) => t != null) ?? null\n\n const tenantList = Array.from(tenantCandidates)\n const tenantNonNull = tenantList.filter((t): t is string => t !== null)\n const tenantFilter = tenantNonNull.length\n ? { tenantId: { $in: [...tenantNonNull, null] as any } }\n : { tenantId: null }\n const cfRows = await em.find(CustomFieldValue, {\n entityId: entityId as any,\n recordId: { $in: normalizedRecordIds as any },\n deletedAt: null,\n ...(tenantList.length ? tenantFilter : {}),\n })\n\n if (!cfRows.length) return {}\n\n const allKeys = Array.from(new Set(cfRows.map((row) => String(row.fieldKey))))\n const organizationCandidates = new Set<string | null>()\n organizationCandidates.add(null)\n if (opts.organizationIdByRecord) {\n for (const val of Object.values(opts.organizationIdByRecord)) {\n organizationCandidates.add(val ? String(val) : null)\n }\n }\n for (const row of cfRows) {\n organizationCandidates.add(row.organizationId ? String(row.organizationId) : null)\n }\n const orgList = Array.from(organizationCandidates)\n\n const defs = allKeys.length\n ? await em.find(CustomFieldDef, {\n entityId: entityId as any,\n key: { $in: allKeys as any },\n deletedAt: null,\n isActive: true,\n ...(tenantList.length ? { tenantId: tenantFilter.tenantId } : {}),\n organizationId: { $in: orgList as any },\n })\n : []\n\n const defsByKey = new Map<string, CustomFieldDef[]>()\n for (const def of defs) {\n const list = defsByKey.get(def.key) || []\n list.push(def)\n defsByKey.set(def.key, list)\n }\n\n const pickDefinition = (fieldKey: string, organizationId: string | null, tenantId: string | null) => {\n const candidates = defsByKey.get(fieldKey)\n if (!candidates || candidates.length === 0) return null\n const active = candidates.filter((opt) => opt.isActive !== false && !opt.deletedAt)\n const list = active.length ? active : candidates\n if (organizationId && tenantId) {\n const exact = list.find((opt) => opt.organizationId === organizationId && opt.tenantId === tenantId)\n if (exact) return exact\n }\n if (organizationId) {\n const orgMatch = list.find((opt) => opt.organizationId === organizationId && (!tenantId || opt.tenantId == null || opt.tenantId === tenantId))\n if (orgMatch) return orgMatch\n }\n if (tenantId) {\n const tenantMatch = list.find((opt) => opt.organizationId == null && opt.tenantId === tenantId)\n if (tenantMatch) return tenantMatch\n }\n const global = list.find((opt) => opt.organizationId == null && opt.tenantId == null)\n return global ?? list[0]\n }\n\n const valueFromRow = (row: CustomFieldValue): unknown => {\n if (row.valueMultiline !== null && row.valueMultiline !== undefined) return row.valueMultiline\n if (row.valueText !== null && row.valueText !== undefined) return row.valueText\n if (row.valueInt !== null && row.valueInt !== undefined) return row.valueInt\n if (row.valueFloat !== null && row.valueFloat !== undefined) return row.valueFloat\n if (row.valueBool !== null && row.valueBool !== undefined) return row.valueBool\n return null\n }\n\n type Bucket = { orgId: string | null; tenantId: string | null; values: unknown[]; def?: CustomFieldDef | null; encrypted?: boolean }\n const buckets = new Map<string, Bucket>()\n\n for (const row of cfRows) {\n const recordId = String(row.recordId)\n const key = String(row.fieldKey)\n const bucketKey = `${recordId}::${key}`\n const orgId = row.organizationId ? String(row.organizationId) : null\n const tenantId = row.tenantId ? String(row.tenantId) : null\n const resolvedOrgId = orgId ?? (opts.organizationIdByRecord?.[recordId] ?? null)\n const resolvedTenantId = tenantId ?? (opts.tenantIdByRecord?.[recordId] ?? fallbackTenant)\n const def = pickDefinition(key, resolvedOrgId, resolvedTenantId)\n const encrypted = Boolean(def?.configJson && (def as any).configJson?.encrypted)\n const value = valueFromRow(row)\n const decrypted = encrypted\n ? await decryptCustomFieldValue(\n value,\n resolvedTenantId ?? tenantId ?? null,\n getEncryptionService(),\n encryptionCache,\n { kind: def?.kind ?? null },\n )\n : value\n const existing = buckets.get(bucketKey)\n if (existing) {\n if (existing.orgId == null && resolvedOrgId) existing.orgId = resolvedOrgId\n if (existing.tenantId == null && resolvedTenantId) existing.tenantId = resolvedTenantId\n if (existing.def == null && def) existing.def = def\n existing.encrypted = existing.encrypted || encrypted\n existing.values.push(decrypted)\n } else {\n buckets.set(bucketKey, { orgId: resolvedOrgId, tenantId: resolvedTenantId, values: [decrypted], def: def ?? null, encrypted })\n }\n }\n\n const result: Record<string, Record<string, unknown>> = {}\n for (const [compoundKey, bucket] of buckets.entries()) {\n const [recordId, fieldKey] = compoundKey.split('::')\n if (!result[recordId]) result[recordId] = {}\n const prefixed = `cf_${fieldKey}`\n const def = bucket.def ?? pickDefinition(fieldKey, bucket.orgId ?? (opts.organizationIdByRecord?.[recordId] ?? null), bucket.tenantId ?? (opts.tenantIdByRecord?.[recordId] ?? null))\n if (def && def.configJson && typeof def.configJson === 'object' && (def.configJson as any).multi) {\n const cleaned = bucket.values.filter((v) => v !== undefined && v !== null)\n result[recordId][prefixed] = cleaned\n } else if (bucket.values.length > 1) {\n const cleaned = bucket.values.filter((v) => v !== undefined)\n result[recordId][prefixed] = cleaned\n } else {\n result[recordId][prefixed] = bucket.values[0] ?? null\n }\n }\n\n return result\n}\n\nexport function summarizeCustomFields(record: Record<string, unknown>): CustomFieldSnapshot {\n const entries = extractAllCustomFieldEntries(record)\n const values = Object.fromEntries(\n Object.entries(entries).map(([prefixedKey, value]) => [\n prefixedKey.replace(/^cf_/, ''),\n value,\n ]),\n )\n const customValues = Object.keys(values).length ? values : null\n const customFields = Object.entries(values).map(([key, value]) => ({\n key,\n label: key,\n value,\n kind: null,\n multi: Array.isArray(value),\n }))\n return { entries, customValues, customFields }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }