@stndrds/schema 0.1.0-alpha.51 → 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.
@@ -9596,12 +9596,105 @@ var RecordQueryService = class extends BaseService {
9596
9596
  }
9597
9597
  };
9598
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
+
9599
9691
  // src/runtime/services/record/relation.service.ts
9600
9692
  var RelationService = class extends BaseService {
9601
9693
  constructor(adapter, nativeRegistry, options) {
9602
9694
  super(adapter);
9603
9695
  this.schemaService = new ObjectSchemaService(adapter, nativeRegistry);
9604
- this.queryService = options?.queryService;
9696
+ this.queryService = options.queryService;
9697
+ this.recordResolver = options.recordResolver;
9605
9698
  }
9606
9699
  /**
9607
9700
  * Set the query service after construction.
@@ -9679,7 +9772,7 @@ var RelationService = class extends BaseService {
9679
9772
  });
9680
9773
  return errors;
9681
9774
  }
9682
- const records = await this.adapter.objectRecords.findByIds(ids);
9775
+ const records = await this.recordResolver.findByIds(ids);
9683
9776
  const recordMap = new Map(records.map((r) => [r.id, r]));
9684
9777
  const invalidIds = [];
9685
9778
  for (const id of ids) {
@@ -9801,20 +9894,7 @@ var RelationService = class extends BaseService {
9801
9894
  const result = query ? await queryService.searchRecords(objectSchema.id, query, queryOptions) : await queryService.listRecords(objectSchema.id, queryOptions);
9802
9895
  totalCount += result.total;
9803
9896
  for (const record of result.records) {
9804
- let label;
9805
- if (target.displayTemplate) {
9806
- label = await computeLabelWithRelations(
9807
- target.displayTemplate,
9808
- record.values,
9809
- objectSchema.attributes,
9810
- async (ids) => {
9811
- const linkedRecords = await this.adapter.objectRecords.findByIds(ids);
9812
- return new Map(linkedRecords.map((r) => [r.id, r.label]));
9813
- }
9814
- );
9815
- } else {
9816
- label = record.label;
9817
- }
9897
+ const label = await this.resolveLabel(record, objectSchema, target.displayTemplate);
9818
9898
  allOptions.push({
9819
9899
  id: record.id,
9820
9900
  objectId: objectSchema.id,
@@ -9852,17 +9932,8 @@ var RelationService = class extends BaseService {
9852
9932
  if (!ids || ids.length === 0) {
9853
9933
  return [];
9854
9934
  }
9855
- const compositeIds = ids.map((id) => `${attributeId}:${id}`);
9856
- return this.cachedByMany(
9857
- "resolvedRelation",
9858
- compositeIds,
9859
- async (missingCompositeIds) => {
9860
- const missingRecordIds = missingCompositeIds.map((c) => c.split(":")[1]);
9861
- return this.fetchResolveIds(missingRecordIds, attributeId);
9862
- },
9863
- (item) => `${attributeId}:${item.id}`,
9864
- cacheTtl.resolvedRelations
9865
- );
9935
+ const result = await this.resolveIdsBatch([{ attributeId, ids }]);
9936
+ return result[attributeId] ?? [];
9866
9937
  }
9867
9938
  /**
9868
9939
  * Resolve multiple attribute/IDs batches in a single operation.
@@ -9905,7 +9976,7 @@ var RelationService = class extends BaseService {
9905
9976
  const allResolved = await this.cachedByMany(
9906
9977
  "resolvedRelation",
9907
9978
  allCompositeIds,
9908
- (missingCompositeIds) => this.fetchResolveIdsBatch(missingCompositeIds),
9979
+ (missingCompositeIds) => this.resolveCompositeIds(missingCompositeIds),
9909
9980
  (item) => item._compositeId,
9910
9981
  cacheTtl.resolvedRelations
9911
9982
  );
@@ -9926,10 +9997,12 @@ var RelationService = class extends BaseService {
9926
9997
  return response;
9927
9998
  }
9928
9999
  /**
9929
- * Internal method to fetch and resolve multiple composite IDs at once.
10000
+ * Internal method to resolve multiple composite IDs at once.
9930
10001
  * Optimized for batch operations - single DB query for all records.
10002
+ *
10003
+ * @param compositeIds - Array of composite IDs in format "attributeId:recordId"
9931
10004
  */
9932
- async fetchResolveIdsBatch(compositeIds) {
10005
+ async resolveCompositeIds(compositeIds) {
9933
10006
  if (compositeIds.length === 0) {
9934
10007
  return [];
9935
10008
  }
@@ -9939,7 +10012,7 @@ var RelationService = class extends BaseService {
9939
10012
  });
9940
10013
  const uniqueRecordIds = [...new Set(parsed.map((p) => p.recordId))];
9941
10014
  const uniqueAttributeIds = [...new Set(parsed.map((p) => p.attributeId))];
9942
- const records = await this.adapter.objectRecords.findByIds(uniqueRecordIds);
10015
+ const records = await this.recordResolver.findByIds(uniqueRecordIds);
9943
10016
  if (records.length === 0) {
9944
10017
  return [];
9945
10018
  }
@@ -9964,20 +10037,7 @@ var RelationService = class extends BaseService {
9964
10037
  const attribute = attributeMap.get(attributeId);
9965
10038
  const targetConfig = attribute?.targets?.find((t) => t.object === objectSchema.name);
9966
10039
  const customTemplate = targetConfig?.displayTemplate;
9967
- let label;
9968
- if (customTemplate) {
9969
- label = await computeLabelWithRelations(
9970
- customTemplate,
9971
- record.values,
9972
- objectSchema.attributes,
9973
- async (nestedIds) => {
9974
- const linkedRecords = await this.adapter.objectRecords.findByIds(nestedIds);
9975
- return new Map(linkedRecords.map((r) => [r.id, r.label]));
9976
- }
9977
- );
9978
- } else {
9979
- label = record.label;
9980
- }
10040
+ const label = await this.resolveLabel(record, objectSchema, customTemplate);
9981
10041
  resolved.push({
9982
10042
  _compositeId: compositeId,
9983
10043
  id: record.id,
@@ -9991,59 +10051,22 @@ var RelationService = class extends BaseService {
9991
10051
  return resolved;
9992
10052
  }
9993
10053
  /**
9994
- * Internal method to fetch and resolve relation IDs (no caching).
9995
- * Uses batch fetching for performance - fetches all records in one query,
9996
- * 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.
9997
10056
  */
9998
- async fetchResolveIds(ids, attributeId) {
9999
- if (ids.length === 0) {
10000
- return [];
10001
- }
10002
- const attribute = await this.findAttributeById(attributeId);
10003
- const records = await this.adapter.objectRecords.findByIds(ids);
10004
- if (records.length === 0) {
10005
- return [];
10006
- }
10007
- const recordsByObjectId = /* @__PURE__ */ new Map();
10008
- for (const record of records) {
10009
- const existing = recordsByObjectId.get(record.objectId) ?? [];
10010
- existing.push(record);
10011
- recordsByObjectId.set(record.objectId, existing);
10012
- }
10013
- const resolved = [];
10014
- for (const [objectId, objectRecords] of recordsByObjectId) {
10015
- const objectSchema = await this.schemaService.getObjectSchema(objectId);
10016
- if (!objectSchema) {
10017
- continue;
10018
- }
10019
- const targetConfig = attribute?.targets?.find((t) => t.object === objectSchema.name);
10020
- const customTemplate = targetConfig?.displayTemplate;
10021
- for (const record of objectRecords) {
10022
- let label;
10023
- if (customTemplate) {
10024
- label = await computeLabelWithRelations(
10025
- customTemplate,
10026
- record.values,
10027
- objectSchema.attributes,
10028
- async (nestedIds) => {
10029
- const linkedRecords = await this.adapter.objectRecords.findByIds(nestedIds);
10030
- return new Map(linkedRecords.map((r) => [r.id, r.label]));
10031
- }
10032
- );
10033
- } else {
10034
- 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]));
10035
10066
  }
10036
- resolved.push({
10037
- id: record.id,
10038
- objectId: record.objectId,
10039
- objectName: objectSchema.name,
10040
- objectLabel: objectSchema.label,
10041
- objectIcon: objectSchema.icon,
10042
- label
10043
- });
10044
- }
10067
+ );
10045
10068
  }
10046
- return resolved;
10069
+ return record.label;
10047
10070
  }
10048
10071
  /**
10049
10072
  * Find a relation attribute by ID.
@@ -10074,8 +10097,9 @@ var RelationService = class extends BaseService {
10074
10097
 
10075
10098
  // src/runtime/services/record/rollup.service.ts
10076
10099
  var RollupService = class extends BaseService {
10077
- constructor(adapter) {
10100
+ constructor(adapter, options) {
10078
10101
  super(adapter);
10102
+ this.recordResolver = options.recordResolver;
10079
10103
  }
10080
10104
  /**
10081
10105
  * Calculate a rollup value for a record
@@ -10129,7 +10153,7 @@ var RollupService = class extends BaseService {
10129
10153
  * Example: entreprise222 has relation "entreprises" → companies, rollup collects from companies
10130
10154
  */
10131
10155
  async calculateForward(recordId, rollupAttr) {
10132
- const record = await this.adapter.objectRecords.findById(recordId);
10156
+ const record = await this.recordResolver.findById(recordId);
10133
10157
  if (!record) {
10134
10158
  return { value: null, recordCount: 0 };
10135
10159
  }
@@ -10143,7 +10167,7 @@ var RollupService = class extends BaseService {
10143
10167
  if (relatedIds.length === 0) {
10144
10168
  return { value: this.getEmptyValue(rollupAttr.function), recordCount: 0 };
10145
10169
  }
10146
- const relatedRecords = await this.adapter.objectRecords.findByIds(relatedIds);
10170
+ const relatedRecords = await this.recordResolver.findByIds(relatedIds);
10147
10171
  if (relatedRecords.length === 0) {
10148
10172
  return { value: this.getEmptyValue(rollupAttr.function), recordCount: 0 };
10149
10173
  }
@@ -10154,7 +10178,7 @@ var RollupService = class extends BaseService {
10154
10178
  * Example: Company has rollup on "orders", Order has relation "company" → companies
10155
10179
  */
10156
10180
  async calculateReverse(recordId, rollupAttr, schema) {
10157
- const record = await this.adapter.objectRecords.findById(recordId);
10181
+ const record = await this.recordResolver.findById(recordId);
10158
10182
  if (!record) {
10159
10183
  return { value: null, recordCount: 0 };
10160
10184
  }
@@ -10461,25 +10485,25 @@ var RecordService = class extends BaseService {
10461
10485
  this.permissionService = options?.permissionService;
10462
10486
  this.auditService = options?.auditService ?? (adapter.audit ? new AuditService(adapter) : void 0);
10463
10487
  this.policyRegistry = options?.policyRegistry === null ? null : options?.policyRegistry ?? defaultPolicyRegistry;
10488
+ this.recordResolver = new RecordResolverService(adapter);
10464
10489
  this.queryService = new RecordQueryService(adapter, this.schemaService, {
10465
10490
  permissionService: this.permissionService,
10466
10491
  policyRegistry: this.policyRegistry
10467
10492
  });
10468
10493
  this.relationService = new RelationService(adapter, registry, {
10469
- queryService: this.queryService
10494
+ queryService: this.queryService,
10495
+ recordResolver: this.recordResolver
10496
+ });
10497
+ this.rollupService = new RollupService(adapter, {
10498
+ recordResolver: this.recordResolver
10470
10499
  });
10471
10500
  this.userService = new UserService(adapter);
10472
- this.rollupService = new RollupService(adapter);
10473
10501
  this.hookRegistry = options?.hookRegistry ?? new NoopHookRegistry();
10474
- this.labelResolver = {
10475
- resolveRelationIds: (ids, attrId) => this.relationService.resolveIds(ids, attrId),
10476
- findRecordLabels: (ids) => this.adapter.objectRecords.findByIds(ids)
10477
- };
10478
- this.rollupContext = {
10479
- rollupService: this.rollupService,
10480
- schemaService: this.schemaService,
10481
- findRecordsByIds: (ids) => this.adapter.objectRecords.findByIds(ids)
10482
- };
10502
+ this.labelResolver = this.recordResolver.createLabelResolver(this.relationService);
10503
+ this.rollupContext = this.recordResolver.createRollupContext(
10504
+ this.rollupService,
10505
+ this.schemaService
10506
+ );
10483
10507
  }
10484
10508
  // ============================================================================
10485
10509
  // CREATE
@@ -10874,15 +10898,19 @@ var RecordService = class extends BaseService {
10874
10898
  }
10875
10899
  };
10876
10900
 
10877
- // src/runtime/services/record/relation-resolver.service.ts
10878
- var RelationResolverService = class {
10879
- constructor(adapter) {
10880
- this.adapter = adapter;
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;
10881
10906
  }
10907
+ // ============================================================================
10908
+ // RESOLUTION
10909
+ // ============================================================================
10882
10910
  /**
10883
10911
  * Resolve values from related records for formula evaluation
10884
10912
  *
10885
- * Phase 2: Supports 1 level of relation traversal only
10913
+ * Supports 1 level of relation traversal only.
10886
10914
  *
10887
10915
  * @param record - The source record
10888
10916
  * @param schema - Schema of the source object
@@ -10891,7 +10919,6 @@ var RelationResolverService = class {
10891
10919
  *
10892
10920
  * @example
10893
10921
  * ```typescript
10894
- * // For an order with company relation
10895
10922
  * const resolved = await resolver.resolveRelationValues(
10896
10923
  * orderRecord,
10897
10924
  * orderSchema,
@@ -10920,7 +10947,7 @@ var RelationResolverService = class {
10920
10947
  if (idsToFetch.length === 0) {
10921
10948
  return result;
10922
10949
  }
10923
- const relatedRecords = await this.adapter.objectRecords.findByIds(idsToFetch);
10950
+ const relatedRecords = await this.recordResolver.findByIds(idsToFetch);
10924
10951
  for (const relatedRecord of relatedRecords) {
10925
10952
  const attrName = attrIdMap.get(relatedRecord.id);
10926
10953
  if (attrName) {
@@ -10966,7 +10993,7 @@ var RelationResolverService = class {
10966
10993
  if (allIdsToFetch.size === 0) {
10967
10994
  return resultMap;
10968
10995
  }
10969
- const relatedRecords = await this.adapter.objectRecords.findByIds([...allIdsToFetch]);
10996
+ const relatedRecords = await this.recordResolver.findByIds([...allIdsToFetch]);
10970
10997
  const relatedRecordMap = new Map(relatedRecords.map((r) => [r.id, r]));
10971
10998
  for (const record of records) {
10972
10999
  const result = resultMap.get(record.id);
@@ -10999,10 +11026,12 @@ var RelationResolverService = class {
10999
11026
  }
11000
11027
  return flat;
11001
11028
  }
11029
+ // ============================================================================
11030
+ // PRIVATE HELPERS
11031
+ // ============================================================================
11002
11032
  /**
11003
11033
  * Extract a single relation ID from a value
11004
11034
  * Handles both single (string) and multi (array) relations
11005
- * @internal
11006
11035
  */
11007
11036
  extractSingleId(value) {
11008
11037
  if (typeof value === "string" && value.length > 0) {
@@ -11021,9 +11050,9 @@ var RollupScheduler = class {
11021
11050
  this.adapter = adapter;
11022
11051
  this.getSchemaById = getSchemaById;
11023
11052
  this.pending = /* @__PURE__ */ new Map();
11024
- this.rollupService = new RollupService(adapter);
11025
- this.debounceMs = options?.debounceMs ?? 100;
11026
- this.maxPending = options?.maxPending ?? 100;
11053
+ this.rollupService = new RollupService(adapter, { recordResolver: options.recordResolver });
11054
+ this.debounceMs = options.debounceMs ?? 100;
11055
+ this.maxPending = options.maxPending ?? 100;
11027
11056
  }
11028
11057
  /**
11029
11058
  * Schedule a rollup recalculation for a parent record.
@@ -14458,10 +14487,11 @@ export {
14458
14487
  createContextForRestore,
14459
14488
  recalculateParentRollups,
14460
14489
  RecordQueryService,
14490
+ RecordResolverService,
14461
14491
  RelationService,
14462
14492
  RollupService,
14463
14493
  RecordService,
14464
- RelationResolverService,
14494
+ FormulaResolverService,
14465
14495
  RollupScheduler,
14466
14496
  WorkflowService,
14467
14497
  WorkflowInstanceService,