@stndrds/schema 0.1.0-alpha.19 → 0.1.0-alpha.20

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/runtime.js CHANGED
@@ -144,7 +144,6 @@ __export(runtime_exports, {
144
144
  ViewService: () => ViewService,
145
145
  buildAuditChanges: () => buildAuditChanges,
146
146
  createMockAdapter: () => createMockAdapter,
147
- createRollupHooks: () => createRollupHooks,
148
147
  enrichValuesWithSelectLabels: () => enrichValuesWithSelectLabels,
149
148
  evaluateFormula: () => evaluateFormula,
150
149
  evaluateFormulaAttribute: () => evaluateFormulaAttribute,
@@ -166,7 +165,6 @@ __export(runtime_exports, {
166
165
  isLabelExpression: () => isLabelExpression,
167
166
  parsePath: () => parsePath,
168
167
  pathHasManyCardinality: () => pathHasManyCardinality,
169
- registerAllRollupHooks: () => registerAllRollupHooks,
170
168
  renderLabelExpression: () => renderLabelExpression,
171
169
  resolveMultiplePaths: () => resolveMultiplePaths,
172
170
  resolveSingleValue: () => resolveSingleValue,
@@ -565,260 +563,6 @@ var NoopHookRegistry = class {
565
563
  }
566
564
  };
567
565
 
568
- // src/runtime/services/rollup.service.ts
569
- var RollupService = class {
570
- constructor(adapter) {
571
- this.adapter = adapter;
572
- }
573
- /**
574
- * Calculate a rollup value for a record
575
- *
576
- * @param recordId - ID of the parent record
577
- * @param rollupAttr - Rollup attribute definition
578
- * @param schema - Schema of the parent object
579
- * @returns Computed rollup value
580
- *
581
- * @example
582
- * ```typescript
583
- * // Sum all order amounts for a company
584
- * const totalOrders = await rollupService.calculate(
585
- * "company-123",
586
- * {
587
- * type: "rollup",
588
- * name: "totalOrders",
589
- * relationAttribute: "orders",
590
- * targetAttribute: "amount",
591
- * function: "sum",
592
- * ...
593
- * },
594
- * companySchema
595
- * );
596
- * ```
597
- */
598
- async calculate(recordId, rollupAttr, schema, tenantId) {
599
- const relationAttr = schema.attributes.find(
600
- (a) => a.type === "relation" && a.name === rollupAttr.relationAttribute
601
- );
602
- if (!relationAttr) {
603
- return { value: null, recordCount: 0 };
604
- }
605
- const targetObjectName = relationAttr.targets[0]?.object;
606
- if (!targetObjectName) {
607
- return { value: null, recordCount: 0 };
608
- }
609
- const targetObject = await this.adapter.objects.findByName(tenantId, targetObjectName);
610
- if (!targetObject) {
611
- return { value: null, recordCount: 0 };
612
- }
613
- const relatedRecords = await this.adapter.objectRecords.findByRelation(
614
- tenantId,
615
- targetObject.id,
616
- rollupAttr.relationAttribute,
617
- recordId
618
- );
619
- if (relatedRecords.length === 0) {
620
- return { value: this.getEmptyValue(rollupAttr.function), recordCount: 0 };
621
- }
622
- const values = relatedRecords.map((r) => r.values[rollupAttr.targetAttribute]).filter((v) => v !== void 0);
623
- const aggregated = this.aggregate(values, rollupAttr.function);
624
- const result = typeof aggregated === "number" && rollupAttr.decimals !== void 0 ? Number(aggregated.toFixed(rollupAttr.decimals)) : aggregated;
625
- return { value: result, recordCount: relatedRecords.length };
626
- }
627
- /**
628
- * Calculate rollup values for multiple records (batched)
629
- *
630
- * More efficient than calling calculate() for each record individually.
631
- */
632
- async calculateForMany(recordIds, rollupAttr, schema, tenantId) {
633
- const results = /* @__PURE__ */ new Map();
634
- for (const id of recordIds) {
635
- results.set(id, { value: this.getEmptyValue(rollupAttr.function), recordCount: 0 });
636
- }
637
- await Promise.all(
638
- recordIds.map(async (id) => {
639
- const result = await this.calculate(id, rollupAttr, schema, tenantId);
640
- results.set(id, result);
641
- })
642
- );
643
- return results;
644
- }
645
- /**
646
- * Apply aggregation function to a set of values
647
- */
648
- aggregate(values, fn) {
649
- switch (fn) {
650
- case "sum":
651
- return this.sumNumbers(values);
652
- case "avg":
653
- return this.averageNumbers(values);
654
- case "min":
655
- return this.minNumbers(values);
656
- case "max":
657
- return this.maxNumbers(values);
658
- case "count":
659
- return values.length;
660
- case "countValues":
661
- return values.filter((v) => v != null && v !== "").length;
662
- case "countEmpty":
663
- return values.filter((v) => v == null || v === "").length;
664
- case "percentFilled": {
665
- if (values.length === 0) return 0;
666
- const filled = values.filter((v) => v != null && v !== "").length;
667
- return filled / values.length * 100;
668
- }
669
- case "percentEmpty": {
670
- if (values.length === 0) return 0;
671
- const empty = values.filter((v) => v == null || v === "").length;
672
- return empty / values.length * 100;
673
- }
674
- }
675
- }
676
- /**
677
- * Get the default empty value for a rollup function
678
- */
679
- getEmptyValue(fn) {
680
- switch (fn) {
681
- case "sum":
682
- case "count":
683
- case "countValues":
684
- case "countEmpty":
685
- case "percentEmpty":
686
- case "percentFilled":
687
- return 0;
688
- case "avg":
689
- case "min":
690
- case "max":
691
- return null;
692
- }
693
- }
694
- /**
695
- * Sum numeric values
696
- */
697
- sumNumbers(values) {
698
- return values.filter((v) => typeof v === "number" && !Number.isNaN(v)).reduce((sum, n) => sum + n, 0);
699
- }
700
- /**
701
- * Average numeric values
702
- */
703
- averageNumbers(values) {
704
- const nums = values.filter((v) => typeof v === "number" && !Number.isNaN(v));
705
- if (nums.length === 0) return null;
706
- return nums.reduce((sum, n) => sum + n, 0) / nums.length;
707
- }
708
- /**
709
- * Minimum numeric value
710
- */
711
- minNumbers(values) {
712
- const nums = values.filter((v) => typeof v === "number" && !Number.isNaN(v));
713
- if (nums.length === 0) return null;
714
- return Math.min(...nums);
715
- }
716
- /**
717
- * Maximum numeric value
718
- */
719
- maxNumbers(values) {
720
- const nums = values.filter((v) => typeof v === "number" && !Number.isNaN(v));
721
- if (nums.length === 0) return null;
722
- return Math.max(...nums);
723
- }
724
- /**
725
- * Recalculate all rollup attributes for a record and update it
726
- *
727
- * Called after related records change to keep rollups up-to-date.
728
- */
729
- async recalculateAndUpdate(record, schema, tenantId) {
730
- const rollupAttrs = schema.attributes.filter((a) => a.type === "rollup");
731
- if (rollupAttrs.length === 0) {
732
- return record;
733
- }
734
- const updates = {};
735
- for (const attr of rollupAttrs) {
736
- const result = await this.calculate(record.id, attr, schema, tenantId);
737
- updates[attr.name] = result.value;
738
- }
739
- return await this.adapter.objectRecords.update(record.id, updates);
740
- }
741
- /**
742
- * Find parent records that need rollup recalculation when a child record changes
743
- *
744
- * Used by hooks to determine which parent records to recalculate after
745
- * a child record is created, updated, or deleted.
746
- *
747
- * @param changedRecord - The record that was modified
748
- * @param changedSchema - Schema of the changed record's object
749
- * @returns Array of parent record IDs that need recalculation
750
- */
751
- async findAffectedParentRecords(changedRecord, changedSchema) {
752
- const affectedIds = [];
753
- const relationAttrs = changedSchema.attributes.filter(
754
- (a) => a.type === "relation"
755
- );
756
- for (const attr of relationAttrs) {
757
- const relatedId = changedRecord.values[attr.name];
758
- if (typeof relatedId === "string" && relatedId.length > 0) {
759
- affectedIds.push(relatedId);
760
- } else if (Array.isArray(relatedId)) {
761
- affectedIds.push(...relatedId.filter((id) => typeof id === "string"));
762
- }
763
- }
764
- return [...new Set(affectedIds)];
765
- }
766
- };
767
-
768
- // src/runtime/hooks/rollup.hooks.ts
769
- function createRollupHooks(objectName, config) {
770
- const rollupService = new RollupService(config.adapter);
771
- async function recalculateParentRollups(ctx) {
772
- const schema = await config.getSchemaById(ctx.objectId);
773
- if (!schema) return;
774
- const affectedParentIds = await rollupService.findAffectedParentRecords(ctx.record, schema);
775
- if (affectedParentIds.length === 0) return;
776
- const parentRecords = await config.adapter.objectRecords.findByIds(affectedParentIds);
777
- for (const parentRecord of parentRecords) {
778
- const parentSchema = await config.getSchemaById(parentRecord.objectId);
779
- if (!parentSchema) continue;
780
- const hasRollups = parentSchema.attributes.some(
781
- (a) => a.type === "rollup"
782
- );
783
- if (hasRollups) {
784
- await rollupService.recalculateAndUpdate(parentRecord, parentSchema, config.tenantId);
785
- }
786
- }
787
- }
788
- return [
789
- {
790
- type: "afterCreate",
791
- objectName,
792
- handler: recalculateParentRollups,
793
- priority: 100
794
- // Run after other hooks
795
- },
796
- {
797
- type: "afterUpdate",
798
- objectName,
799
- handler: recalculateParentRollups,
800
- priority: 100
801
- },
802
- {
803
- type: "afterDelete",
804
- objectName,
805
- handler: recalculateParentRollups,
806
- priority: 100
807
- }
808
- ];
809
- }
810
- function registerAllRollupHooks(hookRegistry, schemas, config) {
811
- for (const schema of schemas) {
812
- const hasRelations = schema.attributes.some((a) => a.type === "relation");
813
- if (hasRelations && hookRegistry.register) {
814
- const hooks = createRollupHooks(schema.name, config);
815
- for (const hook of hooks) {
816
- hookRegistry.register(hook);
817
- }
818
- }
819
- }
820
- }
821
-
822
566
  // src/utils.ts
823
567
  function generateId() {
824
568
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
@@ -3601,10 +3345,12 @@ var rollupConfigSchema = baseConfigSchema.extend({
3601
3345
  "countValues",
3602
3346
  "countEmpty",
3603
3347
  "percentEmpty",
3604
- "percentFilled"
3348
+ "percentFilled",
3349
+ "collect"
3605
3350
  ]),
3606
3351
  decimals: import_zod4.z.number().int().min(0).max(10).optional(),
3607
- materialize: import_zod4.z.boolean().optional()
3352
+ showAsOriginal: import_zod4.z.boolean().optional(),
3353
+ targetAttributeType: import_zod4.z.string().optional()
3608
3354
  });
3609
3355
  var attributeConfigSchemas = {
3610
3356
  text: textConfigSchema,
@@ -5450,6 +5196,263 @@ var RelationService = class {
5450
5196
  }
5451
5197
  };
5452
5198
 
5199
+ // src/runtime/services/rollup.service.ts
5200
+ var RollupService = class {
5201
+ constructor(adapter) {
5202
+ this.adapter = adapter;
5203
+ }
5204
+ /**
5205
+ * Calculate a rollup value for a record
5206
+ *
5207
+ * @param recordId - ID of the parent record
5208
+ * @param rollupAttr - Rollup attribute definition
5209
+ * @param schema - Schema of the parent object
5210
+ * @returns Computed rollup value
5211
+ *
5212
+ * @example
5213
+ * ```typescript
5214
+ * // Sum all order amounts for a company
5215
+ * const totalOrders = await rollupService.calculate(
5216
+ * "company-123",
5217
+ * {
5218
+ * type: "rollup",
5219
+ * name: "totalOrders",
5220
+ * relationAttribute: "orders",
5221
+ * targetAttribute: "amount",
5222
+ * function: "sum",
5223
+ * ...
5224
+ * },
5225
+ * companySchema
5226
+ * );
5227
+ * ```
5228
+ */
5229
+ async calculate(recordId, rollupAttr, schema) {
5230
+ const relationAttr = schema.attributes.find(
5231
+ (a) => a.type === "relation" && a.name === rollupAttr.relationAttribute
5232
+ );
5233
+ if (relationAttr) {
5234
+ return this.calculateForward(recordId, rollupAttr);
5235
+ }
5236
+ return this.calculateReverse(recordId, rollupAttr, schema);
5237
+ }
5238
+ /**
5239
+ * Forward pattern: this record has a relation attribute pointing to other records
5240
+ * Example: entreprise222 has relation "entreprises" → companies, rollup collects from companies
5241
+ */
5242
+ async calculateForward(recordId, rollupAttr) {
5243
+ const record = await this.adapter.objectRecords.findById(recordId);
5244
+ if (!record) {
5245
+ return { value: null, recordCount: 0 };
5246
+ }
5247
+ const relationValue = record.values[rollupAttr.relationAttribute];
5248
+ let relatedIds = [];
5249
+ if (typeof relationValue === "string" && relationValue.length > 0) {
5250
+ relatedIds = [relationValue];
5251
+ } else if (Array.isArray(relationValue)) {
5252
+ relatedIds = relationValue.filter((id) => typeof id === "string" && id.length > 0);
5253
+ }
5254
+ if (relatedIds.length === 0) {
5255
+ return { value: this.getEmptyValue(rollupAttr.function), recordCount: 0 };
5256
+ }
5257
+ const relatedRecords = await this.adapter.objectRecords.findByIds(relatedIds);
5258
+ if (relatedRecords.length === 0) {
5259
+ return { value: this.getEmptyValue(rollupAttr.function), recordCount: 0 };
5260
+ }
5261
+ return this.aggregateValues(relatedRecords, rollupAttr);
5262
+ }
5263
+ /**
5264
+ * Reverse pattern: other records have a relation pointing to this record
5265
+ * Example: Company has rollup on "orders", Order has relation "company" → companies
5266
+ */
5267
+ async calculateReverse(recordId, rollupAttr, schema) {
5268
+ const record = await this.adapter.objectRecords.findById(recordId);
5269
+ if (!record) {
5270
+ return { value: null, recordCount: 0 };
5271
+ }
5272
+ const parentObject = await this.adapter.objects.findById(record.objectId);
5273
+ if (!parentObject) {
5274
+ return { value: null, recordCount: 0 };
5275
+ }
5276
+ const tenantId = parentObject.tenantId;
5277
+ const sourceObjectName = rollupAttr.relationAttribute;
5278
+ const sourceObject = await this.adapter.objects.findByName(tenantId, sourceObjectName);
5279
+ if (!sourceObject) {
5280
+ return { value: null, recordCount: 0 };
5281
+ }
5282
+ const sourceAttributes = await this.adapter.attributes.findByObjectId(sourceObject.id);
5283
+ const reverseRelationAttr = sourceAttributes.find((attr) => {
5284
+ if (attr.type !== "relation") return false;
5285
+ const relationConfig = attr.config;
5286
+ return relationConfig?.targets?.some((t) => t.object === schema.name);
5287
+ });
5288
+ if (!reverseRelationAttr) {
5289
+ return { value: null, recordCount: 0 };
5290
+ }
5291
+ const relatedRecords = await this.adapter.objectRecords.findByRelation(
5292
+ tenantId,
5293
+ sourceObject.id,
5294
+ reverseRelationAttr.name,
5295
+ recordId
5296
+ );
5297
+ if (relatedRecords.length === 0) {
5298
+ return { value: this.getEmptyValue(rollupAttr.function), recordCount: 0 };
5299
+ }
5300
+ return this.aggregateValues(relatedRecords, rollupAttr);
5301
+ }
5302
+ /**
5303
+ * Extract and aggregate values from related records
5304
+ */
5305
+ aggregateValues(relatedRecords, rollupAttr) {
5306
+ const values = relatedRecords.map((r) => r.values[rollupAttr.targetAttribute]).filter((v) => v !== void 0 && v !== null);
5307
+ const aggregated = this.aggregate(values, rollupAttr.function);
5308
+ const result = typeof aggregated === "number" && rollupAttr.decimals !== void 0 ? Number(aggregated.toFixed(rollupAttr.decimals)) : aggregated;
5309
+ return { value: result, recordCount: relatedRecords.length };
5310
+ }
5311
+ /**
5312
+ * Calculate rollup values for multiple records (batched)
5313
+ *
5314
+ * More efficient than calling calculate() for each record individually.
5315
+ */
5316
+ async calculateForMany(recordIds, rollupAttr, schema) {
5317
+ const results = /* @__PURE__ */ new Map();
5318
+ for (const id of recordIds) {
5319
+ results.set(id, { value: this.getEmptyValue(rollupAttr.function), recordCount: 0 });
5320
+ }
5321
+ await Promise.all(
5322
+ recordIds.map(async (id) => {
5323
+ const result = await this.calculate(id, rollupAttr, schema);
5324
+ results.set(id, result);
5325
+ })
5326
+ );
5327
+ return results;
5328
+ }
5329
+ /**
5330
+ * Apply aggregation function to a set of values
5331
+ */
5332
+ aggregate(values, fn) {
5333
+ switch (fn) {
5334
+ case "sum":
5335
+ return this.sumNumbers(values);
5336
+ case "avg":
5337
+ return this.averageNumbers(values);
5338
+ case "min":
5339
+ return this.minNumbers(values);
5340
+ case "max":
5341
+ return this.maxNumbers(values);
5342
+ case "count":
5343
+ return values.length;
5344
+ case "countValues":
5345
+ return values.filter((v) => v != null && v !== "").length;
5346
+ case "countEmpty":
5347
+ return values.filter((v) => v == null || v === "").length;
5348
+ case "percentFilled": {
5349
+ if (values.length === 0) return 0;
5350
+ const filled = values.filter((v) => v != null && v !== "").length;
5351
+ return filled / values.length * 100;
5352
+ }
5353
+ case "percentEmpty": {
5354
+ if (values.length === 0) return 0;
5355
+ const empty = values.filter((v) => v == null || v === "").length;
5356
+ return empty / values.length * 100;
5357
+ }
5358
+ case "collect":
5359
+ return values;
5360
+ }
5361
+ }
5362
+ /**
5363
+ * Get the default empty value for a rollup function
5364
+ */
5365
+ getEmptyValue(fn) {
5366
+ switch (fn) {
5367
+ case "sum":
5368
+ case "count":
5369
+ case "countValues":
5370
+ case "countEmpty":
5371
+ case "percentEmpty":
5372
+ case "percentFilled":
5373
+ return 0;
5374
+ case "avg":
5375
+ case "min":
5376
+ case "max":
5377
+ return null;
5378
+ case "collect":
5379
+ return [];
5380
+ }
5381
+ }
5382
+ /**
5383
+ * Sum numeric values
5384
+ */
5385
+ sumNumbers(values) {
5386
+ return values.filter((v) => typeof v === "number" && !Number.isNaN(v)).reduce((sum, n) => sum + n, 0);
5387
+ }
5388
+ /**
5389
+ * Average numeric values
5390
+ */
5391
+ averageNumbers(values) {
5392
+ const nums = values.filter((v) => typeof v === "number" && !Number.isNaN(v));
5393
+ if (nums.length === 0) return null;
5394
+ return nums.reduce((sum, n) => sum + n, 0) / nums.length;
5395
+ }
5396
+ /**
5397
+ * Minimum numeric value
5398
+ */
5399
+ minNumbers(values) {
5400
+ const nums = values.filter((v) => typeof v === "number" && !Number.isNaN(v));
5401
+ if (nums.length === 0) return null;
5402
+ return Math.min(...nums);
5403
+ }
5404
+ /**
5405
+ * Maximum numeric value
5406
+ */
5407
+ maxNumbers(values) {
5408
+ const nums = values.filter((v) => typeof v === "number" && !Number.isNaN(v));
5409
+ if (nums.length === 0) return null;
5410
+ return Math.max(...nums);
5411
+ }
5412
+ /**
5413
+ * Recalculate all rollup attributes for a record and update it
5414
+ *
5415
+ * Called after related records change to keep rollups up-to-date.
5416
+ */
5417
+ async recalculateAndUpdate(record, schema) {
5418
+ const rollupAttrs = schema.attributes.filter((a) => a.type === "rollup");
5419
+ if (rollupAttrs.length === 0) {
5420
+ return record;
5421
+ }
5422
+ const updates = {};
5423
+ for (const attr of rollupAttrs) {
5424
+ const result = await this.calculate(record.id, attr, schema);
5425
+ updates[attr.name] = result.value;
5426
+ }
5427
+ return await this.adapter.objectRecords.update(record.id, updates);
5428
+ }
5429
+ /**
5430
+ * Find parent records that need rollup recalculation when a child record changes
5431
+ *
5432
+ * Used by hooks to determine which parent records to recalculate after
5433
+ * a child record is created, updated, or deleted.
5434
+ *
5435
+ * @param changedRecord - The record that was modified
5436
+ * @param changedSchema - Schema of the changed record's object
5437
+ * @returns Array of parent record IDs that need recalculation
5438
+ */
5439
+ async findAffectedParentRecords(changedRecord, changedSchema) {
5440
+ const affectedIds = [];
5441
+ const relationAttrs = changedSchema.attributes.filter(
5442
+ (a) => a.type === "relation"
5443
+ );
5444
+ for (const attr of relationAttrs) {
5445
+ const relatedId = changedRecord.values[attr.name];
5446
+ if (typeof relatedId === "string" && relatedId.length > 0) {
5447
+ affectedIds.push(relatedId);
5448
+ } else if (Array.isArray(relatedId)) {
5449
+ affectedIds.push(...relatedId.filter((id) => typeof id === "string"));
5450
+ }
5451
+ }
5452
+ return [...new Set(affectedIds)];
5453
+ }
5454
+ };
5455
+
5453
5456
  // src/runtime/services/user.service.ts
5454
5457
  var UserService = class {
5455
5458
  constructor(adapter, tenantId) {
@@ -5575,6 +5578,7 @@ var RecordService = class {
5575
5578
  this.schemaService = new ObjectSchemaService(adapter, registry);
5576
5579
  this.relationService = new RelationService(adapter, registry);
5577
5580
  this.userService = new UserService(adapter, tenantId);
5581
+ this.rollupService = new RollupService(adapter);
5578
5582
  this.hookRegistry = options?.hookRegistry ?? new NoopHookRegistry();
5579
5583
  this.permissionService = options?.permissionService;
5580
5584
  this.auditService = options?.auditService ?? (adapter.audit ? new AuditService(adapter, tenantId) : void 0);
@@ -5736,6 +5740,7 @@ var RecordService = class {
5736
5740
  };
5737
5741
  await this.hookRegistry.execute("afterCreate", schema.name, afterCtx);
5738
5742
  }
5743
+ await this.recalculateParentRollups(record, schema);
5739
5744
  if (this.auditService && this.userId) {
5740
5745
  await this.auditService.logRecordAction({
5741
5746
  action: "record.created",
@@ -5851,6 +5856,7 @@ var RecordService = class {
5851
5856
  };
5852
5857
  await this.hookRegistry.execute("afterUpdate", schema.name, afterCtx);
5853
5858
  }
5859
+ await this.recalculateParentRollups(updated, schema);
5854
5860
  if (this.auditService && this.userId && changedAttributes.length > 0) {
5855
5861
  const changes = changedAttributes.map((attr) => ({
5856
5862
  field: attr,
@@ -5978,6 +5984,7 @@ var RecordService = class {
5978
5984
  if (!options?.skipHooks) {
5979
5985
  await this.hookRegistry.execute("afterDelete", schema.name, hookCtx);
5980
5986
  }
5987
+ await this.recalculateParentRollups(record, schema);
5981
5988
  if (this.auditService && this.userId) {
5982
5989
  await this.auditService.logRecordAction({
5983
5990
  action: "record.deleted",
@@ -6107,6 +6114,39 @@ var RecordService = class {
6107
6114
  }
6108
6115
  return records.map((record) => this.enrichWithFormulas(record, schema));
6109
6116
  }
6117
+ /**
6118
+ * Recalculate rollups after a record changes
6119
+ *
6120
+ * This handles two cases:
6121
+ * 1. The record itself has rollups (e.g., aggregating from related records it points to)
6122
+ * 2. Parent records have rollups that aggregate from this record
6123
+ *
6124
+ * @param record - The record that was modified
6125
+ * @param schema - Schema of the record's object
6126
+ * @internal
6127
+ */
6128
+ async recalculateParentRollups(record, schema) {
6129
+ const ownRollupAttrs = schema.attributes.filter(
6130
+ (a) => a.type === "rollup"
6131
+ );
6132
+ if (ownRollupAttrs.length > 0) {
6133
+ await this.rollupService.recalculateAndUpdate(record, schema);
6134
+ }
6135
+ const affectedParentIds = await this.rollupService.findAffectedParentRecords(record, schema);
6136
+ if (affectedParentIds.length === 0) {
6137
+ return;
6138
+ }
6139
+ const parentRecords = await this.adapter.objectRecords.findByIds(affectedParentIds);
6140
+ for (const parentRecord of parentRecords) {
6141
+ const parentSchema = await this.schemaService.getObjectSchema(parentRecord.objectId);
6142
+ const rollupAttrs = parentSchema.attributes.filter(
6143
+ (a) => a.type === "rollup"
6144
+ );
6145
+ if (rollupAttrs.length > 0) {
6146
+ await this.rollupService.recalculateAndUpdate(parentRecord, parentSchema);
6147
+ }
6148
+ }
6149
+ }
6110
6150
  /**
6111
6151
  * Permanently delete a record (hard delete)
6112
6152
  *
@@ -6380,7 +6420,7 @@ var RollupScheduler = class {
6380
6420
  clearTimeout(existing.timeout);
6381
6421
  }
6382
6422
  const timeout = setTimeout(async () => {
6383
- await this.executeRecalculation(parentId, parentObjectId, tenantId);
6423
+ await this.executeRecalculation(parentId, parentObjectId);
6384
6424
  this.pending.delete(key);
6385
6425
  }, this.debounceMs);
6386
6426
  this.pending.set(key, { parentId, parentObjectId, tenantId, timeout });
@@ -6407,19 +6447,19 @@ var RollupScheduler = class {
6407
6447
  const schema = await this.getSchemaById(record.objectId);
6408
6448
  const tenantId = tenantMap.get(record.id);
6409
6449
  if (schema && tenantId) {
6410
- await this.rollupService.recalculateAndUpdate(record, schema, tenantId);
6450
+ await this.rollupService.recalculateAndUpdate(record, schema);
6411
6451
  }
6412
6452
  }
6413
6453
  }
6414
6454
  /**
6415
6455
  * Execute a single recalculation
6416
6456
  */
6417
- async executeRecalculation(parentId, parentObjectId, tenantId) {
6457
+ async executeRecalculation(parentId, parentObjectId) {
6418
6458
  const record = await this.adapter.objectRecords.findById(parentId);
6419
6459
  if (!record) return;
6420
6460
  const schema = await this.getSchemaById(parentObjectId);
6421
6461
  if (!schema) return;
6422
- await this.rollupService.recalculateAndUpdate(record, schema, tenantId);
6462
+ await this.rollupService.recalculateAndUpdate(record, schema);
6423
6463
  }
6424
6464
  /**
6425
6465
  * Get number of pending recalculations
@@ -7231,7 +7271,6 @@ var NoopGeocodingAdapter = class {
7231
7271
  ViewService,
7232
7272
  buildAuditChanges,
7233
7273
  createMockAdapter,
7234
- createRollupHooks,
7235
7274
  enrichValuesWithSelectLabels,
7236
7275
  evaluateFormula,
7237
7276
  evaluateFormulaAttribute,
@@ -7253,7 +7292,6 @@ var NoopGeocodingAdapter = class {
7253
7292
  isLabelExpression,
7254
7293
  parsePath,
7255
7294
  pathHasManyCardinality,
7256
- registerAllRollupHooks,
7257
7295
  renderLabelExpression,
7258
7296
  resolveMultiplePaths,
7259
7297
  resolveSingleValue,
package/dist/runtime.mjs CHANGED
@@ -21,7 +21,6 @@ import {
21
21
  ViewService,
22
22
  buildAuditChanges,
23
23
  createMockAdapter,
24
- createRollupHooks,
25
24
  enrichValuesWithSelectLabels,
26
25
  evaluateFormula,
27
26
  evaluateFormulaAttribute,
@@ -43,7 +42,6 @@ import {
43
42
  isLabelExpression,
44
43
  parsePath,
45
44
  pathHasManyCardinality,
46
- registerAllRollupHooks,
47
45
  renderLabelExpression,
48
46
  resolveMultiplePaths,
49
47
  resolveSingleValue,
@@ -55,7 +53,7 @@ import {
55
53
  validatePath,
56
54
  verifyNativeObjectsSync,
57
55
  verifyNativeViewsSync
58
- } from "./chunk-JD2MVLTW.mjs";
56
+ } from "./chunk-ZU5SR355.mjs";
59
57
  export {
60
58
  AuditService,
61
59
  DEFAULT_LABEL_FALLBACK,
@@ -79,7 +77,6 @@ export {
79
77
  ViewService,
80
78
  buildAuditChanges,
81
79
  createMockAdapter,
82
- createRollupHooks,
83
80
  enrichValuesWithSelectLabels,
84
81
  evaluateFormula,
85
82
  evaluateFormulaAttribute,
@@ -101,7 +98,6 @@ export {
101
98
  isLabelExpression,
102
99
  parsePath,
103
100
  pathHasManyCardinality,
104
- registerAllRollupHooks,
105
101
  renderLabelExpression,
106
102
  resolveMultiplePaths,
107
103
  resolveSingleValue,