@open-mercato/shared 0.6.3-develop.3876.1.d40fe4ec2d → 0.6.3-develop.3894.1.352abf4240
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/crud/custom-fields.js +113 -6
- package/dist/lib/crud/custom-fields.js.map +2 -2
- package/dist/lib/crud/factory.js +91 -42
- package/dist/lib/crud/factory.js.map +3 -3
- package/dist/lib/di/container.js +77 -9
- package/dist/lib/di/container.js.map +2 -2
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/package.json +2 -2
- package/src/lib/crud/__tests__/custom-fields.cache.test.ts +142 -0
- package/src/lib/crud/__tests__/log-crud-access.test.ts +158 -0
- package/src/lib/crud/__tests__/user-features-memo.test.ts +106 -0
- package/src/lib/crud/custom-fields.ts +169 -13
- package/src/lib/crud/factory.ts +120 -44
- package/src/lib/di/__tests__/bootstrap-cache.test.ts +182 -0
- package/src/lib/di/container.ts +111 -9
|
@@ -232,12 +232,68 @@ function selectDefinitionForRecord(defs, organizationId, tenantId) {
|
|
|
232
232
|
if (global.length) return sortDefinitionSummaries(global)[0];
|
|
233
233
|
return sortDefinitionSummaries(defs)[0] ?? null;
|
|
234
234
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
235
|
+
const CF_DEF_INDEX_CACHE_KEY_PREFIX = "crud:cf-def-index";
|
|
236
|
+
const CF_DEF_INDEX_DEFAULT_TTL_MS = 0;
|
|
237
|
+
function resolveCfDefIndexCacheTtlMs() {
|
|
238
|
+
const raw = process.env.OM_CF_DEF_CACHE_TTL_MS;
|
|
239
|
+
if (raw === void 0) return CF_DEF_INDEX_DEFAULT_TTL_MS;
|
|
240
|
+
const parsed = Number(raw);
|
|
241
|
+
if (!Number.isFinite(parsed) || parsed < 0) return CF_DEF_INDEX_DEFAULT_TTL_MS;
|
|
242
|
+
return parsed;
|
|
243
|
+
}
|
|
244
|
+
function buildCfDefIndexCacheKey(opts) {
|
|
245
|
+
const tenant = opts.tenantId ?? "global";
|
|
246
|
+
const entities = opts.entityIds.slice().sort().join("|");
|
|
247
|
+
const orgs = opts.organizationIds.length ? opts.organizationIds.slice().sort().join("|") : "none";
|
|
248
|
+
const fieldset = opts.fieldsetKey ?? "all";
|
|
249
|
+
return `${CF_DEF_INDEX_CACHE_KEY_PREFIX}:${tenant}:${entities}:${orgs}:${fieldset}`;
|
|
250
|
+
}
|
|
251
|
+
function buildCfDefIndexCacheTags(opts) {
|
|
252
|
+
const tenant = opts.tenantId ?? "global";
|
|
253
|
+
const tagBase = `entities:definitions:${tenant}`;
|
|
254
|
+
const tags = /* @__PURE__ */ new Set([tagBase]);
|
|
255
|
+
for (const entityId of opts.entityIds) {
|
|
256
|
+
tags.add(`${tagBase}:entity:${entityId}`);
|
|
257
|
+
}
|
|
258
|
+
return Array.from(tags);
|
|
259
|
+
}
|
|
260
|
+
function normalizeFieldsetKey(value) {
|
|
261
|
+
if (!value) return null;
|
|
262
|
+
if (Array.isArray(value)) {
|
|
263
|
+
const cleaned = value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter((entry) => entry.length > 0);
|
|
264
|
+
if (!cleaned.length) return null;
|
|
265
|
+
return cleaned.sort().join(",");
|
|
266
|
+
}
|
|
267
|
+
if (typeof value !== "string") return null;
|
|
268
|
+
const trimmed = value.trim();
|
|
269
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
270
|
+
}
|
|
271
|
+
function serializableIndexFromMap(index) {
|
|
272
|
+
return Array.from(index.entries());
|
|
273
|
+
}
|
|
274
|
+
function indexMapFromSerializable(value) {
|
|
275
|
+
if (!Array.isArray(value)) return null;
|
|
276
|
+
const map = /* @__PURE__ */ new Map();
|
|
277
|
+
for (const entry of value) {
|
|
278
|
+
if (!Array.isArray(entry) || entry.length !== 2) return null;
|
|
279
|
+
const [key, summaries] = entry;
|
|
280
|
+
if (typeof key !== "string" || !Array.isArray(summaries)) return null;
|
|
281
|
+
map.set(key, summaries);
|
|
282
|
+
}
|
|
283
|
+
return map;
|
|
284
|
+
}
|
|
285
|
+
const requestScopedCfDefIndexCache = /* @__PURE__ */ new WeakMap();
|
|
286
|
+
function getRequestScopedCfDefIndexCache(scope) {
|
|
287
|
+
let bucket = requestScopedCfDefIndexCache.get(scope);
|
|
288
|
+
if (!bucket) {
|
|
289
|
+
bucket = /* @__PURE__ */ new Map();
|
|
290
|
+
requestScopedCfDefIndexCache.set(scope, bucket);
|
|
291
|
+
}
|
|
292
|
+
return bucket;
|
|
293
|
+
}
|
|
294
|
+
async function loadCustomFieldDefinitionIndexFresh(opts) {
|
|
295
|
+
const { em, entityIds, orgCandidates } = opts;
|
|
239
296
|
const tenantId = opts.tenantId ?? null;
|
|
240
|
-
const orgCandidates = Array.isArray(opts.organizationIds) ? opts.organizationIds.map((id) => typeof id === "string" ? id.trim() : id).filter((id) => typeof id === "string" && id.length > 0) : [];
|
|
241
297
|
const scopeClauses = [
|
|
242
298
|
tenantId ? { $or: [{ tenantId }, { tenantId: null }] } : { tenantId: null }
|
|
243
299
|
];
|
|
@@ -254,7 +310,7 @@ async function loadCustomFieldDefinitionIndex(opts) {
|
|
|
254
310
|
isActive: true,
|
|
255
311
|
$and: scopeClauses
|
|
256
312
|
};
|
|
257
|
-
const defs = await
|
|
313
|
+
const defs = await em.find(CustomFieldDef, where);
|
|
258
314
|
const fieldsetFilter = normalizeFieldsetFilter(opts.fieldset);
|
|
259
315
|
const index = /* @__PURE__ */ new Map();
|
|
260
316
|
defs.forEach((def) => {
|
|
@@ -277,6 +333,56 @@ async function loadCustomFieldDefinitionIndex(opts) {
|
|
|
277
333
|
});
|
|
278
334
|
return index;
|
|
279
335
|
}
|
|
336
|
+
async function loadCustomFieldDefinitionIndex(opts) {
|
|
337
|
+
const list = Array.isArray(opts.entityIds) ? opts.entityIds : [opts.entityIds];
|
|
338
|
+
const entityIds = list.map((id) => typeof id === "string" ? id.trim() : String(id ?? "")).filter((id) => id.length > 0);
|
|
339
|
+
if (!entityIds.length) return /* @__PURE__ */ new Map();
|
|
340
|
+
const tenantId = opts.tenantId ?? null;
|
|
341
|
+
const orgCandidates = Array.isArray(opts.organizationIds) ? opts.organizationIds.map((id) => typeof id === "string" ? id.trim() : id).filter((id) => typeof id === "string" && id.length > 0) : [];
|
|
342
|
+
const fieldsetKey = normalizeFieldsetKey(opts.fieldset);
|
|
343
|
+
const ttlMs = resolveCfDefIndexCacheTtlMs();
|
|
344
|
+
const cacheKey = buildCfDefIndexCacheKey({
|
|
345
|
+
tenantId,
|
|
346
|
+
entityIds,
|
|
347
|
+
organizationIds: orgCandidates,
|
|
348
|
+
fieldsetKey
|
|
349
|
+
});
|
|
350
|
+
const requestBucket = opts.requestScope ? getRequestScopedCfDefIndexCache(opts.requestScope) : null;
|
|
351
|
+
if (requestBucket) {
|
|
352
|
+
const cached = requestBucket.get(cacheKey);
|
|
353
|
+
if (cached) return cached;
|
|
354
|
+
}
|
|
355
|
+
const sharedCache = ttlMs > 0 ? opts.cache ?? null : null;
|
|
356
|
+
if (sharedCache && typeof sharedCache.get === "function") {
|
|
357
|
+
try {
|
|
358
|
+
const cached = await sharedCache.get(cacheKey);
|
|
359
|
+
const restored = indexMapFromSerializable(cached);
|
|
360
|
+
if (restored) {
|
|
361
|
+
if (requestBucket) requestBucket.set(cacheKey, restored);
|
|
362
|
+
return restored;
|
|
363
|
+
}
|
|
364
|
+
} catch (err) {
|
|
365
|
+
console.warn("[crud:cf-def-cache] read failed", err);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
const index = await loadCustomFieldDefinitionIndexFresh({
|
|
369
|
+
...opts,
|
|
370
|
+
entityIds,
|
|
371
|
+
orgCandidates
|
|
372
|
+
});
|
|
373
|
+
if (sharedCache && typeof sharedCache.set === "function") {
|
|
374
|
+
try {
|
|
375
|
+
await sharedCache.set(cacheKey, serializableIndexFromMap(index), {
|
|
376
|
+
ttl: ttlMs,
|
|
377
|
+
tags: buildCfDefIndexCacheTags({ tenantId, entityIds })
|
|
378
|
+
});
|
|
379
|
+
} catch (err) {
|
|
380
|
+
console.warn("[crud:cf-def-cache] write failed", err);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (requestBucket) requestBucket.set(cacheKey, index);
|
|
384
|
+
return index;
|
|
385
|
+
}
|
|
280
386
|
function applyCustomFieldsNormalization(record, decorated, options = {}) {
|
|
281
387
|
const stripPrefixedKeys = options.stripPrefixedKeys === true;
|
|
282
388
|
const base = {};
|
|
@@ -490,6 +596,7 @@ export {
|
|
|
490
596
|
extractAllCustomFieldEntries,
|
|
491
597
|
extractCustomFieldValuesFromPayload,
|
|
492
598
|
extractCustomFieldsFromItem,
|
|
599
|
+
getRequestScopedCfDefIndexCache,
|
|
493
600
|
loadCustomFieldDefinitionIndex,
|
|
494
601
|
loadCustomFieldValues,
|
|
495
602
|
normalizeCustomFieldValue,
|
|
@@ -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\nexport async function loadCustomFieldDefinitionIndex(opts: {\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}): 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 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 opts.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 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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",
|
|
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": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/lib/crud/factory.js
CHANGED
|
@@ -371,6 +371,23 @@ function resolveAccessLogService(container) {
|
|
|
371
371
|
}
|
|
372
372
|
return null;
|
|
373
373
|
}
|
|
374
|
+
function shouldBlockAccessLogWrites() {
|
|
375
|
+
return process.env.OM_CRUD_ACCESS_LOG_BLOCKING === "1";
|
|
376
|
+
}
|
|
377
|
+
const pendingCrudAccessLogPromises = /* @__PURE__ */ new Set();
|
|
378
|
+
function trackPendingCrudAccessLogPromise(promise) {
|
|
379
|
+
pendingCrudAccessLogPromises.add(promise);
|
|
380
|
+
promise.catch(() => void 0).finally(() => {
|
|
381
|
+
pendingCrudAccessLogPromises.delete(promise);
|
|
382
|
+
});
|
|
383
|
+
return promise;
|
|
384
|
+
}
|
|
385
|
+
async function flushPendingCrudAccessLogs() {
|
|
386
|
+
while (pendingCrudAccessLogPromises.size > 0) {
|
|
387
|
+
const snapshot = Array.from(pendingCrudAccessLogPromises);
|
|
388
|
+
await Promise.allSettled(snapshot);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
374
391
|
function logForbidden(details) {
|
|
375
392
|
try {
|
|
376
393
|
console.warn("[crud] Forbidden request", details);
|
|
@@ -406,10 +423,10 @@ function createCrudProfiler(resource, operation) {
|
|
|
406
423
|
}
|
|
407
424
|
async function logCrudAccess(options) {
|
|
408
425
|
const { container, auth, request, items, resourceKind } = options;
|
|
409
|
-
if (!auth) return;
|
|
410
|
-
if (!Array.isArray(items) || items.length === 0) return;
|
|
426
|
+
if (!auth) return { mode: "skipped", count: 0, pending: pendingCrudAccessLogPromises.size };
|
|
427
|
+
if (!Array.isArray(items) || items.length === 0) return { mode: "skipped", count: 0, pending: pendingCrudAccessLogPromises.size };
|
|
411
428
|
const service = resolveAccessLogService(container);
|
|
412
|
-
if (!service) return;
|
|
429
|
+
if (!service) return { mode: "skipped", count: 0, pending: pendingCrudAccessLogPromises.size };
|
|
413
430
|
const idField = options.idField || "id";
|
|
414
431
|
const tenantId = options.tenantId ?? auth.tenantId ?? null;
|
|
415
432
|
const organizationId = options.organizationId ?? auth.orgId ?? null;
|
|
@@ -431,7 +448,7 @@ async function logCrudAccess(options) {
|
|
|
431
448
|
} catch {
|
|
432
449
|
}
|
|
433
450
|
const uniqueIds = /* @__PURE__ */ new Set();
|
|
434
|
-
const
|
|
451
|
+
const payloads = [];
|
|
435
452
|
for (const item of items) {
|
|
436
453
|
if (!item || typeof item !== "object") continue;
|
|
437
454
|
const rawId = item[idField];
|
|
@@ -448,17 +465,41 @@ async function logCrudAccess(options) {
|
|
|
448
465
|
};
|
|
449
466
|
if (fields.length > 0) payload.fields = fields;
|
|
450
467
|
if (Object.keys(context).length > 0) payload.context = context;
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
468
|
+
payloads.push(payload);
|
|
469
|
+
}
|
|
470
|
+
if (!payloads.length) return { mode: "skipped", count: 0, pending: pendingCrudAccessLogPromises.size };
|
|
471
|
+
const blocking = shouldBlockAccessLogWrites();
|
|
472
|
+
const dispatchMode = typeof service.logMany === "function" ? "batch" : "fanout";
|
|
473
|
+
const writePromise = (async () => {
|
|
474
|
+
try {
|
|
475
|
+
if (typeof service.logMany === "function") {
|
|
476
|
+
await service.logMany(payloads);
|
|
477
|
+
} else {
|
|
478
|
+
await Promise.all(
|
|
479
|
+
payloads.map(
|
|
480
|
+
(payload) => Promise.resolve(service.log(payload)).catch((err) => {
|
|
481
|
+
try {
|
|
482
|
+
console.error("[crud] failed to record access log", { err, payload });
|
|
483
|
+
} catch {
|
|
484
|
+
}
|
|
485
|
+
return void 0;
|
|
486
|
+
})
|
|
487
|
+
)
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
} catch (err) {
|
|
491
|
+
try {
|
|
492
|
+
console.error("[crud] failed to record access logs (batch)", { err, count: payloads.length });
|
|
493
|
+
} catch {
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
})();
|
|
497
|
+
trackPendingCrudAccessLogPromise(writePromise);
|
|
498
|
+
if (blocking) {
|
|
499
|
+
await writePromise;
|
|
500
|
+
return { mode: "blocking", count: payloads.length, pending: pendingCrudAccessLogPromises.size };
|
|
460
501
|
}
|
|
461
|
-
|
|
502
|
+
return { mode: dispatchMode, count: payloads.length, pending: pendingCrudAccessLogPromises.size };
|
|
462
503
|
}
|
|
463
504
|
function safeClone(value) {
|
|
464
505
|
try {
|
|
@@ -582,11 +623,18 @@ function makeCrudRoute(opts) {
|
|
|
582
623
|
try {
|
|
583
624
|
const em = ctx.container.resolve("em");
|
|
584
625
|
const organizationIds = Array.isArray(ctx.organizationIds) && ctx.organizationIds.length ? ctx.organizationIds : [ctx.selectedOrganizationId ?? null];
|
|
626
|
+
let cfDefCache = null;
|
|
627
|
+
try {
|
|
628
|
+
cfDefCache = ctx.container.resolve("cache");
|
|
629
|
+
} catch {
|
|
630
|
+
}
|
|
585
631
|
const definitionIndex = await loadCustomFieldDefinitionIndex({
|
|
586
632
|
em,
|
|
587
633
|
entityIds,
|
|
588
634
|
tenantId: ctx.auth?.tenantId ?? null,
|
|
589
|
-
organizationIds
|
|
635
|
+
organizationIds,
|
|
636
|
+
cache: cfDefCache ?? null,
|
|
637
|
+
requestScope: ctx
|
|
590
638
|
});
|
|
591
639
|
cfProfiler.mark("definitions_loaded", { definitionCount: definitionIndex.size });
|
|
592
640
|
const decoratedItems = items.map((raw) => {
|
|
@@ -619,20 +667,31 @@ function makeCrudRoute(opts) {
|
|
|
619
667
|
return items;
|
|
620
668
|
}
|
|
621
669
|
};
|
|
670
|
+
const userFeaturesPromiseCache = /* @__PURE__ */ new WeakMap();
|
|
671
|
+
function resolveUserFeaturesOnce(ctx) {
|
|
672
|
+
if (!ctx.auth) return Promise.resolve(void 0);
|
|
673
|
+
const cached = userFeaturesPromiseCache.get(ctx);
|
|
674
|
+
if (cached) return cached;
|
|
675
|
+
const promise = (async () => {
|
|
676
|
+
try {
|
|
677
|
+
const rbac = ctx.container.resolve("rbacService");
|
|
678
|
+
if (rbac?.getGrantedFeatures) {
|
|
679
|
+
return await rbac.getGrantedFeatures(ctx.auth.sub, {
|
|
680
|
+
tenantId: ctx.auth.tenantId,
|
|
681
|
+
organizationId: ctx.selectedOrganizationId ?? ctx.auth.orgId
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
} catch {
|
|
685
|
+
}
|
|
686
|
+
return void 0;
|
|
687
|
+
})();
|
|
688
|
+
userFeaturesPromiseCache.set(ctx, promise);
|
|
689
|
+
return promise;
|
|
690
|
+
}
|
|
622
691
|
async function buildEnricherContext(ctx) {
|
|
623
692
|
if (!opts.enrichers?.entityId) return null;
|
|
624
693
|
if (!ctx.auth) return null;
|
|
625
|
-
|
|
626
|
-
try {
|
|
627
|
-
const rbac = ctx.container.resolve("rbacService");
|
|
628
|
-
if (rbac?.getGrantedFeatures) {
|
|
629
|
-
userFeatures = await rbac.getGrantedFeatures(ctx.auth.sub, {
|
|
630
|
-
tenantId: ctx.auth.tenantId,
|
|
631
|
-
organizationId: ctx.selectedOrganizationId ?? ctx.auth.orgId
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
} catch {
|
|
635
|
-
}
|
|
694
|
+
const userFeatures = await resolveUserFeaturesOnce(ctx);
|
|
636
695
|
return {
|
|
637
696
|
organizationId: ctx.selectedOrganizationId ?? ctx.auth.orgId ?? "",
|
|
638
697
|
tenantId: ctx.auth.tenantId ?? "",
|
|
@@ -643,18 +702,7 @@ function makeCrudRoute(opts) {
|
|
|
643
702
|
};
|
|
644
703
|
}
|
|
645
704
|
async function resolveUserFeatures(ctx) {
|
|
646
|
-
|
|
647
|
-
try {
|
|
648
|
-
const rbac = ctx.container.resolve("rbacService");
|
|
649
|
-
if (rbac?.getGrantedFeatures) {
|
|
650
|
-
return await rbac.getGrantedFeatures(ctx.auth.sub, {
|
|
651
|
-
tenantId: ctx.auth.tenantId,
|
|
652
|
-
organizationId: ctx.selectedOrganizationId ?? ctx.auth.orgId
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
} catch {
|
|
656
|
-
}
|
|
657
|
-
return void 0;
|
|
705
|
+
return resolveUserFeaturesOnce(ctx);
|
|
658
706
|
}
|
|
659
707
|
const interceptorContextCache = /* @__PURE__ */ new WeakMap();
|
|
660
708
|
async function buildInterceptorContextInner(ctx) {
|
|
@@ -1106,7 +1154,7 @@ function makeCrudRoute(opts) {
|
|
|
1106
1154
|
}
|
|
1107
1155
|
profiler.mark("translation_overlays_complete", { itemCount: transformedItems.length });
|
|
1108
1156
|
}
|
|
1109
|
-
await logCrudAccess({
|
|
1157
|
+
const accessLogResult2 = await logCrudAccess({
|
|
1110
1158
|
container: ctx.container,
|
|
1111
1159
|
auth: ctx.auth,
|
|
1112
1160
|
request,
|
|
@@ -1117,7 +1165,7 @@ function makeCrudRoute(opts) {
|
|
|
1117
1165
|
tenantId: ctx.auth.tenantId ?? null,
|
|
1118
1166
|
query: validated
|
|
1119
1167
|
});
|
|
1120
|
-
profiler.mark("access_logged");
|
|
1168
|
+
profiler.mark("access_logged", accessLogResult2);
|
|
1121
1169
|
if (exportRequested && requestedExport) {
|
|
1122
1170
|
const total = typeof res.total === "number" ? res.total : rawItems.length;
|
|
1123
1171
|
const initialExportItems = exportFullRequested ? rawItems.map(normalizeFullRecordForExport) : transformedItems;
|
|
@@ -1315,7 +1363,7 @@ function makeCrudRoute(opts) {
|
|
|
1315
1363
|
}
|
|
1316
1364
|
profiler.mark("fallback_translation_overlays_complete", { itemCount: Array.isArray(list) ? list.length : 0 });
|
|
1317
1365
|
}
|
|
1318
|
-
await logCrudAccess({
|
|
1366
|
+
const accessLogResult = await logCrudAccess({
|
|
1319
1367
|
container: ctx.container,
|
|
1320
1368
|
auth: ctx.auth,
|
|
1321
1369
|
request,
|
|
@@ -1326,7 +1374,7 @@ function makeCrudRoute(opts) {
|
|
|
1326
1374
|
tenantId: ctx.auth.tenantId ?? null,
|
|
1327
1375
|
query: validated
|
|
1328
1376
|
});
|
|
1329
|
-
profiler.mark("access_logged");
|
|
1377
|
+
profiler.mark("access_logged", accessLogResult);
|
|
1330
1378
|
if (exportRequested && requestedExport) {
|
|
1331
1379
|
const exportItems = exportFullRequested ? list.map(normalizeFullRecordForExport) : list;
|
|
1332
1380
|
const prepared = exportFullRequested ? { columns: ensureColumns(exportItems), rows: exportItems } : prepareExportData(exportItems, opts.list);
|
|
@@ -2190,6 +2238,7 @@ function makeCrudRoute(opts) {
|
|
|
2190
2238
|
return { metadata, GET, POST, PUT, DELETE };
|
|
2191
2239
|
}
|
|
2192
2240
|
export {
|
|
2241
|
+
flushPendingCrudAccessLogs,
|
|
2193
2242
|
logCrudAccess,
|
|
2194
2243
|
makeCrudRoute
|
|
2195
2244
|
};
|