@le-space/core 0.1.15 → 0.1.17

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.
Files changed (3) hide show
  1. package/index.d.ts +21 -0
  2. package/index.js +188 -9
  3. package/package.json +2 -2
package/index.d.ts CHANGED
@@ -320,6 +320,9 @@ declare function retainSuccessfulDeployments(args: {
320
320
  retainedRecords: RetentionRecord[];
321
321
  prunedRecords: RetentionRecord[];
322
322
  forgetHashes: string[];
323
+ forgottenHashes: string[];
324
+ outstandingForgetHashes: string[];
325
+ forgetStatuses: Record<string, string>;
323
326
  forgetResult: {
324
327
  sender: string;
325
328
  itemHash: string;
@@ -327,6 +330,24 @@ declare function retainSuccessfulDeployments(args: {
327
330
  httpStatus: number;
328
331
  status: _shared_aleph_shared_types.MessageStatus;
329
332
  } | null;
333
+ followUpForgetResults: {
334
+ hash: string;
335
+ result: Awaited<ReturnType<typeof forgetAlephMessages>>;
336
+ }[];
337
+ forgetStageResults: {
338
+ stage: "instances" | "dependents";
339
+ hashes: string[];
340
+ skipped?: boolean;
341
+ skippedReason?: string;
342
+ forgottenHashes: string[];
343
+ outstandingForgetHashes: string[];
344
+ statuses: Record<string, string>;
345
+ forgetResult: Awaited<ReturnType<typeof forgetAlephMessages>> | null;
346
+ followUpForgetResults: Array<{
347
+ hash: string;
348
+ result: Awaited<ReturnType<typeof forgetAlephMessages>>;
349
+ }>;
350
+ }[];
330
351
  }>;
331
352
 
332
353
  declare function createPortForwardAggregateContent(args: {
package/index.js CHANGED
@@ -841,6 +841,26 @@ function retentionRecordId(record) {
841
841
  function hashesFromRetentionRecord(record) {
842
842
  return [record.instance_item_hash, record.rootfs_item_hash, record.site_item_hash].filter(Boolean);
843
843
  }
844
+ function uniqueHashes(hashes) {
845
+ return [...new Set(hashes.filter(Boolean))];
846
+ }
847
+ function dependentHashesFromRetentionRecord(record) {
848
+ return [record.rootfs_item_hash, record.site_item_hash].filter(Boolean);
849
+ }
850
+ function splitForgetStages(args) {
851
+ const instanceForgetHashes = uniqueHashes(args.prunedRecords.map((record) => record.instance_item_hash)).filter(
852
+ (hash) => !args.retainedHashes.has(hash)
853
+ );
854
+ const dependentForgetHashes = uniqueHashes([
855
+ ...args.prunedRecords.flatMap(dependentHashesFromRetentionRecord),
856
+ ...args.extraForgetHashes
857
+ ]).filter((hash) => !args.retainedHashes.has(hash) && !instanceForgetHashes.includes(hash));
858
+ return {
859
+ instanceForgetHashes,
860
+ dependentForgetHashes,
861
+ orderedForgetHashes: [...instanceForgetHashes, ...dependentForgetHashes]
862
+ };
863
+ }
844
864
  async function fetchAggregateKey(args) {
845
865
  const requestUrl = new URL(`/api/v0/aggregates/${args.address}.json`, args.apiHost ?? "https://api2.aleph.im");
846
866
  requestUrl.searchParams.set("keys", args.key);
@@ -893,6 +913,98 @@ async function publishAggregateKey(args) {
893
913
  httpStatus
894
914
  };
895
915
  }
916
+ async function fetchMessageStatus(args) {
917
+ const requestUrl = new URL(`/api/v0/messages/${args.hash}`, args.apiHost ?? "https://api2.aleph.im");
918
+ const response = await args.fetch(requestUrl.toString(), { cache: "no-cache" });
919
+ if (response.status === 404) return "missing";
920
+ if (!response.ok) return "unknown";
921
+ const payload = await response.json();
922
+ return asString3(payload?.status) ?? "unknown";
923
+ }
924
+ async function classifyForgetHashes(args) {
925
+ const statuses = {};
926
+ for (const hash of args.hashes) {
927
+ statuses[hash] = await fetchMessageStatus({
928
+ hash,
929
+ fetch: args.fetch,
930
+ apiHost: args.apiHost
931
+ });
932
+ }
933
+ const forgottenHashes = args.hashes.filter((hash) => statuses[hash] === "forgotten");
934
+ const outstandingForgetHashes = args.hashes.filter((hash) => statuses[hash] !== "forgotten");
935
+ return {
936
+ forgottenHashes,
937
+ outstandingForgetHashes,
938
+ statuses
939
+ };
940
+ }
941
+ async function executeForgetStage(args) {
942
+ const stageHashes = uniqueHashes(args.hashes);
943
+ let forgetResult = null;
944
+ const followUpForgetResults = [];
945
+ if (stageHashes.length === 0) {
946
+ return {
947
+ hashes: stageHashes,
948
+ forgottenHashes: [],
949
+ outstandingForgetHashes: [],
950
+ statuses: {},
951
+ forgetResult,
952
+ followUpForgetResults
953
+ };
954
+ }
955
+ let classified = await classifyForgetHashes({
956
+ hashes: stageHashes,
957
+ fetch: args.fetch,
958
+ apiHost: args.apiHost
959
+ });
960
+ if (classified.outstandingForgetHashes.length > 0) {
961
+ forgetResult = await forgetAlephMessages({
962
+ sender: args.sender,
963
+ hashes: classified.outstandingForgetHashes,
964
+ reason: args.reason,
965
+ signer: args.signer,
966
+ hasher: args.hasher,
967
+ fetch: args.fetch,
968
+ channel: args.channel,
969
+ apiHost: args.apiHost,
970
+ now: args.now
971
+ });
972
+ classified = await classifyForgetHashes({
973
+ hashes: stageHashes,
974
+ fetch: args.fetch,
975
+ apiHost: args.apiHost
976
+ });
977
+ if (classified.outstandingForgetHashes.length > 0) {
978
+ for (const hash of classified.outstandingForgetHashes) {
979
+ const result = await forgetAlephMessages({
980
+ sender: args.sender,
981
+ hashes: [hash],
982
+ reason: args.reason,
983
+ signer: args.signer,
984
+ hasher: args.hasher,
985
+ fetch: args.fetch,
986
+ channel: args.channel,
987
+ apiHost: args.apiHost,
988
+ now: args.now
989
+ });
990
+ followUpForgetResults.push({ hash, result });
991
+ }
992
+ classified = await classifyForgetHashes({
993
+ hashes: stageHashes,
994
+ fetch: args.fetch,
995
+ apiHost: args.apiHost
996
+ });
997
+ }
998
+ }
999
+ return {
1000
+ hashes: stageHashes,
1001
+ forgottenHashes: classified.forgottenHashes,
1002
+ outstandingForgetHashes: classified.outstandingForgetHashes,
1003
+ statuses: classified.statuses,
1004
+ forgetResult,
1005
+ followUpForgetResults
1006
+ };
1007
+ }
896
1008
  async function retainSuccessfulDeployments(args) {
897
1009
  const keepCount = Math.max(0, Number.parseInt(String(args.keepCount ?? 0), 10) || 0);
898
1010
  const aggregateKey = asString3(args.aggregateKey) ?? SUCCESSFUL_DEPLOYMENTS_AGGREGATE_KEY;
@@ -919,10 +1031,13 @@ async function retainSuccessfulDeployments(args) {
919
1031
  const retainedRecords = keepCount > 0 ? uniqueRecords.slice(0, keepCount) : [];
920
1032
  const prunedRecords = keepCount > 0 ? uniqueRecords.slice(keepCount) : uniqueRecords;
921
1033
  const retainedHashes = new Set(retainedRecords.flatMap(hashesFromRetentionRecord));
922
- const extraForgetHashes = [...new Set((args.extraForgetHashes ?? []).filter(Boolean))];
923
- const forgetHashes = [.../* @__PURE__ */ new Set([...prunedRecords.flatMap(hashesFromRetentionRecord), ...extraForgetHashes])].filter(
924
- (hash) => !retainedHashes.has(hash)
925
- );
1034
+ const extraForgetHashes = uniqueHashes(args.extraForgetHashes ?? []);
1035
+ const { instanceForgetHashes, dependentForgetHashes, orderedForgetHashes } = splitForgetStages({
1036
+ prunedRecords,
1037
+ extraForgetHashes,
1038
+ retainedHashes
1039
+ });
1040
+ const forgetHashes = orderedForgetHashes;
926
1041
  const aggregateContent = {
927
1042
  keep: keepCount,
928
1043
  updated_at: (/* @__PURE__ */ new Date()).toISOString(),
@@ -939,12 +1054,18 @@ async function retainSuccessfulDeployments(args) {
939
1054
  apiHost: args.apiHost,
940
1055
  now: args.now
941
1056
  });
1057
+ const forgetReason = args.reason ?? `Prune successful deployments beyond retention limit ${keepCount}`;
1058
+ const forgetStageResults = [];
1059
+ let forgottenHashes = [];
1060
+ let outstandingForgetHashes = [];
1061
+ let forgetStatuses = {};
942
1062
  let forgetResult = null;
943
- if (forgetHashes.length > 0) {
944
- forgetResult = await forgetAlephMessages({
1063
+ const followUpForgetResults = [];
1064
+ if (instanceForgetHashes.length > 0) {
1065
+ const instanceStage = await executeForgetStage({
945
1066
  sender: args.sender,
946
- hashes: forgetHashes,
947
- reason: args.reason ?? `Prune successful deployments beyond retention limit ${keepCount}`,
1067
+ hashes: instanceForgetHashes,
1068
+ reason: forgetReason,
948
1069
  signer: args.signer,
949
1070
  hasher: args.hasher,
950
1071
  fetch: args.fetch,
@@ -952,7 +1073,60 @@ async function retainSuccessfulDeployments(args) {
952
1073
  apiHost: args.apiHost,
953
1074
  now: args.now
954
1075
  });
1076
+ forgetStageResults.push({ stage: "instances", ...instanceStage });
1077
+ forgottenHashes.push(...instanceStage.forgottenHashes);
1078
+ outstandingForgetHashes.push(...instanceStage.outstandingForgetHashes);
1079
+ forgetStatuses = { ...forgetStatuses, ...instanceStage.statuses };
1080
+ if (!forgetResult && instanceStage.forgetResult) {
1081
+ forgetResult = instanceStage.forgetResult;
1082
+ }
1083
+ followUpForgetResults.push(...instanceStage.followUpForgetResults);
1084
+ }
1085
+ if (dependentForgetHashes.length > 0) {
1086
+ if (outstandingForgetHashes.length === 0) {
1087
+ const dependentStage = await executeForgetStage({
1088
+ sender: args.sender,
1089
+ hashes: dependentForgetHashes,
1090
+ reason: forgetReason,
1091
+ signer: args.signer,
1092
+ hasher: args.hasher,
1093
+ fetch: args.fetch,
1094
+ channel: args.channel,
1095
+ apiHost: args.apiHost,
1096
+ now: args.now
1097
+ });
1098
+ forgetStageResults.push({ stage: "dependents", ...dependentStage });
1099
+ forgottenHashes.push(...dependentStage.forgottenHashes);
1100
+ outstandingForgetHashes.push(...dependentStage.outstandingForgetHashes);
1101
+ forgetStatuses = { ...forgetStatuses, ...dependentStage.statuses };
1102
+ if (!forgetResult && dependentStage.forgetResult) {
1103
+ forgetResult = dependentStage.forgetResult;
1104
+ }
1105
+ followUpForgetResults.push(...dependentStage.followUpForgetResults);
1106
+ } else {
1107
+ const dependentStageStatus = await classifyForgetHashes({
1108
+ hashes: dependentForgetHashes,
1109
+ fetch: args.fetch,
1110
+ apiHost: args.apiHost
1111
+ });
1112
+ forgetStageResults.push({
1113
+ stage: "dependents",
1114
+ hashes: dependentForgetHashes,
1115
+ skipped: true,
1116
+ skippedReason: "Waiting for instance forget stage to complete before pruning dependent store items.",
1117
+ forgottenHashes: dependentStageStatus.forgottenHashes,
1118
+ outstandingForgetHashes: dependentStageStatus.outstandingForgetHashes,
1119
+ statuses: dependentStageStatus.statuses,
1120
+ forgetResult: null,
1121
+ followUpForgetResults: []
1122
+ });
1123
+ forgottenHashes.push(...dependentStageStatus.forgottenHashes);
1124
+ outstandingForgetHashes.push(...dependentStageStatus.outstandingForgetHashes);
1125
+ forgetStatuses = { ...forgetStatuses, ...dependentStageStatus.statuses };
1126
+ }
955
1127
  }
1128
+ forgottenHashes = uniqueHashes(forgottenHashes);
1129
+ outstandingForgetHashes = uniqueHashes(outstandingForgetHashes);
956
1130
  return {
957
1131
  sender: args.sender,
958
1132
  aggregateKey,
@@ -961,7 +1135,12 @@ async function retainSuccessfulDeployments(args) {
961
1135
  retainedRecords,
962
1136
  prunedRecords,
963
1137
  forgetHashes,
964
- forgetResult
1138
+ forgottenHashes,
1139
+ outstandingForgetHashes,
1140
+ forgetStatuses,
1141
+ forgetResult,
1142
+ followUpForgetResults,
1143
+ forgetStageResults
965
1144
  };
966
1145
  }
967
1146
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@le-space/core",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "Reusable Aleph deployment, runtime, and guest lifecycle logic.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -16,6 +16,6 @@
16
16
  "access": "public"
17
17
  },
18
18
  "dependencies": {
19
- "@le-space/shared-types": "0.1.15"
19
+ "@le-space/shared-types": "0.1.17"
20
20
  }
21
21
  }