@stndrds/schema 0.1.0-alpha.35 → 0.1.0-alpha.37
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-EIRY4I7Q.mjs → chunk-OXPVVEZS.mjs} +235 -73
- package/dist/{chunk-UCUFSIOX.js → chunk-TL7PFCPC.js} +302 -140
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +12 -6
- package/dist/index.mjs +7 -1
- package/dist/{runtime-BWBRGKY1.d.mts → runtime-Hwgvv9Bn.d.mts} +243 -32
- package/dist/{runtime-BWBRGKY1.d.ts → runtime-Hwgvv9Bn.d.ts} +243 -32
- package/dist/runtime.d.mts +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +8 -2
- package/dist/runtime.mjs +7 -1
- package/package.json +2 -2
|
@@ -2,6 +2,93 @@ import {
|
|
|
2
2
|
__require
|
|
3
3
|
} from "./chunk-Y6FXYEAI.mjs";
|
|
4
4
|
|
|
5
|
+
// src/runtime/cache.ts
|
|
6
|
+
var cacheKeys = {
|
|
7
|
+
// -------------------------------------------------------------------------
|
|
8
|
+
// Schemas - TTL: 1 hour (rarely change)
|
|
9
|
+
// -------------------------------------------------------------------------
|
|
10
|
+
/** Object schema by ID */
|
|
11
|
+
objectSchema: (tenantId, objectId) => `schema:${tenantId}:obj:${objectId}`,
|
|
12
|
+
/** Object schema by name */
|
|
13
|
+
objectSchemaByName: (tenantId, name) => `schema:${tenantId}:name:${name}`,
|
|
14
|
+
/** List of all object schemas */
|
|
15
|
+
objectSchemaList: (tenantId) => `schema:${tenantId}:list`,
|
|
16
|
+
// -------------------------------------------------------------------------
|
|
17
|
+
// Attributes - TTL: 1 hour (rarely change)
|
|
18
|
+
// -------------------------------------------------------------------------
|
|
19
|
+
/** Attributes for an object */
|
|
20
|
+
objectAttributes: (tenantId, objectId) => `attrs:${tenantId}:${objectId}`,
|
|
21
|
+
// -------------------------------------------------------------------------
|
|
22
|
+
// Permissions - TTL: 15 minutes (medium volatility)
|
|
23
|
+
// -------------------------------------------------------------------------
|
|
24
|
+
/** Effective permissions for a user */
|
|
25
|
+
userPermissions: (tenantId, userId) => `perms:${tenantId}:user:${userId}`,
|
|
26
|
+
// -------------------------------------------------------------------------
|
|
27
|
+
// Relations - TTL: 5 minutes (medium volatility)
|
|
28
|
+
// -------------------------------------------------------------------------
|
|
29
|
+
/** Relation options for an attribute */
|
|
30
|
+
relationOptions: (tenantId, attrId, hash) => `rel:${tenantId}:${attrId}:${hash}`,
|
|
31
|
+
// -------------------------------------------------------------------------
|
|
32
|
+
// Rollups - TTL: 2 minutes (high volatility)
|
|
33
|
+
// -------------------------------------------------------------------------
|
|
34
|
+
/** Computed rollup value for a record */
|
|
35
|
+
rollupValue: (tenantId, recordId, attrName) => `rollup:${tenantId}:${recordId}:${attrName}`,
|
|
36
|
+
// -------------------------------------------------------------------------
|
|
37
|
+
// Invalidation Patterns
|
|
38
|
+
// -------------------------------------------------------------------------
|
|
39
|
+
/** All schema cache for a tenant */
|
|
40
|
+
allSchemas: (tenantId) => `schema:${tenantId}:*`,
|
|
41
|
+
/** All attribute cache for a tenant */
|
|
42
|
+
allAttributes: (tenantId) => `attrs:${tenantId}:*`,
|
|
43
|
+
/** All permission cache for a tenant */
|
|
44
|
+
allPermissions: (tenantId) => `perms:${tenantId}:*`,
|
|
45
|
+
/** All relation cache for a tenant */
|
|
46
|
+
allRelations: (tenantId) => `rel:${tenantId}:*`,
|
|
47
|
+
/** All rollup cache for a tenant */
|
|
48
|
+
allRollups: (tenantId) => `rollup:${tenantId}:*`,
|
|
49
|
+
/** All rollups for a specific record */
|
|
50
|
+
rollupsByRecord: (tenantId, recordId) => `rollup:${tenantId}:${recordId}:*`,
|
|
51
|
+
/** All cache for a tenant (nuclear option) */
|
|
52
|
+
allForTenant: (tenantId) => `*:${tenantId}:*`
|
|
53
|
+
};
|
|
54
|
+
var cacheTtl = {
|
|
55
|
+
/** Object schemas - rarely change (1 hour) */
|
|
56
|
+
schema: 60 * 60 * 1e3,
|
|
57
|
+
/** Schema list - new objects more frequent (5 minutes) */
|
|
58
|
+
schemaList: 5 * 60 * 1e3,
|
|
59
|
+
/** Object attributes - rarely change (1 hour) */
|
|
60
|
+
attributes: 60 * 60 * 1e3,
|
|
61
|
+
/** User permissions - medium volatility (15 minutes) */
|
|
62
|
+
permissions: 15 * 60 * 1e3,
|
|
63
|
+
/** Relation options - medium volatility (5 minutes) */
|
|
64
|
+
relations: 5 * 60 * 1e3,
|
|
65
|
+
/** Rollup values - high volatility (2 minutes) */
|
|
66
|
+
rollup: 2 * 60 * 1e3
|
|
67
|
+
};
|
|
68
|
+
var NoopCacheAdapter = class {
|
|
69
|
+
get() {
|
|
70
|
+
return Promise.resolve(null);
|
|
71
|
+
}
|
|
72
|
+
set() {
|
|
73
|
+
return Promise.resolve();
|
|
74
|
+
}
|
|
75
|
+
delete() {
|
|
76
|
+
return Promise.resolve();
|
|
77
|
+
}
|
|
78
|
+
deletePattern() {
|
|
79
|
+
return Promise.resolve();
|
|
80
|
+
}
|
|
81
|
+
has() {
|
|
82
|
+
return Promise.resolve(false);
|
|
83
|
+
}
|
|
84
|
+
clear() {
|
|
85
|
+
return Promise.resolve();
|
|
86
|
+
}
|
|
87
|
+
getOrSet(_key, fetcher) {
|
|
88
|
+
return fetcher();
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
5
92
|
// src/runtime/client/types.ts
|
|
6
93
|
function formatRecord(record) {
|
|
7
94
|
return {
|
|
@@ -6284,6 +6371,7 @@ var ObjectSchemaService = class extends TenantAwareService {
|
|
|
6284
6371
|
this.adapter = adapter;
|
|
6285
6372
|
this.nativeRegistry = nativeRegistry;
|
|
6286
6373
|
this.auditService = options?.auditService;
|
|
6374
|
+
this.cache = options?.cache ?? adapter.cache;
|
|
6287
6375
|
}
|
|
6288
6376
|
/**
|
|
6289
6377
|
* Create a new custom object.
|
|
@@ -6359,6 +6447,7 @@ var ObjectSchemaService = class extends TenantAwareService {
|
|
|
6359
6447
|
attributes: createdAttributes,
|
|
6360
6448
|
system: false
|
|
6361
6449
|
};
|
|
6450
|
+
await this.invalidateSchemaCache();
|
|
6362
6451
|
if (this.auditService && this.userId) {
|
|
6363
6452
|
await this.auditService.logSchemaAction({
|
|
6364
6453
|
action: "object.created",
|
|
@@ -6406,6 +6495,7 @@ var ObjectSchemaService = class extends TenantAwareService {
|
|
|
6406
6495
|
// Always false for attributes added via API
|
|
6407
6496
|
config
|
|
6408
6497
|
});
|
|
6498
|
+
await this.invalidateSchemaCache();
|
|
6409
6499
|
if (this.auditService && this.userId) {
|
|
6410
6500
|
await this.auditService.logSchemaAction({
|
|
6411
6501
|
action: "attribute.created",
|
|
@@ -6485,6 +6575,7 @@ var ObjectSchemaService = class extends TenantAwareService {
|
|
|
6485
6575
|
});
|
|
6486
6576
|
}
|
|
6487
6577
|
}
|
|
6578
|
+
await this.invalidateSchemaCache();
|
|
6488
6579
|
if (updates.required !== void 0 && updates.required !== oldValues.required) {
|
|
6489
6580
|
const schema = await this.getObjectSchema(dbAttr.objectId);
|
|
6490
6581
|
await this.adapter.objectRecords.batchRefreshStatus(
|
|
@@ -6519,6 +6610,7 @@ var ObjectSchemaService = class extends TenantAwareService {
|
|
|
6519
6610
|
}
|
|
6520
6611
|
await this.adapter.attributes.delete(attributeId);
|
|
6521
6612
|
await this.adapter.objectRecords.removeAttributeData(dbAttr.objectId, dbAttr.name);
|
|
6613
|
+
await this.invalidateSchemaCache();
|
|
6522
6614
|
if (this.auditService && this.userId) {
|
|
6523
6615
|
await this.auditService.logSchemaAction({
|
|
6524
6616
|
action: "attribute.deleted",
|
|
@@ -6619,6 +6711,7 @@ var ObjectSchemaService = class extends TenantAwareService {
|
|
|
6619
6711
|
}
|
|
6620
6712
|
const dbAttributes = await this.adapter.attributes.findByObjectId(objectId);
|
|
6621
6713
|
const attributes = dbAttributes.map((attr) => this.convertDBAttributeToAttribute(attr));
|
|
6714
|
+
await this.invalidateSchemaCache();
|
|
6622
6715
|
if (updates.labelExpression !== void 0 && updates.labelExpression !== oldValues.labelExpression) {
|
|
6623
6716
|
const newExpression = updates.labelExpression;
|
|
6624
6717
|
await this.adapter.objectRecords.batchRefreshLabels(objectId, (values) => {
|
|
@@ -6656,6 +6749,7 @@ var ObjectSchemaService = class extends TenantAwareService {
|
|
|
6656
6749
|
throw new ObjectReferencedError(dbObject.name, referencingObjects);
|
|
6657
6750
|
}
|
|
6658
6751
|
await this.adapter.objects.delete(objectId);
|
|
6752
|
+
await this.invalidateSchemaCache();
|
|
6659
6753
|
if (this.auditService && this.userId) {
|
|
6660
6754
|
await this.auditService.logSchemaAction({
|
|
6661
6755
|
action: "object.deleted",
|
|
@@ -6699,10 +6793,26 @@ Reserved names: ${RESERVED_ATTRIBUTE_NAMES.join(", ")}`
|
|
|
6699
6793
|
* Get complete object schema (object attributes + system attributes)
|
|
6700
6794
|
* Includes system attributes (createdAt, updatedAt, createdBy, lastUpdatedBy)
|
|
6701
6795
|
*
|
|
6796
|
+
* Results are cached if a CacheAdapter is configured.
|
|
6797
|
+
*
|
|
6702
6798
|
* @param objectId - Object UUID from database
|
|
6703
6799
|
* @returns Complete ObjectDefinition with all attributes including system attributes
|
|
6704
6800
|
*/
|
|
6705
6801
|
async getObjectSchema(objectId) {
|
|
6802
|
+
if (this.cache) {
|
|
6803
|
+
const cacheKey = cacheKeys.objectSchema(this.tenantId, objectId);
|
|
6804
|
+
return this.cache.getOrSet(
|
|
6805
|
+
cacheKey,
|
|
6806
|
+
() => this.fetchObjectSchemaById(objectId),
|
|
6807
|
+
cacheTtl.schema
|
|
6808
|
+
);
|
|
6809
|
+
}
|
|
6810
|
+
return this.fetchObjectSchemaById(objectId);
|
|
6811
|
+
}
|
|
6812
|
+
/**
|
|
6813
|
+
* Internal method to fetch object schema by ID (no caching)
|
|
6814
|
+
*/
|
|
6815
|
+
async fetchObjectSchemaById(objectId) {
|
|
6706
6816
|
const dbObject = await this.adapter.objects.findById(objectId);
|
|
6707
6817
|
if (!dbObject) {
|
|
6708
6818
|
throw new Error(`Object with id "${objectId}" not found`);
|
|
@@ -6723,8 +6833,24 @@ Reserved names: ${RESERVED_ATTRIBUTE_NAMES.join(", ")}`
|
|
|
6723
6833
|
/**
|
|
6724
6834
|
* Get object schema by name (supports both native and custom objects).
|
|
6725
6835
|
* Automatically uses tenant context from AsyncLocalStorage.
|
|
6836
|
+
*
|
|
6837
|
+
* Results are cached if a CacheAdapter is configured.
|
|
6726
6838
|
*/
|
|
6727
6839
|
async getObjectSchemaByNameForTenant(name) {
|
|
6840
|
+
if (this.cache) {
|
|
6841
|
+
const cacheKey = cacheKeys.objectSchemaByName(this.tenantId, name);
|
|
6842
|
+
return this.cache.getOrSet(
|
|
6843
|
+
cacheKey,
|
|
6844
|
+
() => this.fetchObjectSchemaByName(name),
|
|
6845
|
+
cacheTtl.schema
|
|
6846
|
+
);
|
|
6847
|
+
}
|
|
6848
|
+
return this.fetchObjectSchemaByName(name);
|
|
6849
|
+
}
|
|
6850
|
+
/**
|
|
6851
|
+
* Internal method to fetch object schema by name (no caching)
|
|
6852
|
+
*/
|
|
6853
|
+
async fetchObjectSchemaByName(name) {
|
|
6728
6854
|
let dbObject = await this.adapter.objects.findByName(name);
|
|
6729
6855
|
if (!dbObject) {
|
|
6730
6856
|
dbObject = await this.adapter.objects.findSystemByName(name);
|
|
@@ -6737,11 +6863,32 @@ Reserved names: ${RESERVED_ATTRIBUTE_NAMES.join(", ")}`
|
|
|
6737
6863
|
/**
|
|
6738
6864
|
* List all object schemas.
|
|
6739
6865
|
* Automatically uses tenant context from AsyncLocalStorage.
|
|
6866
|
+
*
|
|
6867
|
+
* Results are cached if a CacheAdapter is configured.
|
|
6740
6868
|
*/
|
|
6741
6869
|
async listObjectSchemas() {
|
|
6870
|
+
if (this.cache) {
|
|
6871
|
+
const cacheKey = cacheKeys.objectSchemaList(this.tenantId);
|
|
6872
|
+
return this.cache.getOrSet(cacheKey, () => this.fetchObjectSchemaList(), cacheTtl.schemaList);
|
|
6873
|
+
}
|
|
6874
|
+
return this.fetchObjectSchemaList();
|
|
6875
|
+
}
|
|
6876
|
+
/**
|
|
6877
|
+
* Internal method to fetch all object schemas (no caching)
|
|
6878
|
+
*/
|
|
6879
|
+
async fetchObjectSchemaList() {
|
|
6742
6880
|
const dbObjects = await this.adapter.objects.list();
|
|
6743
6881
|
return Promise.all(dbObjects.map((dbObject) => this.buildObjectDefinition(dbObject)));
|
|
6744
6882
|
}
|
|
6883
|
+
/**
|
|
6884
|
+
* Invalidate all schema-related cache for the current tenant.
|
|
6885
|
+
* Called automatically after schema mutations.
|
|
6886
|
+
*/
|
|
6887
|
+
async invalidateSchemaCache() {
|
|
6888
|
+
if (!this.cache) return;
|
|
6889
|
+
await this.cache.deletePattern(cacheKeys.allSchemas(this.tenantId));
|
|
6890
|
+
await this.cache.deletePattern(cacheKeys.allAttributes(this.tenantId));
|
|
6891
|
+
}
|
|
6745
6892
|
// ============================================================================
|
|
6746
6893
|
// PRIVATE HELPERS - SCHEMA BUILDING
|
|
6747
6894
|
// ============================================================================
|
|
@@ -6951,17 +7098,13 @@ var PermissionService = class extends TenantAwareService {
|
|
|
6951
7098
|
constructor(adapter, options) {
|
|
6952
7099
|
super();
|
|
6953
7100
|
this.adapter = adapter;
|
|
6954
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
6955
|
-
/** Track pending permission fetches to prevent duplicate concurrent requests */
|
|
6956
|
-
this.pendingFetches = /* @__PURE__ */ new Map();
|
|
6957
7101
|
if (!adapter.permissions) {
|
|
6958
7102
|
throw new Error(
|
|
6959
7103
|
"PermissionService requires a DatabaseAdapter with permissions repository. Make sure your adapter implements the permissions property."
|
|
6960
7104
|
);
|
|
6961
7105
|
}
|
|
6962
7106
|
this.permissionsRepo = adapter.permissions;
|
|
6963
|
-
this.
|
|
6964
|
-
this.maxCacheSize = options?.maxCacheSize ?? 1e4;
|
|
7107
|
+
this.cache = options?.cache ?? adapter.cache ?? new NoopCacheAdapter();
|
|
6965
7108
|
this.auditService = options?.auditService;
|
|
6966
7109
|
}
|
|
6967
7110
|
// ============================================================================
|
|
@@ -7089,46 +7232,18 @@ var PermissionService = class extends TenantAwareService {
|
|
|
7089
7232
|
// ============================================================================
|
|
7090
7233
|
/**
|
|
7091
7234
|
* Get effective permissions for a user.
|
|
7092
|
-
* Results are cached with TTL
|
|
7235
|
+
* Results are cached using CacheAdapter with TTL.
|
|
7093
7236
|
*
|
|
7094
7237
|
* @param userProfileId - User profile ID
|
|
7095
7238
|
* @returns Merged permissions from all user's roles
|
|
7096
7239
|
*/
|
|
7097
7240
|
async getEffectivePermissions(userProfileId) {
|
|
7098
|
-
const cacheKey = this.
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
if (pendingFetch) {
|
|
7105
|
-
return pendingFetch;
|
|
7106
|
-
}
|
|
7107
|
-
const fetchPromise = this.fetchAndCachePermissions(userProfileId, cacheKey);
|
|
7108
|
-
this.pendingFetches.set(cacheKey, fetchPromise);
|
|
7109
|
-
try {
|
|
7110
|
-
return await fetchPromise;
|
|
7111
|
-
} finally {
|
|
7112
|
-
this.pendingFetches.delete(cacheKey);
|
|
7113
|
-
}
|
|
7114
|
-
}
|
|
7115
|
-
/**
|
|
7116
|
-
* Fetch permissions from database and cache the result
|
|
7117
|
-
* @internal
|
|
7118
|
-
*/
|
|
7119
|
-
async fetchAndCachePermissions(userProfileId, cacheKey) {
|
|
7120
|
-
const permissions = await this.permissionsRepo.getEffectivePermissions(userProfileId);
|
|
7121
|
-
if (this.cache.size >= this.maxCacheSize) {
|
|
7122
|
-
const firstKey = this.cache.keys().next().value;
|
|
7123
|
-
if (firstKey) {
|
|
7124
|
-
this.cache.delete(firstKey);
|
|
7125
|
-
}
|
|
7126
|
-
}
|
|
7127
|
-
this.cache.set(cacheKey, {
|
|
7128
|
-
permissions,
|
|
7129
|
-
expiresAt: Date.now() + this.cacheTtlMs
|
|
7130
|
-
});
|
|
7131
|
-
return permissions;
|
|
7241
|
+
const cacheKey = cacheKeys.userPermissions(this.tenantId, userProfileId);
|
|
7242
|
+
return this.cache.getOrSet(
|
|
7243
|
+
cacheKey,
|
|
7244
|
+
() => this.permissionsRepo.getEffectivePermissions(userProfileId),
|
|
7245
|
+
cacheTtl.permissions
|
|
7246
|
+
);
|
|
7132
7247
|
}
|
|
7133
7248
|
// ============================================================================
|
|
7134
7249
|
// CACHE MANAGEMENT
|
|
@@ -7137,24 +7252,15 @@ var PermissionService = class extends TenantAwareService {
|
|
|
7137
7252
|
* Invalidate cached permissions for a specific user.
|
|
7138
7253
|
* Call this after role/permission changes.
|
|
7139
7254
|
*/
|
|
7140
|
-
invalidateCache(userProfileId) {
|
|
7141
|
-
this.cache.delete(this.
|
|
7255
|
+
async invalidateCache(userProfileId) {
|
|
7256
|
+
await this.cache.delete(cacheKeys.userPermissions(this.tenantId, userProfileId));
|
|
7142
7257
|
}
|
|
7143
7258
|
/**
|
|
7144
|
-
* Invalidate all cached permissions.
|
|
7259
|
+
* Invalidate all cached permissions for the current tenant.
|
|
7145
7260
|
* Call this after bulk role/permission changes.
|
|
7146
7261
|
*/
|
|
7147
|
-
invalidateAllCache() {
|
|
7148
|
-
this.cache.
|
|
7149
|
-
}
|
|
7150
|
-
/**
|
|
7151
|
-
* Get current cache size (for monitoring).
|
|
7152
|
-
*/
|
|
7153
|
-
getCacheSize() {
|
|
7154
|
-
return this.cache.size;
|
|
7155
|
-
}
|
|
7156
|
-
getCacheKey(userProfileId) {
|
|
7157
|
-
return `${this.tenantId}:${userProfileId}`;
|
|
7262
|
+
async invalidateAllCache() {
|
|
7263
|
+
await this.cache.deletePattern(cacheKeys.allPermissions(this.tenantId));
|
|
7158
7264
|
}
|
|
7159
7265
|
// ============================================================================
|
|
7160
7266
|
// ROLE MANAGEMENT
|
|
@@ -7516,10 +7622,11 @@ var registry = new NativeObjectRegistryClass();
|
|
|
7516
7622
|
|
|
7517
7623
|
// src/runtime/services/relation.service.ts
|
|
7518
7624
|
var RelationService = class extends TenantAwareService {
|
|
7519
|
-
constructor(adapter, nativeRegistry) {
|
|
7625
|
+
constructor(adapter, nativeRegistry, options) {
|
|
7520
7626
|
super();
|
|
7521
7627
|
this.adapter = adapter;
|
|
7522
|
-
this.
|
|
7628
|
+
this.cache = options?.cache ?? adapter.cache;
|
|
7629
|
+
this.schemaService = new ObjectSchemaService(adapter, nativeRegistry, { cache: this.cache });
|
|
7523
7630
|
}
|
|
7524
7631
|
/**
|
|
7525
7632
|
* Validate all relation attributes in the data
|
|
@@ -7786,12 +7893,15 @@ var RelationService = class extends TenantAwareService {
|
|
|
7786
7893
|
|
|
7787
7894
|
// src/runtime/services/rollup.service.ts
|
|
7788
7895
|
var RollupService = class {
|
|
7789
|
-
constructor(adapter) {
|
|
7896
|
+
constructor(adapter, options) {
|
|
7790
7897
|
this.adapter = adapter;
|
|
7898
|
+
this.cache = options?.cache ?? adapter.cache;
|
|
7791
7899
|
}
|
|
7792
7900
|
/**
|
|
7793
7901
|
* Calculate a rollup value for a record
|
|
7794
7902
|
*
|
|
7903
|
+
* Results are cached if a CacheAdapter is configured.
|
|
7904
|
+
*
|
|
7795
7905
|
* @param recordId - ID of the parent record
|
|
7796
7906
|
* @param rollupAttr - Rollup attribute definition
|
|
7797
7907
|
* @param schema - Schema of the parent object
|
|
@@ -7815,6 +7925,21 @@ var RollupService = class {
|
|
|
7815
7925
|
* ```
|
|
7816
7926
|
*/
|
|
7817
7927
|
async calculate(recordId, rollupAttr, schema) {
|
|
7928
|
+
if (this.cache) {
|
|
7929
|
+
const tenantId = getTenantId();
|
|
7930
|
+
const cacheKey = cacheKeys.rollupValue(tenantId, recordId, rollupAttr.name);
|
|
7931
|
+
return this.cache.getOrSet(
|
|
7932
|
+
cacheKey,
|
|
7933
|
+
() => this.computeRollup(recordId, rollupAttr, schema),
|
|
7934
|
+
cacheTtl.rollup
|
|
7935
|
+
);
|
|
7936
|
+
}
|
|
7937
|
+
return this.computeRollup(recordId, rollupAttr, schema);
|
|
7938
|
+
}
|
|
7939
|
+
/**
|
|
7940
|
+
* Internal method to compute rollup value (no caching)
|
|
7941
|
+
*/
|
|
7942
|
+
async computeRollup(recordId, rollupAttr, schema) {
|
|
7818
7943
|
const relationAttr = schema.attributes.find(
|
|
7819
7944
|
(a) => a.type === "relation" && a.name === rollupAttr.relationAttribute
|
|
7820
7945
|
);
|
|
@@ -8017,9 +8142,14 @@ var RollupService = class {
|
|
|
8017
8142
|
return record;
|
|
8018
8143
|
}
|
|
8019
8144
|
const updates = {};
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8145
|
+
const results = await Promise.all(
|
|
8146
|
+
rollupAttrs.map(async (attr) => {
|
|
8147
|
+
const result = await this.calculate(record.id, attr, schema);
|
|
8148
|
+
return { name: attr.name, value: result.value };
|
|
8149
|
+
})
|
|
8150
|
+
);
|
|
8151
|
+
for (const { name, value } of results) {
|
|
8152
|
+
updates[name] = value;
|
|
8023
8153
|
}
|
|
8024
8154
|
return await this.adapter.objectRecords.update(record.id, updates);
|
|
8025
8155
|
}
|
|
@@ -8048,6 +8178,31 @@ var RollupService = class {
|
|
|
8048
8178
|
}
|
|
8049
8179
|
return [...new Set(affectedIds)];
|
|
8050
8180
|
}
|
|
8181
|
+
/**
|
|
8182
|
+
* Invalidate cached rollups for affected parent records.
|
|
8183
|
+
* Call this after a child record is created, updated, or deleted.
|
|
8184
|
+
*
|
|
8185
|
+
* @param affectedParentIds - Array of parent record IDs whose rollups need invalidation
|
|
8186
|
+
*/
|
|
8187
|
+
async invalidateAffectedRollups(affectedParentIds) {
|
|
8188
|
+
if (!this.cache || affectedParentIds.length === 0) return;
|
|
8189
|
+
const tenantId = getTenantId();
|
|
8190
|
+
const cache = this.cache;
|
|
8191
|
+
await Promise.all(
|
|
8192
|
+
affectedParentIds.map(
|
|
8193
|
+
(parentId) => cache.deletePattern(cacheKeys.rollupsByRecord(tenantId, parentId))
|
|
8194
|
+
)
|
|
8195
|
+
);
|
|
8196
|
+
}
|
|
8197
|
+
/**
|
|
8198
|
+
* Invalidate all cached rollups for the current tenant.
|
|
8199
|
+
* Use sparingly - prefer targeted invalidation.
|
|
8200
|
+
*/
|
|
8201
|
+
async invalidateAllRollups() {
|
|
8202
|
+
if (!this.cache) return;
|
|
8203
|
+
const tenantId = getTenantId();
|
|
8204
|
+
await this.cache.deletePattern(cacheKeys.allRollups(tenantId));
|
|
8205
|
+
}
|
|
8051
8206
|
/**
|
|
8052
8207
|
* Find records that have forward rollups pointing to the modified record.
|
|
8053
8208
|
*
|
|
@@ -8910,24 +9065,28 @@ var RecordService = class extends TenantAwareService {
|
|
|
8910
9065
|
const affectedParentIds = await this.rollupService.findAffectedParentRecords(record, schema);
|
|
8911
9066
|
if (affectedParentIds.length > 0) {
|
|
8912
9067
|
const parentRecords = await this.adapter.objectRecords.findByIds(affectedParentIds);
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
9068
|
+
await Promise.all(
|
|
9069
|
+
parentRecords.map(async (parentRecord) => {
|
|
9070
|
+
const parentSchema = await this.schemaService.getObjectSchema(parentRecord.objectId);
|
|
9071
|
+
const rollupAttrs = parentSchema.attributes.filter(
|
|
9072
|
+
(a) => a.type === "rollup"
|
|
9073
|
+
);
|
|
9074
|
+
if (rollupAttrs.length > 0) {
|
|
9075
|
+
await this.rollupService.recalculateAndUpdate(parentRecord, parentSchema);
|
|
9076
|
+
}
|
|
9077
|
+
})
|
|
9078
|
+
);
|
|
8922
9079
|
}
|
|
8923
9080
|
const affectedForwardRecords = await this.rollupService.findRecordsWithForwardRollup(
|
|
8924
9081
|
record,
|
|
8925
9082
|
schema
|
|
8926
9083
|
);
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
9084
|
+
await Promise.all(
|
|
9085
|
+
affectedForwardRecords.map(async (forwardRecord) => {
|
|
9086
|
+
const forwardSchema = await this.schemaService.getObjectSchema(forwardRecord.objectId);
|
|
9087
|
+
await this.rollupService.recalculateAndUpdate(forwardRecord, forwardSchema);
|
|
9088
|
+
})
|
|
9089
|
+
);
|
|
8931
9090
|
}
|
|
8932
9091
|
/**
|
|
8933
9092
|
* Permanently delete a record (hard delete)
|
|
@@ -10237,6 +10396,9 @@ export {
|
|
|
10237
10396
|
getMissingRequiredAttributes,
|
|
10238
10397
|
isRecordComplete,
|
|
10239
10398
|
computeRecordStatus,
|
|
10399
|
+
cacheKeys,
|
|
10400
|
+
cacheTtl,
|
|
10401
|
+
NoopCacheAdapter,
|
|
10240
10402
|
formatRecord,
|
|
10241
10403
|
formatRecords,
|
|
10242
10404
|
createDefaultState,
|