@objectstack/metadata 5.0.0 → 5.2.0
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/index.cjs +116 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -2
- package/dist/index.d.ts +23 -2
- package/dist/index.js +116 -37
- package/dist/index.js.map +1 -1
- package/dist/node.cjs +116 -37
- package/dist/node.cjs.map +1 -1
- package/dist/node.js +116 -37
- package/dist/node.js.map +1 -1
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -799,7 +799,6 @@ var DatabaseLoader = class {
|
|
|
799
799
|
/**
|
|
800
800
|
* Create a history record for a metadata change.
|
|
801
801
|
*
|
|
802
|
-
* @param metadataId - The metadata record ID
|
|
803
802
|
* @param type - Metadata type
|
|
804
803
|
* @param name - Metadata name
|
|
805
804
|
* @param version - Version number
|
|
@@ -809,7 +808,7 @@ var DatabaseLoader = class {
|
|
|
809
808
|
* @param changeNote - Optional change description
|
|
810
809
|
* @param recordedBy - Optional user who made the change
|
|
811
810
|
*/
|
|
812
|
-
async createHistoryRecord(
|
|
811
|
+
async createHistoryRecord(type, name, version, metadata, operationType, previousChecksum, changeNote, recordedBy) {
|
|
813
812
|
if (!this.trackHistory) return;
|
|
814
813
|
await this.ensureHistorySchema();
|
|
815
814
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -822,7 +821,6 @@ var DatabaseLoader = class {
|
|
|
822
821
|
const eventSeq = await this.nextEventSeq();
|
|
823
822
|
const historyRecord = {
|
|
824
823
|
id: historyId,
|
|
825
|
-
metadataId,
|
|
826
824
|
name,
|
|
827
825
|
type,
|
|
828
826
|
version,
|
|
@@ -839,7 +837,6 @@ var DatabaseLoader = class {
|
|
|
839
837
|
await this._create(this.historyTableName, {
|
|
840
838
|
id: historyRecord.id,
|
|
841
839
|
event_seq: eventSeq,
|
|
842
|
-
metadata_id: historyRecord.metadataId,
|
|
843
840
|
name: historyRecord.name,
|
|
844
841
|
type: historyRecord.type,
|
|
845
842
|
version: historyRecord.version,
|
|
@@ -1027,12 +1024,9 @@ var DatabaseLoader = class {
|
|
|
1027
1024
|
async getHistoryRecord(type, name, version) {
|
|
1028
1025
|
if (!this.trackHistory) return null;
|
|
1029
1026
|
await this.ensureHistorySchema();
|
|
1030
|
-
const metadataRow = await this._findOne(this.tableName, {
|
|
1031
|
-
where: this.baseFilter(type, name)
|
|
1032
|
-
});
|
|
1033
|
-
if (!metadataRow) return null;
|
|
1034
1027
|
const filter = {
|
|
1035
|
-
|
|
1028
|
+
type,
|
|
1029
|
+
name,
|
|
1036
1030
|
version
|
|
1037
1031
|
};
|
|
1038
1032
|
if (this.organizationId) {
|
|
@@ -1044,7 +1038,6 @@ var DatabaseLoader = class {
|
|
|
1044
1038
|
if (!row) return null;
|
|
1045
1039
|
return {
|
|
1046
1040
|
id: row.id,
|
|
1047
|
-
metadataId: row.metadata_id,
|
|
1048
1041
|
name: row.name,
|
|
1049
1042
|
type: row.type,
|
|
1050
1043
|
version: row.version,
|
|
@@ -1069,14 +1062,9 @@ var DatabaseLoader = class {
|
|
|
1069
1062
|
}
|
|
1070
1063
|
await this.ensureSchema();
|
|
1071
1064
|
await this.ensureHistorySchema();
|
|
1072
|
-
const filter = { type, name };
|
|
1073
|
-
if (this.organizationId) filter.organization_id = this.organizationId;
|
|
1074
|
-
const metadataRecord = await this._findOne(this.tableName, { where: filter });
|
|
1075
|
-
if (!metadataRecord) {
|
|
1076
|
-
return { records: [], total: 0, hasMore: false };
|
|
1077
|
-
}
|
|
1078
1065
|
const historyFilter = {
|
|
1079
|
-
|
|
1066
|
+
type,
|
|
1067
|
+
name
|
|
1080
1068
|
};
|
|
1081
1069
|
if (this.organizationId) historyFilter.organization_id = this.organizationId;
|
|
1082
1070
|
if (options?.operationType) historyFilter.operation_type = options.operationType;
|
|
@@ -1107,7 +1095,6 @@ var DatabaseLoader = class {
|
|
|
1107
1095
|
const parsedMetadata = typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata;
|
|
1108
1096
|
return {
|
|
1109
1097
|
id: row.id,
|
|
1110
|
-
metadataId: row.metadata_id,
|
|
1111
1098
|
name: row.name,
|
|
1112
1099
|
type: row.type,
|
|
1113
1100
|
version: row.version,
|
|
@@ -1152,7 +1139,6 @@ var DatabaseLoader = class {
|
|
|
1152
1139
|
});
|
|
1153
1140
|
this.invalidate(type, name);
|
|
1154
1141
|
await this.createHistoryRecord(
|
|
1155
|
-
existing.id,
|
|
1156
1142
|
type,
|
|
1157
1143
|
name,
|
|
1158
1144
|
newVersion,
|
|
@@ -1194,7 +1180,6 @@ var DatabaseLoader = class {
|
|
|
1194
1180
|
});
|
|
1195
1181
|
this.invalidate(type, name);
|
|
1196
1182
|
await this.createHistoryRecord(
|
|
1197
|
-
existing.id,
|
|
1198
1183
|
type,
|
|
1199
1184
|
name,
|
|
1200
1185
|
version,
|
|
@@ -1228,7 +1213,6 @@ var DatabaseLoader = class {
|
|
|
1228
1213
|
});
|
|
1229
1214
|
this.invalidate(type, name);
|
|
1230
1215
|
await this.createHistoryRecord(
|
|
1231
|
-
id,
|
|
1232
1216
|
type,
|
|
1233
1217
|
name,
|
|
1234
1218
|
1,
|
|
@@ -2408,6 +2392,23 @@ var _MetadataManager = class _MetadataManager {
|
|
|
2408
2392
|
this.notifyWatchers(type, legacyEvent);
|
|
2409
2393
|
}
|
|
2410
2394
|
notifyWatchers(type, event) {
|
|
2395
|
+
this.notifyWatchersLocal(type, event);
|
|
2396
|
+
if (this.clusterPubSub) {
|
|
2397
|
+
const payload = {
|
|
2398
|
+
originNode: this.clusterNodeId,
|
|
2399
|
+
type,
|
|
2400
|
+
event
|
|
2401
|
+
};
|
|
2402
|
+
const key = `${type}:${event.name ?? ""}`;
|
|
2403
|
+
void this.clusterPubSub.publish(_MetadataManager.CLUSTER_CHANNEL, payload, { partitionKey: key }).catch((err) => {
|
|
2404
|
+
this.logger.error("Cluster metadata publish failed", void 0, {
|
|
2405
|
+
type,
|
|
2406
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2407
|
+
});
|
|
2408
|
+
});
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
notifyWatchersLocal(type, event) {
|
|
2411
2412
|
const callbacks = this.watchCallbacks.get(type);
|
|
2412
2413
|
if (!callbacks) return;
|
|
2413
2414
|
for (const callback of callbacks) {
|
|
@@ -2421,6 +2422,63 @@ var _MetadataManager = class _MetadataManager {
|
|
|
2421
2422
|
}
|
|
2422
2423
|
}
|
|
2423
2424
|
}
|
|
2425
|
+
/**
|
|
2426
|
+
* Attach a cluster pub/sub transport so metadata-change events fan
|
|
2427
|
+
* out to peer nodes and remote events replay into local watchers.
|
|
2428
|
+
*
|
|
2429
|
+
* The bridge plugin in @objectstack/service-cluster calls this once
|
|
2430
|
+
* per kernel boot after both cluster and metadata services are
|
|
2431
|
+
* registered. Passing the same MetadataManager twice no-ops; passing
|
|
2432
|
+
* a different transport replaces the prior subscription.
|
|
2433
|
+
*
|
|
2434
|
+
* Pass `nodeId` matching the local cluster's nodeId so loopback
|
|
2435
|
+
* suppression works.
|
|
2436
|
+
*
|
|
2437
|
+
* @returns disposer that unsubscribes from cluster events.
|
|
2438
|
+
*/
|
|
2439
|
+
attachClusterPubSub(pubsub, nodeId) {
|
|
2440
|
+
if (this.clusterPubSub === pubsub && this.clusterNodeId === nodeId) {
|
|
2441
|
+
return () => this.detachClusterPubSub();
|
|
2442
|
+
}
|
|
2443
|
+
this.detachClusterPubSub();
|
|
2444
|
+
this.clusterPubSub = pubsub;
|
|
2445
|
+
this.clusterNodeId = nodeId;
|
|
2446
|
+
this.clusterUnsubscribe = pubsub.subscribe(
|
|
2447
|
+
_MetadataManager.CLUSTER_CHANNEL,
|
|
2448
|
+
(msg) => {
|
|
2449
|
+
const p = msg.payload;
|
|
2450
|
+
if (p?.originNode && p.originNode === this.clusterNodeId) return;
|
|
2451
|
+
if (!p?.type || !p.event) return;
|
|
2452
|
+
setImmediate(() => {
|
|
2453
|
+
try {
|
|
2454
|
+
this.notifyWatchersLocal(p.type, p.event);
|
|
2455
|
+
} catch (err) {
|
|
2456
|
+
this.logger.error("Cluster remote replay failed", void 0, {
|
|
2457
|
+
type: p.type,
|
|
2458
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2459
|
+
});
|
|
2460
|
+
}
|
|
2461
|
+
});
|
|
2462
|
+
}
|
|
2463
|
+
);
|
|
2464
|
+
this.logger.info("MetadataManager attached to cluster pubsub", {
|
|
2465
|
+
nodeId,
|
|
2466
|
+
channel: _MetadataManager.CLUSTER_CHANNEL
|
|
2467
|
+
});
|
|
2468
|
+
return () => this.detachClusterPubSub();
|
|
2469
|
+
}
|
|
2470
|
+
/** Tear down cluster wiring. Safe to call multiple times. */
|
|
2471
|
+
detachClusterPubSub() {
|
|
2472
|
+
if (this.clusterUnsubscribe) {
|
|
2473
|
+
try {
|
|
2474
|
+
this.clusterUnsubscribe();
|
|
2475
|
+
} catch {
|
|
2476
|
+
}
|
|
2477
|
+
this.clusterUnsubscribe = void 0;
|
|
2478
|
+
}
|
|
2479
|
+
this.clusterPubSub = void 0;
|
|
2480
|
+
this.clusterNodeId = void 0;
|
|
2481
|
+
}
|
|
2424
2482
|
// ==========================================
|
|
2425
2483
|
// Version History & Rollback
|
|
2426
2484
|
// ==========================================
|
|
@@ -2521,6 +2579,7 @@ var _MetadataManager = class _MetadataManager {
|
|
|
2521
2579
|
}
|
|
2522
2580
|
};
|
|
2523
2581
|
_MetadataManager.LIST_CACHE_TTL_MS = 3e4;
|
|
2582
|
+
_MetadataManager.CLUSTER_CHANNEL = "metadata.changed";
|
|
2524
2583
|
var MetadataManager = _MetadataManager;
|
|
2525
2584
|
|
|
2526
2585
|
// src/plugin.ts
|
|
@@ -3644,6 +3703,10 @@ function registerMetadataHistoryRoutes(app, metadataService) {
|
|
|
3644
3703
|
}
|
|
3645
3704
|
|
|
3646
3705
|
// src/utils/history-cleanup.ts
|
|
3706
|
+
var import_kernel2 = require("@objectstack/spec/kernel");
|
|
3707
|
+
function executionPinnedTypes() {
|
|
3708
|
+
return import_kernel2.DEFAULT_METADATA_TYPE_REGISTRY.filter((entry) => entry.executionPinned).map((entry) => entry.type);
|
|
3709
|
+
}
|
|
3647
3710
|
var HistoryCleanupManager = class {
|
|
3648
3711
|
constructor(policy, dbLoader) {
|
|
3649
3712
|
this.policy = policy;
|
|
@@ -3681,6 +3744,8 @@ var HistoryCleanupManager = class {
|
|
|
3681
3744
|
const organizationId = this.dbLoader.organizationId;
|
|
3682
3745
|
let deleted = 0;
|
|
3683
3746
|
let errors = 0;
|
|
3747
|
+
const pinnedTypes = executionPinnedTypes();
|
|
3748
|
+
const isPinned = (t) => !!t && pinnedTypes.includes(t);
|
|
3684
3749
|
try {
|
|
3685
3750
|
if (this.policy.maxAgeDays) {
|
|
3686
3751
|
const cutoffDate = /* @__PURE__ */ new Date();
|
|
@@ -3692,6 +3757,9 @@ var HistoryCleanupManager = class {
|
|
|
3692
3757
|
if (organizationId) {
|
|
3693
3758
|
filter.organization_id = organizationId;
|
|
3694
3759
|
}
|
|
3760
|
+
if (pinnedTypes.length > 0) {
|
|
3761
|
+
filter.type = { $nin: pinnedTypes };
|
|
3762
|
+
}
|
|
3695
3763
|
try {
|
|
3696
3764
|
const result = await this.bulkDeleteByFilter(driver, historyTableName, filter);
|
|
3697
3765
|
deleted += result.deleted;
|
|
@@ -3704,19 +3772,22 @@ var HistoryCleanupManager = class {
|
|
|
3704
3772
|
try {
|
|
3705
3773
|
const baseWhere = {};
|
|
3706
3774
|
if (organizationId) baseWhere.organization_id = organizationId;
|
|
3707
|
-
const
|
|
3775
|
+
const metaItems = await driver.find(historyTableName, {
|
|
3708
3776
|
object: historyTableName,
|
|
3709
3777
|
where: baseWhere,
|
|
3710
|
-
fields: ["
|
|
3778
|
+
fields: ["type", "name"]
|
|
3711
3779
|
});
|
|
3712
|
-
const
|
|
3713
|
-
for (const record of
|
|
3714
|
-
|
|
3715
|
-
|
|
3780
|
+
const uniqueKeys = /* @__PURE__ */ new Set();
|
|
3781
|
+
for (const record of metaItems) {
|
|
3782
|
+
const t = record.type;
|
|
3783
|
+
const n = record.name;
|
|
3784
|
+
if (t && n && !isPinned(t)) {
|
|
3785
|
+
uniqueKeys.add(`${t}${n}`);
|
|
3716
3786
|
}
|
|
3717
3787
|
}
|
|
3718
|
-
for (const
|
|
3719
|
-
const
|
|
3788
|
+
for (const key of uniqueKeys) {
|
|
3789
|
+
const [type, name] = key.split("");
|
|
3790
|
+
const filter = { type, name, ...baseWhere };
|
|
3720
3791
|
try {
|
|
3721
3792
|
const historyRecords = await driver.find(historyTableName, {
|
|
3722
3793
|
object: historyTableName,
|
|
@@ -3793,6 +3864,8 @@ var HistoryCleanupManager = class {
|
|
|
3793
3864
|
const organizationId = this.dbLoader.organizationId;
|
|
3794
3865
|
let recordsByAge = 0;
|
|
3795
3866
|
let recordsByCount = 0;
|
|
3867
|
+
const pinnedTypes = executionPinnedTypes();
|
|
3868
|
+
const isPinned = (t) => !!t && pinnedTypes.includes(t);
|
|
3796
3869
|
try {
|
|
3797
3870
|
const baseWhere = {};
|
|
3798
3871
|
if (organizationId) baseWhere.organization_id = organizationId;
|
|
@@ -3804,25 +3877,31 @@ var HistoryCleanupManager = class {
|
|
|
3804
3877
|
recorded_at: { $lt: cutoffISO },
|
|
3805
3878
|
...baseWhere
|
|
3806
3879
|
};
|
|
3880
|
+
if (pinnedTypes.length > 0) {
|
|
3881
|
+
filter.type = { $nin: pinnedTypes };
|
|
3882
|
+
}
|
|
3807
3883
|
recordsByAge = await driver.count(historyTableName, {
|
|
3808
3884
|
object: historyTableName,
|
|
3809
3885
|
where: filter
|
|
3810
3886
|
});
|
|
3811
3887
|
}
|
|
3812
3888
|
if (this.policy.maxVersions) {
|
|
3813
|
-
const
|
|
3889
|
+
const metaItems = await driver.find(historyTableName, {
|
|
3814
3890
|
object: historyTableName,
|
|
3815
3891
|
where: baseWhere,
|
|
3816
|
-
fields: ["
|
|
3892
|
+
fields: ["type", "name"]
|
|
3817
3893
|
});
|
|
3818
|
-
const
|
|
3819
|
-
for (const record of
|
|
3820
|
-
|
|
3821
|
-
|
|
3894
|
+
const uniqueKeys = /* @__PURE__ */ new Set();
|
|
3895
|
+
for (const record of metaItems) {
|
|
3896
|
+
const t = record.type;
|
|
3897
|
+
const n = record.name;
|
|
3898
|
+
if (t && n && !isPinned(t)) {
|
|
3899
|
+
uniqueKeys.add(`${t}${n}`);
|
|
3822
3900
|
}
|
|
3823
3901
|
}
|
|
3824
|
-
for (const
|
|
3825
|
-
const
|
|
3902
|
+
for (const key of uniqueKeys) {
|
|
3903
|
+
const [type, name] = key.split("");
|
|
3904
|
+
const filter = { type, name, ...baseWhere };
|
|
3826
3905
|
const count = await driver.count(historyTableName, {
|
|
3827
3906
|
object: historyTableName,
|
|
3828
3907
|
where: filter
|