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