@stndrds/schema 0.1.0-alpha.50 → 0.1.0-alpha.52
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/{chunk-FDWX5X6L.mjs → chunk-D4AFKFAX.mjs} +449 -124
- package/dist/{chunk-LPCQM5VG.js → chunk-LPOSOME6.js} +588 -263
- package/dist/index.d.mts +70 -3
- package/dist/index.d.ts +70 -3
- package/dist/index.js +8 -6
- package/dist/index.mjs +5 -3
- package/dist/{runtime-BbM_jyVU.d.mts → runtime-C8IgSFtA.d.mts} +974 -411
- package/dist/{runtime-BbM_jyVU.d.ts → runtime-C8IgSFtA.d.ts} +974 -411
- package/dist/runtime.d.mts +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +4 -2
- package/dist/runtime.mjs +5 -3
- package/package.json +2 -2
|
@@ -3152,6 +3152,10 @@ function createMockFilesRepository(stores) {
|
|
|
3152
3152
|
if (file2?.deletedAt) return Promise.resolve(null);
|
|
3153
3153
|
return Promise.resolve(file2 ?? null);
|
|
3154
3154
|
},
|
|
3155
|
+
findByIds(ids) {
|
|
3156
|
+
const files = ids.map((id) => stores.files.get(id)).filter((f) => f != null && !f.deletedAt);
|
|
3157
|
+
return Promise.resolve(files);
|
|
3158
|
+
},
|
|
3155
3159
|
create(data) {
|
|
3156
3160
|
const tenantId = getTenantId();
|
|
3157
3161
|
const file2 = {
|
|
@@ -4123,6 +4127,284 @@ function createMockWorkflowParticipationsRepository(stores) {
|
|
|
4123
4127
|
}
|
|
4124
4128
|
};
|
|
4125
4129
|
}
|
|
4130
|
+
function requireUserId() {
|
|
4131
|
+
const userId = getUserId();
|
|
4132
|
+
if (!userId) {
|
|
4133
|
+
throw new Error("User context required for AI operations");
|
|
4134
|
+
}
|
|
4135
|
+
return userId;
|
|
4136
|
+
}
|
|
4137
|
+
function createMockAIConversationsRepository(stores) {
|
|
4138
|
+
return {
|
|
4139
|
+
findById(id) {
|
|
4140
|
+
const conversation = stores.aiConversations.get(id);
|
|
4141
|
+
if (!conversation || conversation.deletedAt) return Promise.resolve(null);
|
|
4142
|
+
const tenantId = getTenantId();
|
|
4143
|
+
if (conversation.tenantId !== tenantId) return Promise.resolve(null);
|
|
4144
|
+
return Promise.resolve(conversation);
|
|
4145
|
+
},
|
|
4146
|
+
list(options) {
|
|
4147
|
+
const tenantId = getTenantId();
|
|
4148
|
+
const userId = requireUserId();
|
|
4149
|
+
let results = Array.from(stores.aiConversations.values()).filter((c) => {
|
|
4150
|
+
if (c.tenantId !== tenantId || c.userId !== userId) return false;
|
|
4151
|
+
if (!options?.includeDeleted && c.deletedAt) return false;
|
|
4152
|
+
return true;
|
|
4153
|
+
});
|
|
4154
|
+
results.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
4155
|
+
const total = results.length;
|
|
4156
|
+
if (options?.limit) {
|
|
4157
|
+
results = results.slice(options.offset ?? 0, (options.offset ?? 0) + options.limit);
|
|
4158
|
+
}
|
|
4159
|
+
return Promise.resolve({ conversations: results, total });
|
|
4160
|
+
},
|
|
4161
|
+
create(data) {
|
|
4162
|
+
const tenantId = getTenantId();
|
|
4163
|
+
const userId = requireUserId();
|
|
4164
|
+
const now = /* @__PURE__ */ new Date();
|
|
4165
|
+
const conversation = {
|
|
4166
|
+
id: generateId(),
|
|
4167
|
+
tenantId,
|
|
4168
|
+
userId,
|
|
4169
|
+
title: data.title ?? null,
|
|
4170
|
+
messageCount: 0,
|
|
4171
|
+
totalTokens: 0,
|
|
4172
|
+
totalCost: 0,
|
|
4173
|
+
createdAt: now,
|
|
4174
|
+
updatedAt: now,
|
|
4175
|
+
deletedAt: null
|
|
4176
|
+
};
|
|
4177
|
+
stores.aiConversations.set(conversation.id, conversation);
|
|
4178
|
+
return Promise.resolve(conversation);
|
|
4179
|
+
},
|
|
4180
|
+
updateTitle(id, title) {
|
|
4181
|
+
const conversation = stores.aiConversations.get(id);
|
|
4182
|
+
if (!conversation || conversation.deletedAt) return Promise.resolve(null);
|
|
4183
|
+
const tenantId = getTenantId();
|
|
4184
|
+
if (conversation.tenantId !== tenantId) return Promise.resolve(null);
|
|
4185
|
+
conversation.title = title;
|
|
4186
|
+
conversation.updatedAt = /* @__PURE__ */ new Date();
|
|
4187
|
+
stores.aiConversations.set(id, conversation);
|
|
4188
|
+
return Promise.resolve(conversation);
|
|
4189
|
+
},
|
|
4190
|
+
delete(id) {
|
|
4191
|
+
const conversation = stores.aiConversations.get(id);
|
|
4192
|
+
if (!conversation) return Promise.resolve(false);
|
|
4193
|
+
const tenantId = getTenantId();
|
|
4194
|
+
if (conversation.tenantId !== tenantId) return Promise.resolve(false);
|
|
4195
|
+
conversation.deletedAt = /* @__PURE__ */ new Date();
|
|
4196
|
+
stores.aiConversations.set(id, conversation);
|
|
4197
|
+
return Promise.resolve(true);
|
|
4198
|
+
},
|
|
4199
|
+
addMessage(input) {
|
|
4200
|
+
const now = /* @__PURE__ */ new Date();
|
|
4201
|
+
const message = {
|
|
4202
|
+
id: generateId(),
|
|
4203
|
+
conversationId: input.conversationId,
|
|
4204
|
+
role: input.role,
|
|
4205
|
+
content: input.content,
|
|
4206
|
+
thinkingLevel: input.thinkingLevel ?? null,
|
|
4207
|
+
thinkingSummary: input.thinkingSummary ?? null,
|
|
4208
|
+
toolCalls: input.toolCalls ?? null,
|
|
4209
|
+
inputTokens: input.inputTokens ?? null,
|
|
4210
|
+
outputTokens: input.outputTokens ?? null,
|
|
4211
|
+
cost: input.cost ?? null,
|
|
4212
|
+
provider: input.provider ?? null,
|
|
4213
|
+
model: input.model ?? null,
|
|
4214
|
+
attachmentIds: input.attachmentIds ?? null,
|
|
4215
|
+
createdAt: now
|
|
4216
|
+
};
|
|
4217
|
+
stores.aiMessages.set(message.id, message);
|
|
4218
|
+
const conversation = stores.aiConversations.get(input.conversationId);
|
|
4219
|
+
if (conversation) {
|
|
4220
|
+
conversation.messageCount++;
|
|
4221
|
+
conversation.totalTokens += (input.inputTokens ?? 0) + (input.outputTokens ?? 0);
|
|
4222
|
+
conversation.totalCost += input.cost ?? 0;
|
|
4223
|
+
conversation.updatedAt = now;
|
|
4224
|
+
stores.aiConversations.set(input.conversationId, conversation);
|
|
4225
|
+
}
|
|
4226
|
+
return Promise.resolve(message);
|
|
4227
|
+
},
|
|
4228
|
+
listMessages(conversationId, options) {
|
|
4229
|
+
let results = Array.from(stores.aiMessages.values()).filter((m) => m.conversationId === conversationId).sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
|
4230
|
+
const total = results.length;
|
|
4231
|
+
if (options?.limit) {
|
|
4232
|
+
results = results.slice(options.offset ?? 0, (options.offset ?? 0) + options.limit);
|
|
4233
|
+
}
|
|
4234
|
+
return Promise.resolve({ messages: results, total });
|
|
4235
|
+
},
|
|
4236
|
+
getRecentMessages(conversationId, count = 20) {
|
|
4237
|
+
const results = Array.from(stores.aiMessages.values()).filter((m) => m.conversationId === conversationId).sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()).slice(-count);
|
|
4238
|
+
return Promise.resolve(results);
|
|
4239
|
+
}
|
|
4240
|
+
};
|
|
4241
|
+
}
|
|
4242
|
+
function createMockAIUserMemoryRepository(stores) {
|
|
4243
|
+
const getKey = () => {
|
|
4244
|
+
const tenantId = getTenantId();
|
|
4245
|
+
const userId = requireUserId();
|
|
4246
|
+
return `${tenantId}:${userId}`;
|
|
4247
|
+
};
|
|
4248
|
+
return {
|
|
4249
|
+
get() {
|
|
4250
|
+
const key = getKey();
|
|
4251
|
+
return Promise.resolve(stores.aiUserMemory.get(key) ?? null);
|
|
4252
|
+
},
|
|
4253
|
+
upsert(data) {
|
|
4254
|
+
const key = getKey();
|
|
4255
|
+
const tenantId = getTenantId();
|
|
4256
|
+
const userId = requireUserId();
|
|
4257
|
+
const now = /* @__PURE__ */ new Date();
|
|
4258
|
+
const existing = stores.aiUserMemory.get(key);
|
|
4259
|
+
const memory = {
|
|
4260
|
+
id: existing?.id ?? generateId(),
|
|
4261
|
+
tenantId,
|
|
4262
|
+
userId,
|
|
4263
|
+
preferences: data.preferences ?? existing?.preferences ?? {},
|
|
4264
|
+
facts: data.facts ?? existing?.facts ?? [],
|
|
4265
|
+
createdAt: existing?.createdAt ?? now,
|
|
4266
|
+
updatedAt: now
|
|
4267
|
+
};
|
|
4268
|
+
stores.aiUserMemory.set(key, memory);
|
|
4269
|
+
return Promise.resolve(memory);
|
|
4270
|
+
},
|
|
4271
|
+
addFact(fact) {
|
|
4272
|
+
const key = getKey();
|
|
4273
|
+
const tenantId = getTenantId();
|
|
4274
|
+
const userId = requireUserId();
|
|
4275
|
+
const now = /* @__PURE__ */ new Date();
|
|
4276
|
+
const existing = stores.aiUserMemory.get(key);
|
|
4277
|
+
const memory = {
|
|
4278
|
+
id: existing?.id ?? generateId(),
|
|
4279
|
+
tenantId,
|
|
4280
|
+
userId,
|
|
4281
|
+
preferences: existing?.preferences ?? {},
|
|
4282
|
+
facts: [...existing?.facts ?? [], fact],
|
|
4283
|
+
createdAt: existing?.createdAt ?? now,
|
|
4284
|
+
updatedAt: now
|
|
4285
|
+
};
|
|
4286
|
+
stores.aiUserMemory.set(key, memory);
|
|
4287
|
+
return Promise.resolve(memory);
|
|
4288
|
+
},
|
|
4289
|
+
removeFact(fact) {
|
|
4290
|
+
const key = getKey();
|
|
4291
|
+
const tenantId = getTenantId();
|
|
4292
|
+
const userId = requireUserId();
|
|
4293
|
+
const now = /* @__PURE__ */ new Date();
|
|
4294
|
+
const existing = stores.aiUserMemory.get(key);
|
|
4295
|
+
const memory = {
|
|
4296
|
+
id: existing?.id ?? generateId(),
|
|
4297
|
+
tenantId,
|
|
4298
|
+
userId,
|
|
4299
|
+
preferences: existing?.preferences ?? {},
|
|
4300
|
+
facts: (existing?.facts ?? []).filter((f) => f !== fact),
|
|
4301
|
+
createdAt: existing?.createdAt ?? now,
|
|
4302
|
+
updatedAt: now
|
|
4303
|
+
};
|
|
4304
|
+
stores.aiUserMemory.set(key, memory);
|
|
4305
|
+
return Promise.resolve(memory);
|
|
4306
|
+
},
|
|
4307
|
+
setPreference(prefKey, value) {
|
|
4308
|
+
const memoryKey = getKey();
|
|
4309
|
+
const tenantId = getTenantId();
|
|
4310
|
+
const userId = requireUserId();
|
|
4311
|
+
const now = /* @__PURE__ */ new Date();
|
|
4312
|
+
const existing = stores.aiUserMemory.get(memoryKey);
|
|
4313
|
+
const memory = {
|
|
4314
|
+
id: existing?.id ?? generateId(),
|
|
4315
|
+
tenantId,
|
|
4316
|
+
userId,
|
|
4317
|
+
preferences: { ...existing?.preferences ?? {}, [prefKey]: value },
|
|
4318
|
+
facts: existing?.facts ?? [],
|
|
4319
|
+
createdAt: existing?.createdAt ?? now,
|
|
4320
|
+
updatedAt: now
|
|
4321
|
+
};
|
|
4322
|
+
stores.aiUserMemory.set(memoryKey, memory);
|
|
4323
|
+
return Promise.resolve(memory);
|
|
4324
|
+
},
|
|
4325
|
+
clear() {
|
|
4326
|
+
const key = getKey();
|
|
4327
|
+
stores.aiUserMemory.delete(key);
|
|
4328
|
+
return Promise.resolve();
|
|
4329
|
+
}
|
|
4330
|
+
};
|
|
4331
|
+
}
|
|
4332
|
+
function createMockAIUsageMetricsRepository(stores) {
|
|
4333
|
+
const getDateKey = (date2) => {
|
|
4334
|
+
const tenantId = getTenantId();
|
|
4335
|
+
const dateStr = date2.toISOString().split("T")[0];
|
|
4336
|
+
return `${tenantId}:${dateStr}`;
|
|
4337
|
+
};
|
|
4338
|
+
return {
|
|
4339
|
+
recordUsage(data) {
|
|
4340
|
+
const tenantId = getTenantId();
|
|
4341
|
+
const now = /* @__PURE__ */ new Date();
|
|
4342
|
+
const key = getDateKey(now);
|
|
4343
|
+
const existing = stores.aiUsageMetrics.get(key);
|
|
4344
|
+
const providerBreakdown = existing?.providerBreakdown ?? {};
|
|
4345
|
+
if (!providerBreakdown[data.provider]) {
|
|
4346
|
+
providerBreakdown[data.provider] = { requests: 0, tokens: 0, cost: 0 };
|
|
4347
|
+
}
|
|
4348
|
+
providerBreakdown[data.provider].requests++;
|
|
4349
|
+
providerBreakdown[data.provider].tokens += data.tokens;
|
|
4350
|
+
providerBreakdown[data.provider].cost += data.cost;
|
|
4351
|
+
const toolUsage = existing?.toolUsage ?? {};
|
|
4352
|
+
if (data.toolName) {
|
|
4353
|
+
toolUsage[data.toolName] = (toolUsage[data.toolName] ?? 0) + 1;
|
|
4354
|
+
}
|
|
4355
|
+
const metrics = {
|
|
4356
|
+
id: existing?.id ?? generateId(),
|
|
4357
|
+
tenantId,
|
|
4358
|
+
date: new Date(now.toISOString().split("T")[0] ?? now.toISOString()),
|
|
4359
|
+
requestCount: (existing?.requestCount ?? 0) + 1,
|
|
4360
|
+
totalTokens: (existing?.totalTokens ?? 0) + data.tokens,
|
|
4361
|
+
totalCost: (existing?.totalCost ?? 0) + data.cost,
|
|
4362
|
+
providerBreakdown,
|
|
4363
|
+
toolUsage
|
|
4364
|
+
};
|
|
4365
|
+
stores.aiUsageMetrics.set(key, metrics);
|
|
4366
|
+
return Promise.resolve();
|
|
4367
|
+
},
|
|
4368
|
+
getByDateRange(startDate, endDate) {
|
|
4369
|
+
const tenantId = getTenantId();
|
|
4370
|
+
const results = Array.from(stores.aiUsageMetrics.values()).filter((m) => {
|
|
4371
|
+
if (m.tenantId !== tenantId) return false;
|
|
4372
|
+
return m.date >= startDate && m.date <= endDate;
|
|
4373
|
+
});
|
|
4374
|
+
results.sort((a, b) => a.date.getTime() - b.date.getTime());
|
|
4375
|
+
return Promise.resolve(results);
|
|
4376
|
+
},
|
|
4377
|
+
getCurrentMonthUsage() {
|
|
4378
|
+
const tenantId = getTenantId();
|
|
4379
|
+
const now = /* @__PURE__ */ new Date();
|
|
4380
|
+
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
4381
|
+
const monthMetrics = Array.from(stores.aiUsageMetrics.values()).filter((m) => {
|
|
4382
|
+
if (m.tenantId !== tenantId) return false;
|
|
4383
|
+
return m.date >= startOfMonth;
|
|
4384
|
+
});
|
|
4385
|
+
const aggregated = {
|
|
4386
|
+
requestCount: 0,
|
|
4387
|
+
totalTokens: 0,
|
|
4388
|
+
totalCost: 0,
|
|
4389
|
+
providerBreakdown: {}
|
|
4390
|
+
};
|
|
4391
|
+
for (const m of monthMetrics) {
|
|
4392
|
+
aggregated.requestCount += m.requestCount;
|
|
4393
|
+
aggregated.totalTokens += m.totalTokens;
|
|
4394
|
+
aggregated.totalCost += m.totalCost;
|
|
4395
|
+
for (const [provider, stats] of Object.entries(m.providerBreakdown)) {
|
|
4396
|
+
if (!aggregated.providerBreakdown[provider]) {
|
|
4397
|
+
aggregated.providerBreakdown[provider] = { requests: 0, tokens: 0, cost: 0 };
|
|
4398
|
+
}
|
|
4399
|
+
aggregated.providerBreakdown[provider].requests += stats.requests;
|
|
4400
|
+
aggregated.providerBreakdown[provider].tokens += stats.tokens;
|
|
4401
|
+
aggregated.providerBreakdown[provider].cost += stats.cost;
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
return Promise.resolve(aggregated);
|
|
4405
|
+
}
|
|
4406
|
+
};
|
|
4407
|
+
}
|
|
4126
4408
|
function createMockAdapter() {
|
|
4127
4409
|
const stores = {
|
|
4128
4410
|
objects: /* @__PURE__ */ new Map(),
|
|
@@ -4136,7 +4418,12 @@ function createMockAdapter() {
|
|
|
4136
4418
|
userRoles: /* @__PURE__ */ new Map(),
|
|
4137
4419
|
workflows: /* @__PURE__ */ new Map(),
|
|
4138
4420
|
workflowInstances: /* @__PURE__ */ new Map(),
|
|
4139
|
-
workflowParticipations: /* @__PURE__ */ new Map()
|
|
4421
|
+
workflowParticipations: /* @__PURE__ */ new Map(),
|
|
4422
|
+
// AI stores
|
|
4423
|
+
aiConversations: /* @__PURE__ */ new Map(),
|
|
4424
|
+
aiMessages: /* @__PURE__ */ new Map(),
|
|
4425
|
+
aiUserMemory: /* @__PURE__ */ new Map(),
|
|
4426
|
+
aiUsageMetrics: /* @__PURE__ */ new Map()
|
|
4140
4427
|
};
|
|
4141
4428
|
const adapter = {
|
|
4142
4429
|
objects: createMockObjectsRepository(stores),
|
|
@@ -4149,6 +4436,10 @@ function createMockAdapter() {
|
|
|
4149
4436
|
workflows: createMockWorkflowsRepository(stores),
|
|
4150
4437
|
workflowInstances: createMockWorkflowInstancesRepository(stores),
|
|
4151
4438
|
workflowParticipations: createMockWorkflowParticipationsRepository(stores),
|
|
4439
|
+
// AI repositories
|
|
4440
|
+
aiConversations: createMockAIConversationsRepository(stores),
|
|
4441
|
+
aiUserMemory: createMockAIUserMemoryRepository(stores),
|
|
4442
|
+
aiUsageMetrics: createMockAIUsageMetricsRepository(stores),
|
|
4152
4443
|
async transaction(callback) {
|
|
4153
4444
|
return await callback(adapter);
|
|
4154
4445
|
},
|
|
@@ -4168,6 +4459,10 @@ function createMockAdapter() {
|
|
|
4168
4459
|
stores.workflows.clear();
|
|
4169
4460
|
stores.workflowInstances.clear();
|
|
4170
4461
|
stores.workflowParticipations.clear();
|
|
4462
|
+
stores.aiConversations.clear();
|
|
4463
|
+
stores.aiMessages.clear();
|
|
4464
|
+
stores.aiUserMemory.clear();
|
|
4465
|
+
stores.aiUsageMetrics.clear();
|
|
4171
4466
|
}
|
|
4172
4467
|
};
|
|
4173
4468
|
return adapter;
|
|
@@ -9301,12 +9596,105 @@ var RecordQueryService = class extends BaseService {
|
|
|
9301
9596
|
}
|
|
9302
9597
|
};
|
|
9303
9598
|
|
|
9599
|
+
// src/runtime/services/record/record-resolver.service.ts
|
|
9600
|
+
var RecordResolverService = class extends BaseService {
|
|
9601
|
+
constructor(adapter) {
|
|
9602
|
+
super(adapter);
|
|
9603
|
+
}
|
|
9604
|
+
// ============================================================================
|
|
9605
|
+
// CACHED RECORD ACCESS
|
|
9606
|
+
// ============================================================================
|
|
9607
|
+
/**
|
|
9608
|
+
* Find a record by ID with caching.
|
|
9609
|
+
*
|
|
9610
|
+
* Uses the shared record cache for optimal performance.
|
|
9611
|
+
* Delegates to findByIds for consistent cache handling.
|
|
9612
|
+
*
|
|
9613
|
+
* @param id - Record ID
|
|
9614
|
+
* @returns Record or null if not found
|
|
9615
|
+
*/
|
|
9616
|
+
async findById(id) {
|
|
9617
|
+
if (!id) return null;
|
|
9618
|
+
const results = await this.findByIds([id]);
|
|
9619
|
+
return results[0] ?? null;
|
|
9620
|
+
}
|
|
9621
|
+
/**
|
|
9622
|
+
* Find multiple records by IDs with caching.
|
|
9623
|
+
*
|
|
9624
|
+
* Each record is cached individually for reuse across services.
|
|
9625
|
+
* Only fetches records not already in cache.
|
|
9626
|
+
*
|
|
9627
|
+
* @param ids - Record IDs to fetch
|
|
9628
|
+
* @returns Array of found records (missing IDs are not included)
|
|
9629
|
+
*/
|
|
9630
|
+
async findByIds(ids) {
|
|
9631
|
+
if (!ids || ids.length === 0) {
|
|
9632
|
+
return [];
|
|
9633
|
+
}
|
|
9634
|
+
const uniqueIds = [...new Set(ids)];
|
|
9635
|
+
return this.cachedByMany(
|
|
9636
|
+
"record",
|
|
9637
|
+
uniqueIds,
|
|
9638
|
+
(missingIds) => this.adapter.objectRecords.findByIds(missingIds),
|
|
9639
|
+
(record) => record.id,
|
|
9640
|
+
cacheTtl.records
|
|
9641
|
+
);
|
|
9642
|
+
}
|
|
9643
|
+
// ============================================================================
|
|
9644
|
+
// FACTORY METHODS
|
|
9645
|
+
// ============================================================================
|
|
9646
|
+
/**
|
|
9647
|
+
* Create a RelationLabelResolver callback for computeLabelWithRelations.
|
|
9648
|
+
*
|
|
9649
|
+
* Used by RelationService.resolveLabel() and ObjectSchemaService.
|
|
9650
|
+
*
|
|
9651
|
+
* @returns Callback that resolves record IDs to their labels (cached)
|
|
9652
|
+
*/
|
|
9653
|
+
createRelationLabelResolver() {
|
|
9654
|
+
return async (ids) => {
|
|
9655
|
+
const records = await this.findByIds(ids);
|
|
9656
|
+
return new Map(records.map((r) => [r.id, r.label]));
|
|
9657
|
+
};
|
|
9658
|
+
}
|
|
9659
|
+
/**
|
|
9660
|
+
* Create a LabelResolver interface for label computation helpers.
|
|
9661
|
+
*
|
|
9662
|
+
* Used by RecordService for computing record labels.
|
|
9663
|
+
*
|
|
9664
|
+
* @param relationService - RelationService for resolving relation display labels
|
|
9665
|
+
* @returns LabelResolver interface with cached record fetching
|
|
9666
|
+
*/
|
|
9667
|
+
createLabelResolver(relationService) {
|
|
9668
|
+
return {
|
|
9669
|
+
resolveRelationIds: (ids, attrId) => relationService.resolveIds(ids, attrId),
|
|
9670
|
+
findRecordLabels: (ids) => this.findByIds(ids)
|
|
9671
|
+
};
|
|
9672
|
+
}
|
|
9673
|
+
/**
|
|
9674
|
+
* Create a RollupCascadeContext for rollup recalculation.
|
|
9675
|
+
*
|
|
9676
|
+
* Used by RecordService after create/update/delete operations.
|
|
9677
|
+
*
|
|
9678
|
+
* @param rollupService - RollupService for recalculating rollups
|
|
9679
|
+
* @param schemaService - ObjectSchemaService for fetching schemas
|
|
9680
|
+
* @returns Context with cached record fetching
|
|
9681
|
+
*/
|
|
9682
|
+
createRollupContext(rollupService, schemaService) {
|
|
9683
|
+
return {
|
|
9684
|
+
rollupService,
|
|
9685
|
+
schemaService,
|
|
9686
|
+
findRecordsByIds: (ids) => this.findByIds(ids)
|
|
9687
|
+
};
|
|
9688
|
+
}
|
|
9689
|
+
};
|
|
9690
|
+
|
|
9304
9691
|
// src/runtime/services/record/relation.service.ts
|
|
9305
9692
|
var RelationService = class extends BaseService {
|
|
9306
9693
|
constructor(adapter, nativeRegistry, options) {
|
|
9307
9694
|
super(adapter);
|
|
9308
9695
|
this.schemaService = new ObjectSchemaService(adapter, nativeRegistry);
|
|
9309
|
-
this.queryService = options
|
|
9696
|
+
this.queryService = options.queryService;
|
|
9697
|
+
this.recordResolver = options.recordResolver;
|
|
9310
9698
|
}
|
|
9311
9699
|
/**
|
|
9312
9700
|
* Set the query service after construction.
|
|
@@ -9384,7 +9772,7 @@ var RelationService = class extends BaseService {
|
|
|
9384
9772
|
});
|
|
9385
9773
|
return errors;
|
|
9386
9774
|
}
|
|
9387
|
-
const records = await this.
|
|
9775
|
+
const records = await this.recordResolver.findByIds(ids);
|
|
9388
9776
|
const recordMap = new Map(records.map((r) => [r.id, r]));
|
|
9389
9777
|
const invalidIds = [];
|
|
9390
9778
|
for (const id of ids) {
|
|
@@ -9506,20 +9894,7 @@ var RelationService = class extends BaseService {
|
|
|
9506
9894
|
const result = query ? await queryService.searchRecords(objectSchema.id, query, queryOptions) : await queryService.listRecords(objectSchema.id, queryOptions);
|
|
9507
9895
|
totalCount += result.total;
|
|
9508
9896
|
for (const record of result.records) {
|
|
9509
|
-
|
|
9510
|
-
if (target.displayTemplate) {
|
|
9511
|
-
label = await computeLabelWithRelations(
|
|
9512
|
-
target.displayTemplate,
|
|
9513
|
-
record.values,
|
|
9514
|
-
objectSchema.attributes,
|
|
9515
|
-
async (ids) => {
|
|
9516
|
-
const linkedRecords = await this.adapter.objectRecords.findByIds(ids);
|
|
9517
|
-
return new Map(linkedRecords.map((r) => [r.id, r.label]));
|
|
9518
|
-
}
|
|
9519
|
-
);
|
|
9520
|
-
} else {
|
|
9521
|
-
label = record.label;
|
|
9522
|
-
}
|
|
9897
|
+
const label = await this.resolveLabel(record, objectSchema, target.displayTemplate);
|
|
9523
9898
|
allOptions.push({
|
|
9524
9899
|
id: record.id,
|
|
9525
9900
|
objectId: objectSchema.id,
|
|
@@ -9557,17 +9932,8 @@ var RelationService = class extends BaseService {
|
|
|
9557
9932
|
if (!ids || ids.length === 0) {
|
|
9558
9933
|
return [];
|
|
9559
9934
|
}
|
|
9560
|
-
const
|
|
9561
|
-
return
|
|
9562
|
-
"resolvedRelation",
|
|
9563
|
-
compositeIds,
|
|
9564
|
-
async (missingCompositeIds) => {
|
|
9565
|
-
const missingRecordIds = missingCompositeIds.map((c) => c.split(":")[1]);
|
|
9566
|
-
return this.fetchResolveIds(missingRecordIds, attributeId);
|
|
9567
|
-
},
|
|
9568
|
-
(item) => `${attributeId}:${item.id}`,
|
|
9569
|
-
cacheTtl.resolvedRelations
|
|
9570
|
-
);
|
|
9935
|
+
const result = await this.resolveIdsBatch([{ attributeId, ids }]);
|
|
9936
|
+
return result[attributeId] ?? [];
|
|
9571
9937
|
}
|
|
9572
9938
|
/**
|
|
9573
9939
|
* Resolve multiple attribute/IDs batches in a single operation.
|
|
@@ -9610,7 +9976,7 @@ var RelationService = class extends BaseService {
|
|
|
9610
9976
|
const allResolved = await this.cachedByMany(
|
|
9611
9977
|
"resolvedRelation",
|
|
9612
9978
|
allCompositeIds,
|
|
9613
|
-
(missingCompositeIds) => this.
|
|
9979
|
+
(missingCompositeIds) => this.resolveCompositeIds(missingCompositeIds),
|
|
9614
9980
|
(item) => item._compositeId,
|
|
9615
9981
|
cacheTtl.resolvedRelations
|
|
9616
9982
|
);
|
|
@@ -9631,10 +9997,12 @@ var RelationService = class extends BaseService {
|
|
|
9631
9997
|
return response;
|
|
9632
9998
|
}
|
|
9633
9999
|
/**
|
|
9634
|
-
* Internal method to
|
|
10000
|
+
* Internal method to resolve multiple composite IDs at once.
|
|
9635
10001
|
* Optimized for batch operations - single DB query for all records.
|
|
10002
|
+
*
|
|
10003
|
+
* @param compositeIds - Array of composite IDs in format "attributeId:recordId"
|
|
9636
10004
|
*/
|
|
9637
|
-
async
|
|
10005
|
+
async resolveCompositeIds(compositeIds) {
|
|
9638
10006
|
if (compositeIds.length === 0) {
|
|
9639
10007
|
return [];
|
|
9640
10008
|
}
|
|
@@ -9644,7 +10012,7 @@ var RelationService = class extends BaseService {
|
|
|
9644
10012
|
});
|
|
9645
10013
|
const uniqueRecordIds = [...new Set(parsed.map((p) => p.recordId))];
|
|
9646
10014
|
const uniqueAttributeIds = [...new Set(parsed.map((p) => p.attributeId))];
|
|
9647
|
-
const records = await this.
|
|
10015
|
+
const records = await this.recordResolver.findByIds(uniqueRecordIds);
|
|
9648
10016
|
if (records.length === 0) {
|
|
9649
10017
|
return [];
|
|
9650
10018
|
}
|
|
@@ -9669,20 +10037,7 @@ var RelationService = class extends BaseService {
|
|
|
9669
10037
|
const attribute = attributeMap.get(attributeId);
|
|
9670
10038
|
const targetConfig = attribute?.targets?.find((t) => t.object === objectSchema.name);
|
|
9671
10039
|
const customTemplate = targetConfig?.displayTemplate;
|
|
9672
|
-
|
|
9673
|
-
if (customTemplate) {
|
|
9674
|
-
label = await computeLabelWithRelations(
|
|
9675
|
-
customTemplate,
|
|
9676
|
-
record.values,
|
|
9677
|
-
objectSchema.attributes,
|
|
9678
|
-
async (nestedIds) => {
|
|
9679
|
-
const linkedRecords = await this.adapter.objectRecords.findByIds(nestedIds);
|
|
9680
|
-
return new Map(linkedRecords.map((r) => [r.id, r.label]));
|
|
9681
|
-
}
|
|
9682
|
-
);
|
|
9683
|
-
} else {
|
|
9684
|
-
label = record.label;
|
|
9685
|
-
}
|
|
10040
|
+
const label = await this.resolveLabel(record, objectSchema, customTemplate);
|
|
9686
10041
|
resolved.push({
|
|
9687
10042
|
_compositeId: compositeId,
|
|
9688
10043
|
id: record.id,
|
|
@@ -9696,59 +10051,22 @@ var RelationService = class extends BaseService {
|
|
|
9696
10051
|
return resolved;
|
|
9697
10052
|
}
|
|
9698
10053
|
/**
|
|
9699
|
-
*
|
|
9700
|
-
* Uses
|
|
9701
|
-
* then groups by objectId to minimize schema lookups.
|
|
10054
|
+
* Resolve the display label for a record.
|
|
10055
|
+
* Uses custom template if provided, otherwise falls back to pre-computed label.
|
|
9702
10056
|
*/
|
|
9703
|
-
async
|
|
9704
|
-
if (
|
|
9705
|
-
return
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
const recordsByObjectId = /* @__PURE__ */ new Map();
|
|
9713
|
-
for (const record of records) {
|
|
9714
|
-
const existing = recordsByObjectId.get(record.objectId) ?? [];
|
|
9715
|
-
existing.push(record);
|
|
9716
|
-
recordsByObjectId.set(record.objectId, existing);
|
|
9717
|
-
}
|
|
9718
|
-
const resolved = [];
|
|
9719
|
-
for (const [objectId, objectRecords] of recordsByObjectId) {
|
|
9720
|
-
const objectSchema = await this.schemaService.getObjectSchema(objectId);
|
|
9721
|
-
if (!objectSchema) {
|
|
9722
|
-
continue;
|
|
9723
|
-
}
|
|
9724
|
-
const targetConfig = attribute?.targets?.find((t) => t.object === objectSchema.name);
|
|
9725
|
-
const customTemplate = targetConfig?.displayTemplate;
|
|
9726
|
-
for (const record of objectRecords) {
|
|
9727
|
-
let label;
|
|
9728
|
-
if (customTemplate) {
|
|
9729
|
-
label = await computeLabelWithRelations(
|
|
9730
|
-
customTemplate,
|
|
9731
|
-
record.values,
|
|
9732
|
-
objectSchema.attributes,
|
|
9733
|
-
async (nestedIds) => {
|
|
9734
|
-
const linkedRecords = await this.adapter.objectRecords.findByIds(nestedIds);
|
|
9735
|
-
return new Map(linkedRecords.map((r) => [r.id, r.label]));
|
|
9736
|
-
}
|
|
9737
|
-
);
|
|
9738
|
-
} else {
|
|
9739
|
-
label = record.label;
|
|
10057
|
+
async resolveLabel(record, objectSchema, customTemplate) {
|
|
10058
|
+
if (customTemplate) {
|
|
10059
|
+
return computeLabelWithRelations(
|
|
10060
|
+
customTemplate,
|
|
10061
|
+
record.values,
|
|
10062
|
+
objectSchema.attributes,
|
|
10063
|
+
async (nestedIds) => {
|
|
10064
|
+
const linkedRecords = await this.recordResolver.findByIds(nestedIds);
|
|
10065
|
+
return new Map(linkedRecords.map((r) => [r.id, r.label]));
|
|
9740
10066
|
}
|
|
9741
|
-
|
|
9742
|
-
id: record.id,
|
|
9743
|
-
objectId: record.objectId,
|
|
9744
|
-
objectName: objectSchema.name,
|
|
9745
|
-
objectLabel: objectSchema.label,
|
|
9746
|
-
objectIcon: objectSchema.icon,
|
|
9747
|
-
label
|
|
9748
|
-
});
|
|
9749
|
-
}
|
|
10067
|
+
);
|
|
9750
10068
|
}
|
|
9751
|
-
return
|
|
10069
|
+
return record.label;
|
|
9752
10070
|
}
|
|
9753
10071
|
/**
|
|
9754
10072
|
* Find a relation attribute by ID.
|
|
@@ -9779,8 +10097,9 @@ var RelationService = class extends BaseService {
|
|
|
9779
10097
|
|
|
9780
10098
|
// src/runtime/services/record/rollup.service.ts
|
|
9781
10099
|
var RollupService = class extends BaseService {
|
|
9782
|
-
constructor(adapter) {
|
|
10100
|
+
constructor(adapter, options) {
|
|
9783
10101
|
super(adapter);
|
|
10102
|
+
this.recordResolver = options.recordResolver;
|
|
9784
10103
|
}
|
|
9785
10104
|
/**
|
|
9786
10105
|
* Calculate a rollup value for a record
|
|
@@ -9834,7 +10153,7 @@ var RollupService = class extends BaseService {
|
|
|
9834
10153
|
* Example: entreprise222 has relation "entreprises" → companies, rollup collects from companies
|
|
9835
10154
|
*/
|
|
9836
10155
|
async calculateForward(recordId, rollupAttr) {
|
|
9837
|
-
const record = await this.
|
|
10156
|
+
const record = await this.recordResolver.findById(recordId);
|
|
9838
10157
|
if (!record) {
|
|
9839
10158
|
return { value: null, recordCount: 0 };
|
|
9840
10159
|
}
|
|
@@ -9848,7 +10167,7 @@ var RollupService = class extends BaseService {
|
|
|
9848
10167
|
if (relatedIds.length === 0) {
|
|
9849
10168
|
return { value: this.getEmptyValue(rollupAttr.function), recordCount: 0 };
|
|
9850
10169
|
}
|
|
9851
|
-
const relatedRecords = await this.
|
|
10170
|
+
const relatedRecords = await this.recordResolver.findByIds(relatedIds);
|
|
9852
10171
|
if (relatedRecords.length === 0) {
|
|
9853
10172
|
return { value: this.getEmptyValue(rollupAttr.function), recordCount: 0 };
|
|
9854
10173
|
}
|
|
@@ -9859,7 +10178,7 @@ var RollupService = class extends BaseService {
|
|
|
9859
10178
|
* Example: Company has rollup on "orders", Order has relation "company" → companies
|
|
9860
10179
|
*/
|
|
9861
10180
|
async calculateReverse(recordId, rollupAttr, schema) {
|
|
9862
|
-
const record = await this.
|
|
10181
|
+
const record = await this.recordResolver.findById(recordId);
|
|
9863
10182
|
if (!record) {
|
|
9864
10183
|
return { value: null, recordCount: 0 };
|
|
9865
10184
|
}
|
|
@@ -10166,25 +10485,25 @@ var RecordService = class extends BaseService {
|
|
|
10166
10485
|
this.permissionService = options?.permissionService;
|
|
10167
10486
|
this.auditService = options?.auditService ?? (adapter.audit ? new AuditService(adapter) : void 0);
|
|
10168
10487
|
this.policyRegistry = options?.policyRegistry === null ? null : options?.policyRegistry ?? defaultPolicyRegistry;
|
|
10488
|
+
this.recordResolver = new RecordResolverService(adapter);
|
|
10169
10489
|
this.queryService = new RecordQueryService(adapter, this.schemaService, {
|
|
10170
10490
|
permissionService: this.permissionService,
|
|
10171
10491
|
policyRegistry: this.policyRegistry
|
|
10172
10492
|
});
|
|
10173
10493
|
this.relationService = new RelationService(adapter, registry, {
|
|
10174
|
-
queryService: this.queryService
|
|
10494
|
+
queryService: this.queryService,
|
|
10495
|
+
recordResolver: this.recordResolver
|
|
10496
|
+
});
|
|
10497
|
+
this.rollupService = new RollupService(adapter, {
|
|
10498
|
+
recordResolver: this.recordResolver
|
|
10175
10499
|
});
|
|
10176
10500
|
this.userService = new UserService(adapter);
|
|
10177
|
-
this.rollupService = new RollupService(adapter);
|
|
10178
10501
|
this.hookRegistry = options?.hookRegistry ?? new NoopHookRegistry();
|
|
10179
|
-
this.labelResolver =
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
rollupService: this.rollupService,
|
|
10185
|
-
schemaService: this.schemaService,
|
|
10186
|
-
findRecordsByIds: (ids) => this.adapter.objectRecords.findByIds(ids)
|
|
10187
|
-
};
|
|
10502
|
+
this.labelResolver = this.recordResolver.createLabelResolver(this.relationService);
|
|
10503
|
+
this.rollupContext = this.recordResolver.createRollupContext(
|
|
10504
|
+
this.rollupService,
|
|
10505
|
+
this.schemaService
|
|
10506
|
+
);
|
|
10188
10507
|
}
|
|
10189
10508
|
// ============================================================================
|
|
10190
10509
|
// CREATE
|
|
@@ -10579,15 +10898,19 @@ var RecordService = class extends BaseService {
|
|
|
10579
10898
|
}
|
|
10580
10899
|
};
|
|
10581
10900
|
|
|
10582
|
-
// src/runtime/services/record/
|
|
10583
|
-
var
|
|
10584
|
-
constructor(adapter) {
|
|
10585
|
-
|
|
10901
|
+
// src/runtime/services/record/formula-resolver.service.ts
|
|
10902
|
+
var FormulaResolverService = class extends BaseService {
|
|
10903
|
+
constructor(adapter, options) {
|
|
10904
|
+
super(adapter);
|
|
10905
|
+
this.recordResolver = options.recordResolver;
|
|
10586
10906
|
}
|
|
10907
|
+
// ============================================================================
|
|
10908
|
+
// RESOLUTION
|
|
10909
|
+
// ============================================================================
|
|
10587
10910
|
/**
|
|
10588
10911
|
* Resolve values from related records for formula evaluation
|
|
10589
10912
|
*
|
|
10590
|
-
*
|
|
10913
|
+
* Supports 1 level of relation traversal only.
|
|
10591
10914
|
*
|
|
10592
10915
|
* @param record - The source record
|
|
10593
10916
|
* @param schema - Schema of the source object
|
|
@@ -10596,7 +10919,6 @@ var RelationResolverService = class {
|
|
|
10596
10919
|
*
|
|
10597
10920
|
* @example
|
|
10598
10921
|
* ```typescript
|
|
10599
|
-
* // For an order with company relation
|
|
10600
10922
|
* const resolved = await resolver.resolveRelationValues(
|
|
10601
10923
|
* orderRecord,
|
|
10602
10924
|
* orderSchema,
|
|
@@ -10625,7 +10947,7 @@ var RelationResolverService = class {
|
|
|
10625
10947
|
if (idsToFetch.length === 0) {
|
|
10626
10948
|
return result;
|
|
10627
10949
|
}
|
|
10628
|
-
const relatedRecords = await this.
|
|
10950
|
+
const relatedRecords = await this.recordResolver.findByIds(idsToFetch);
|
|
10629
10951
|
for (const relatedRecord of relatedRecords) {
|
|
10630
10952
|
const attrName = attrIdMap.get(relatedRecord.id);
|
|
10631
10953
|
if (attrName) {
|
|
@@ -10671,7 +10993,7 @@ var RelationResolverService = class {
|
|
|
10671
10993
|
if (allIdsToFetch.size === 0) {
|
|
10672
10994
|
return resultMap;
|
|
10673
10995
|
}
|
|
10674
|
-
const relatedRecords = await this.
|
|
10996
|
+
const relatedRecords = await this.recordResolver.findByIds([...allIdsToFetch]);
|
|
10675
10997
|
const relatedRecordMap = new Map(relatedRecords.map((r) => [r.id, r]));
|
|
10676
10998
|
for (const record of records) {
|
|
10677
10999
|
const result = resultMap.get(record.id);
|
|
@@ -10704,10 +11026,12 @@ var RelationResolverService = class {
|
|
|
10704
11026
|
}
|
|
10705
11027
|
return flat;
|
|
10706
11028
|
}
|
|
11029
|
+
// ============================================================================
|
|
11030
|
+
// PRIVATE HELPERS
|
|
11031
|
+
// ============================================================================
|
|
10707
11032
|
/**
|
|
10708
11033
|
* Extract a single relation ID from a value
|
|
10709
11034
|
* Handles both single (string) and multi (array) relations
|
|
10710
|
-
* @internal
|
|
10711
11035
|
*/
|
|
10712
11036
|
extractSingleId(value) {
|
|
10713
11037
|
if (typeof value === "string" && value.length > 0) {
|
|
@@ -10726,9 +11050,9 @@ var RollupScheduler = class {
|
|
|
10726
11050
|
this.adapter = adapter;
|
|
10727
11051
|
this.getSchemaById = getSchemaById;
|
|
10728
11052
|
this.pending = /* @__PURE__ */ new Map();
|
|
10729
|
-
this.rollupService = new RollupService(adapter);
|
|
10730
|
-
this.debounceMs = options
|
|
10731
|
-
this.maxPending = options
|
|
11053
|
+
this.rollupService = new RollupService(adapter, { recordResolver: options.recordResolver });
|
|
11054
|
+
this.debounceMs = options.debounceMs ?? 100;
|
|
11055
|
+
this.maxPending = options.maxPending ?? 100;
|
|
10732
11056
|
}
|
|
10733
11057
|
/**
|
|
10734
11058
|
* Schedule a rollup recalculation for a parent record.
|
|
@@ -14163,10 +14487,11 @@ export {
|
|
|
14163
14487
|
createContextForRestore,
|
|
14164
14488
|
recalculateParentRollups,
|
|
14165
14489
|
RecordQueryService,
|
|
14490
|
+
RecordResolverService,
|
|
14166
14491
|
RelationService,
|
|
14167
14492
|
RollupService,
|
|
14168
14493
|
RecordService,
|
|
14169
|
-
|
|
14494
|
+
FormulaResolverService,
|
|
14170
14495
|
RollupScheduler,
|
|
14171
14496
|
WorkflowService,
|
|
14172
14497
|
WorkflowInstanceService,
|