@le-space/node 0.1.16 → 0.1.18
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/index.d.ts +14 -0
- package/index.js +156 -48
- package/package.json +4 -4
- package/reference/publish-static-site.py +65 -0
package/index.d.ts
CHANGED
|
@@ -231,6 +231,20 @@ declare function retainSuccessfulDeployments(args: {
|
|
|
231
231
|
hash: string;
|
|
232
232
|
result: Awaited<ReturnType<typeof forgetAlephMessages>>;
|
|
233
233
|
}[];
|
|
234
|
+
forgetStageResults: {
|
|
235
|
+
stage: "instances" | "dependents";
|
|
236
|
+
hashes: string[];
|
|
237
|
+
skipped?: boolean;
|
|
238
|
+
skippedReason?: string;
|
|
239
|
+
forgottenHashes: string[];
|
|
240
|
+
outstandingForgetHashes: string[];
|
|
241
|
+
statuses: Record<string, string>;
|
|
242
|
+
forgetResult: Awaited<ReturnType<typeof forgetAlephMessages>> | null;
|
|
243
|
+
followUpForgetResults: Array<{
|
|
244
|
+
hash: string;
|
|
245
|
+
result: Awaited<ReturnType<typeof forgetAlephMessages>>;
|
|
246
|
+
}>;
|
|
247
|
+
}[];
|
|
234
248
|
}>;
|
|
235
249
|
|
|
236
250
|
declare function buildScaffoldDeployResult(env?: NodeJS.ProcessEnv): DeployOutputResult;
|
package/index.js
CHANGED
|
@@ -896,6 +896,26 @@ function retentionRecordId(record) {
|
|
|
896
896
|
function hashesFromRetentionRecord(record) {
|
|
897
897
|
return [record.instance_item_hash, record.rootfs_item_hash, record.site_item_hash].filter(Boolean);
|
|
898
898
|
}
|
|
899
|
+
function uniqueHashes(hashes) {
|
|
900
|
+
return [...new Set(hashes.filter(Boolean))];
|
|
901
|
+
}
|
|
902
|
+
function dependentHashesFromRetentionRecord(record) {
|
|
903
|
+
return [record.rootfs_item_hash, record.site_item_hash].filter(Boolean);
|
|
904
|
+
}
|
|
905
|
+
function splitForgetStages(args) {
|
|
906
|
+
const instanceForgetHashes = uniqueHashes(args.prunedRecords.map((record) => record.instance_item_hash)).filter(
|
|
907
|
+
(hash) => !args.retainedHashes.has(hash)
|
|
908
|
+
);
|
|
909
|
+
const dependentForgetHashes = uniqueHashes([
|
|
910
|
+
...args.prunedRecords.flatMap(dependentHashesFromRetentionRecord),
|
|
911
|
+
...args.extraForgetHashes
|
|
912
|
+
]).filter((hash) => !args.retainedHashes.has(hash) && !instanceForgetHashes.includes(hash));
|
|
913
|
+
return {
|
|
914
|
+
instanceForgetHashes,
|
|
915
|
+
dependentForgetHashes,
|
|
916
|
+
orderedForgetHashes: [...instanceForgetHashes, ...dependentForgetHashes]
|
|
917
|
+
};
|
|
918
|
+
}
|
|
899
919
|
async function fetchAggregateKey(args) {
|
|
900
920
|
const requestUrl = new URL(`/api/v0/aggregates/${args.address}.json`, args.apiHost ?? "https://api2.aleph.im");
|
|
901
921
|
requestUrl.searchParams.set("keys", args.key);
|
|
@@ -973,6 +993,73 @@ async function classifyForgetHashes(args) {
|
|
|
973
993
|
statuses
|
|
974
994
|
};
|
|
975
995
|
}
|
|
996
|
+
async function executeForgetStage(args) {
|
|
997
|
+
const stageHashes = uniqueHashes(args.hashes);
|
|
998
|
+
let forgetResult = null;
|
|
999
|
+
const followUpForgetResults = [];
|
|
1000
|
+
if (stageHashes.length === 0) {
|
|
1001
|
+
return {
|
|
1002
|
+
hashes: stageHashes,
|
|
1003
|
+
forgottenHashes: [],
|
|
1004
|
+
outstandingForgetHashes: [],
|
|
1005
|
+
statuses: {},
|
|
1006
|
+
forgetResult,
|
|
1007
|
+
followUpForgetResults
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
let classified = await classifyForgetHashes({
|
|
1011
|
+
hashes: stageHashes,
|
|
1012
|
+
fetch: args.fetch,
|
|
1013
|
+
apiHost: args.apiHost
|
|
1014
|
+
});
|
|
1015
|
+
if (classified.outstandingForgetHashes.length > 0) {
|
|
1016
|
+
forgetResult = await forgetAlephMessages({
|
|
1017
|
+
sender: args.sender,
|
|
1018
|
+
hashes: classified.outstandingForgetHashes,
|
|
1019
|
+
reason: args.reason,
|
|
1020
|
+
signer: args.signer,
|
|
1021
|
+
hasher: args.hasher,
|
|
1022
|
+
fetch: args.fetch,
|
|
1023
|
+
channel: args.channel,
|
|
1024
|
+
apiHost: args.apiHost,
|
|
1025
|
+
now: args.now
|
|
1026
|
+
});
|
|
1027
|
+
classified = await classifyForgetHashes({
|
|
1028
|
+
hashes: stageHashes,
|
|
1029
|
+
fetch: args.fetch,
|
|
1030
|
+
apiHost: args.apiHost
|
|
1031
|
+
});
|
|
1032
|
+
if (classified.outstandingForgetHashes.length > 0) {
|
|
1033
|
+
for (const hash of classified.outstandingForgetHashes) {
|
|
1034
|
+
const result = await forgetAlephMessages({
|
|
1035
|
+
sender: args.sender,
|
|
1036
|
+
hashes: [hash],
|
|
1037
|
+
reason: args.reason,
|
|
1038
|
+
signer: args.signer,
|
|
1039
|
+
hasher: args.hasher,
|
|
1040
|
+
fetch: args.fetch,
|
|
1041
|
+
channel: args.channel,
|
|
1042
|
+
apiHost: args.apiHost,
|
|
1043
|
+
now: args.now
|
|
1044
|
+
});
|
|
1045
|
+
followUpForgetResults.push({ hash, result });
|
|
1046
|
+
}
|
|
1047
|
+
classified = await classifyForgetHashes({
|
|
1048
|
+
hashes: stageHashes,
|
|
1049
|
+
fetch: args.fetch,
|
|
1050
|
+
apiHost: args.apiHost
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
return {
|
|
1055
|
+
hashes: stageHashes,
|
|
1056
|
+
forgottenHashes: classified.forgottenHashes,
|
|
1057
|
+
outstandingForgetHashes: classified.outstandingForgetHashes,
|
|
1058
|
+
statuses: classified.statuses,
|
|
1059
|
+
forgetResult,
|
|
1060
|
+
followUpForgetResults
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
976
1063
|
async function retainSuccessfulDeployments(args) {
|
|
977
1064
|
const keepCount = Math.max(0, Number.parseInt(String(args.keepCount ?? 0), 10) || 0);
|
|
978
1065
|
const aggregateKey = asString3(args.aggregateKey) ?? SUCCESSFUL_DEPLOYMENTS_AGGREGATE_KEY;
|
|
@@ -999,10 +1086,13 @@ async function retainSuccessfulDeployments(args) {
|
|
|
999
1086
|
const retainedRecords = keepCount > 0 ? uniqueRecords.slice(0, keepCount) : [];
|
|
1000
1087
|
const prunedRecords = keepCount > 0 ? uniqueRecords.slice(keepCount) : uniqueRecords;
|
|
1001
1088
|
const retainedHashes = new Set(retainedRecords.flatMap(hashesFromRetentionRecord));
|
|
1002
|
-
const extraForgetHashes =
|
|
1003
|
-
const
|
|
1004
|
-
|
|
1005
|
-
|
|
1089
|
+
const extraForgetHashes = uniqueHashes(args.extraForgetHashes ?? []);
|
|
1090
|
+
const { instanceForgetHashes, dependentForgetHashes, orderedForgetHashes } = splitForgetStages({
|
|
1091
|
+
prunedRecords,
|
|
1092
|
+
extraForgetHashes,
|
|
1093
|
+
retainedHashes
|
|
1094
|
+
});
|
|
1095
|
+
const forgetHashes = orderedForgetHashes;
|
|
1006
1096
|
const aggregateContent = {
|
|
1007
1097
|
keep: keepCount,
|
|
1008
1098
|
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -1019,25 +1109,40 @@ async function retainSuccessfulDeployments(args) {
|
|
|
1019
1109
|
apiHost: args.apiHost,
|
|
1020
1110
|
now: args.now
|
|
1021
1111
|
});
|
|
1022
|
-
|
|
1023
|
-
|
|
1112
|
+
const forgetReason = args.reason ?? `Prune successful deployments beyond retention limit ${keepCount}`;
|
|
1113
|
+
const forgetStageResults = [];
|
|
1024
1114
|
let forgottenHashes = [];
|
|
1025
1115
|
let outstandingForgetHashes = [];
|
|
1026
1116
|
let forgetStatuses = {};
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1117
|
+
let forgetResult = null;
|
|
1118
|
+
const followUpForgetResults = [];
|
|
1119
|
+
if (instanceForgetHashes.length > 0) {
|
|
1120
|
+
const instanceStage = await executeForgetStage({
|
|
1121
|
+
sender: args.sender,
|
|
1122
|
+
hashes: instanceForgetHashes,
|
|
1123
|
+
reason: forgetReason,
|
|
1124
|
+
signer: args.signer,
|
|
1125
|
+
hasher: args.hasher,
|
|
1030
1126
|
fetch: args.fetch,
|
|
1031
|
-
|
|
1127
|
+
channel: args.channel,
|
|
1128
|
+
apiHost: args.apiHost,
|
|
1129
|
+
now: args.now
|
|
1032
1130
|
});
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1131
|
+
forgetStageResults.push({ stage: "instances", ...instanceStage });
|
|
1132
|
+
forgottenHashes.push(...instanceStage.forgottenHashes);
|
|
1133
|
+
outstandingForgetHashes.push(...instanceStage.outstandingForgetHashes);
|
|
1134
|
+
forgetStatuses = { ...forgetStatuses, ...instanceStage.statuses };
|
|
1135
|
+
if (!forgetResult && instanceStage.forgetResult) {
|
|
1136
|
+
forgetResult = instanceStage.forgetResult;
|
|
1137
|
+
}
|
|
1138
|
+
followUpForgetResults.push(...instanceStage.followUpForgetResults);
|
|
1139
|
+
}
|
|
1140
|
+
if (dependentForgetHashes.length > 0) {
|
|
1141
|
+
if (outstandingForgetHashes.length === 0) {
|
|
1142
|
+
const dependentStage = await executeForgetStage({
|
|
1038
1143
|
sender: args.sender,
|
|
1039
|
-
hashes:
|
|
1040
|
-
reason:
|
|
1144
|
+
hashes: dependentForgetHashes,
|
|
1145
|
+
reason: forgetReason,
|
|
1041
1146
|
signer: args.signer,
|
|
1042
1147
|
hasher: args.hasher,
|
|
1043
1148
|
fetch: args.fetch,
|
|
@@ -1045,40 +1150,38 @@ async function retainSuccessfulDeployments(args) {
|
|
|
1045
1150
|
apiHost: args.apiHost,
|
|
1046
1151
|
now: args.now
|
|
1047
1152
|
});
|
|
1048
|
-
|
|
1049
|
-
|
|
1153
|
+
forgetStageResults.push({ stage: "dependents", ...dependentStage });
|
|
1154
|
+
forgottenHashes.push(...dependentStage.forgottenHashes);
|
|
1155
|
+
outstandingForgetHashes.push(...dependentStage.outstandingForgetHashes);
|
|
1156
|
+
forgetStatuses = { ...forgetStatuses, ...dependentStage.statuses };
|
|
1157
|
+
if (!forgetResult && dependentStage.forgetResult) {
|
|
1158
|
+
forgetResult = dependentStage.forgetResult;
|
|
1159
|
+
}
|
|
1160
|
+
followUpForgetResults.push(...dependentStage.followUpForgetResults);
|
|
1161
|
+
} else {
|
|
1162
|
+
const dependentStageStatus = await classifyForgetHashes({
|
|
1163
|
+
hashes: dependentForgetHashes,
|
|
1050
1164
|
fetch: args.fetch,
|
|
1051
1165
|
apiHost: args.apiHost
|
|
1052
1166
|
});
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
for
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
now: args.now
|
|
1068
|
-
});
|
|
1069
|
-
followUpForgetResults.push({ hash, result });
|
|
1070
|
-
}
|
|
1071
|
-
postBatch = await classifyForgetHashes({
|
|
1072
|
-
hashes: forgetHashes,
|
|
1073
|
-
fetch: args.fetch,
|
|
1074
|
-
apiHost: args.apiHost
|
|
1075
|
-
});
|
|
1076
|
-
forgottenHashes = postBatch.forgottenHashes;
|
|
1077
|
-
outstandingForgetHashes = postBatch.outstandingForgetHashes;
|
|
1078
|
-
forgetStatuses = postBatch.statuses;
|
|
1079
|
-
}
|
|
1167
|
+
forgetStageResults.push({
|
|
1168
|
+
stage: "dependents",
|
|
1169
|
+
hashes: dependentForgetHashes,
|
|
1170
|
+
skipped: true,
|
|
1171
|
+
skippedReason: "Waiting for instance forget stage to complete before pruning dependent store items.",
|
|
1172
|
+
forgottenHashes: dependentStageStatus.forgottenHashes,
|
|
1173
|
+
outstandingForgetHashes: dependentStageStatus.outstandingForgetHashes,
|
|
1174
|
+
statuses: dependentStageStatus.statuses,
|
|
1175
|
+
forgetResult: null,
|
|
1176
|
+
followUpForgetResults: []
|
|
1177
|
+
});
|
|
1178
|
+
forgottenHashes.push(...dependentStageStatus.forgottenHashes);
|
|
1179
|
+
outstandingForgetHashes.push(...dependentStageStatus.outstandingForgetHashes);
|
|
1180
|
+
forgetStatuses = { ...forgetStatuses, ...dependentStageStatus.statuses };
|
|
1080
1181
|
}
|
|
1081
1182
|
}
|
|
1183
|
+
forgottenHashes = uniqueHashes(forgottenHashes);
|
|
1184
|
+
outstandingForgetHashes = uniqueHashes(outstandingForgetHashes);
|
|
1082
1185
|
return {
|
|
1083
1186
|
sender: args.sender,
|
|
1084
1187
|
aggregateKey,
|
|
@@ -1091,7 +1194,8 @@ async function retainSuccessfulDeployments(args) {
|
|
|
1091
1194
|
outstandingForgetHashes,
|
|
1092
1195
|
forgetStatuses,
|
|
1093
1196
|
forgetResult,
|
|
1094
|
-
followUpForgetResults
|
|
1197
|
+
followUpForgetResults,
|
|
1198
|
+
forgetStageResults
|
|
1095
1199
|
};
|
|
1096
1200
|
}
|
|
1097
1201
|
|
|
@@ -2905,6 +3009,7 @@ if (import.meta.url === pathToFileURL2(process.argv[1] ?? "").href) {
|
|
|
2905
3009
|
// src/site-runner.ts
|
|
2906
3010
|
import process2 from "process";
|
|
2907
3011
|
import { spawn as spawn2 } from "child_process";
|
|
3012
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2908
3013
|
async function runCapture(command, args, options = {}) {
|
|
2909
3014
|
return await new Promise((resolve, reject) => {
|
|
2910
3015
|
const child = spawn2(command, args, {
|
|
@@ -2957,6 +3062,9 @@ async function waitForAlephMessage(itemHash, env = process2.env) {
|
|
|
2957
3062
|
}
|
|
2958
3063
|
throw new Error(`Aleph STORE message ${itemHash} did not become processed in time.`);
|
|
2959
3064
|
}
|
|
3065
|
+
function defaultSitePublishScriptPath() {
|
|
3066
|
+
return fileURLToPath2(new URL("../reference/publish-static-site.py", import.meta.url));
|
|
3067
|
+
}
|
|
2960
3068
|
function mergedAddrs(env = process2.env) {
|
|
2961
3069
|
const combined = [];
|
|
2962
3070
|
for (const key of ["PROBE_MULTIADDRS_JSON", "BROWSER_BOOTSTRAP_MULTIADDRS_JSON"]) {
|
|
@@ -2972,7 +3080,7 @@ function mergedAddrs(env = process2.env) {
|
|
|
2972
3080
|
}
|
|
2973
3081
|
async function runSitePublishMode(env = process2.env) {
|
|
2974
3082
|
const projectDir = requiredEnv("ALEPH_SITE_PROJECT_DIR", env);
|
|
2975
|
-
const publishScript = optionalEnv("ALEPH_SITE_PUBLISH_SCRIPT",
|
|
3083
|
+
const publishScript = optionalEnv("ALEPH_SITE_PUBLISH_SCRIPT", defaultSitePublishScriptPath(), env);
|
|
2976
3084
|
const siteDirectory = requiredEnv("ALEPH_SITE_DIRECTORY", env);
|
|
2977
3085
|
const pythonBin = optionalEnv("ALEPH_SITE_PYTHON", "python3", env);
|
|
2978
3086
|
const alephBin = optionalEnv("ALEPH_SITE_ALEPH_BIN", "aleph", env);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@le-space/node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "Node and GitHub Actions adapters for shared Aleph tooling.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"access": "public"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@le-space/core": "0.1.
|
|
20
|
-
"@le-space/shared-types": "0.1.
|
|
21
|
-
"@le-space/rootfs": "0.1.
|
|
19
|
+
"@le-space/core": "0.1.18",
|
|
20
|
+
"@le-space/shared-types": "0.1.18",
|
|
21
|
+
"@le-space/rootfs": "0.1.18",
|
|
22
22
|
"ethers": "^6.15.0"
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
from cid import make_cid
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def upload_directory(folder: Path, gateway: str) -> dict[str, str]:
|
|
11
|
+
url = f"{gateway.rstrip('/')}/api/v0/add"
|
|
12
|
+
params = {
|
|
13
|
+
"recursive": "true",
|
|
14
|
+
"wrap-with-directory": "true",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
handles = []
|
|
18
|
+
files = []
|
|
19
|
+
try:
|
|
20
|
+
for path in sorted(folder.rglob("*")):
|
|
21
|
+
if not path.is_file():
|
|
22
|
+
continue
|
|
23
|
+
relative_path = path.relative_to(folder)
|
|
24
|
+
handle = path.open("rb")
|
|
25
|
+
handles.append(handle)
|
|
26
|
+
files.append(("file", (str(relative_path), handle)))
|
|
27
|
+
|
|
28
|
+
if not files:
|
|
29
|
+
raise RuntimeError(f"No files found under {folder}")
|
|
30
|
+
|
|
31
|
+
response = requests.post(url, params=params, files=files, timeout=300)
|
|
32
|
+
response.raise_for_status()
|
|
33
|
+
|
|
34
|
+
cid_v0 = None
|
|
35
|
+
for line in response.text.strip().splitlines():
|
|
36
|
+
entry = json.loads(line)
|
|
37
|
+
cid_v0 = entry.get("Hash") or cid_v0
|
|
38
|
+
|
|
39
|
+
if not cid_v0:
|
|
40
|
+
raise RuntimeError("CID not found in IPFS response")
|
|
41
|
+
|
|
42
|
+
cid_v1 = make_cid(cid_v0).to_v1().encode("base32").decode()
|
|
43
|
+
return {"cid_v0": cid_v0, "cid_v1": cid_v1}
|
|
44
|
+
finally:
|
|
45
|
+
for handle in handles:
|
|
46
|
+
handle.close()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def main() -> int:
|
|
50
|
+
if len(sys.argv) != 2:
|
|
51
|
+
print("usage: publish-static-site.py <directory>", file=sys.stderr)
|
|
52
|
+
return 2
|
|
53
|
+
|
|
54
|
+
directory = Path(sys.argv[1]).resolve()
|
|
55
|
+
if not directory.is_dir():
|
|
56
|
+
print(f"Error: path must be a directory: {directory}", file=sys.stderr)
|
|
57
|
+
return 1
|
|
58
|
+
|
|
59
|
+
result = upload_directory(directory, "https://ipfs-2.aleph.im")
|
|
60
|
+
print(json.dumps(result))
|
|
61
|
+
return 0
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
raise SystemExit(main())
|