pakstr 0.14.0 ā 0.16.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/commands/publish.js +12 -3
- package/dist/core/zapStore.js +9 -1
- package/dist/core/zapstoreConfig.js +69 -4
- package/package.json +1 -1
package/dist/commands/publish.js
CHANGED
|
@@ -38,6 +38,7 @@ async function publishCommand(configPath, options = {}) {
|
|
|
38
38
|
console.log("\nš¤ pakstr publish" + (options.dryRun ? " (dry-run)" : ""));
|
|
39
39
|
console.log("App:", config.app.appName, `(${config.app.appId})`);
|
|
40
40
|
console.log("Publishing as:", publishNsec.envVar);
|
|
41
|
+
console.log("Publisher npub:", npub);
|
|
41
42
|
console.log("Relay:", config.publish.relay);
|
|
42
43
|
console.log("Blossom:", config.publish.blossom);
|
|
43
44
|
let blossomUrl;
|
|
@@ -50,10 +51,15 @@ async function publishCommand(configPath, options = {}) {
|
|
|
50
51
|
const repository = zapstoreConfig?.repository ?? (0, zapstoreConfig_1.loadZapstoreRepository)(config.configDir);
|
|
51
52
|
const app = {
|
|
52
53
|
appId: config.app.appId,
|
|
53
|
-
appName: config.app.appName,
|
|
54
|
+
appName: zapstoreConfig?.name ?? config.app.appName,
|
|
54
55
|
versionName: config.app.versionName,
|
|
55
56
|
versionCode: config.app.versionCode,
|
|
56
|
-
description: config.app.description,
|
|
57
|
+
description: zapstoreConfig?.description ?? config.app.description,
|
|
58
|
+
summary: zapstoreConfig?.summary,
|
|
59
|
+
tags: zapstoreConfig?.tags,
|
|
60
|
+
license: zapstoreConfig?.license,
|
|
61
|
+
website: zapstoreConfig?.website,
|
|
62
|
+
releaseNotes: zapstoreConfig?.releaseNotes,
|
|
57
63
|
repository,
|
|
58
64
|
};
|
|
59
65
|
const target = { relayUrl: config.publish.relay };
|
|
@@ -97,14 +103,17 @@ async function publishCommand(configPath, options = {}) {
|
|
|
97
103
|
pubkeyHex,
|
|
98
104
|
relayUrl: config.publish.relay,
|
|
99
105
|
});
|
|
106
|
+
console.log(`š Existing kind-32267: ${existingAppMetadata?.id ?? "none"}`);
|
|
100
107
|
const appMetadata = existingAppMetadata && (0, zapStore_1.isCurrentAppMetadataEvent)(existingAppMetadata, appPublish.appMetadata)
|
|
101
108
|
? existingAppMetadata
|
|
102
109
|
: appPublish.appMetadata;
|
|
103
110
|
if (appMetadata === appPublish.appMetadata) {
|
|
111
|
+
console.log("š¤ Publishing kind-32267...");
|
|
104
112
|
await transport.publish(appMetadata, config.publish.relay);
|
|
113
|
+
console.log(`ā
Published kind-32267: ${appMetadata.id}`);
|
|
105
114
|
}
|
|
106
115
|
else {
|
|
107
|
-
console.log("
|
|
116
|
+
console.log("ā
kind-32267 already up to date, skipping publish");
|
|
108
117
|
}
|
|
109
118
|
if (options.dryRun) {
|
|
110
119
|
// No network, no APK required. Synthesize a stub descriptor so the events
|
package/dist/core/zapStore.js
CHANGED
|
@@ -76,6 +76,10 @@ async function buildAppMetadataEvent(input) {
|
|
|
76
76
|
const pubkeyHex = await (0, nostr_1.getNpubHex)(input.publishNsec.bytes);
|
|
77
77
|
const npub = (0, nostr_1.pubkeyHexToNpub)(pubkeyHex);
|
|
78
78
|
const communities = input.communities?.length ? input.communities : [exports.DEFAULT_COMMUNITY];
|
|
79
|
+
const summaryTags = input.app.summary !== undefined ? [["summary", input.app.summary]] : [];
|
|
80
|
+
const topicTags = (input.app.tags ?? []).map(tag => ["t", tag]);
|
|
81
|
+
const licenseTags = input.app.license !== undefined ? [["license", input.app.license]] : [];
|
|
82
|
+
const websiteTags = input.app.website !== undefined ? [["url", input.app.website]] : [];
|
|
79
83
|
const repositoryTags = input.app.repository ? [["repository", input.app.repository]] : [];
|
|
80
84
|
const appMetadata = await (0, nostr_1.signNostrEvent)({
|
|
81
85
|
kind: exports.KIND_APP_METADATA,
|
|
@@ -83,8 +87,12 @@ async function buildAppMetadataEvent(input) {
|
|
|
83
87
|
tags: [
|
|
84
88
|
["d", input.app.appId],
|
|
85
89
|
["name", input.app.appName],
|
|
90
|
+
...summaryTags,
|
|
86
91
|
["f", PLATFORM],
|
|
87
92
|
...communities.map(c => ["h", c]),
|
|
93
|
+
...topicTags,
|
|
94
|
+
...licenseTags,
|
|
95
|
+
...websiteTags,
|
|
88
96
|
...repositoryTags,
|
|
89
97
|
],
|
|
90
98
|
content: input.app.description ?? "",
|
|
@@ -128,7 +136,7 @@ async function buildReleaseEvents(input) {
|
|
|
128
136
|
["f", PLATFORM],
|
|
129
137
|
["e", asset.id, input.target.relayUrl],
|
|
130
138
|
],
|
|
131
|
-
content: "",
|
|
139
|
+
content: input.app.releaseNotes ?? "",
|
|
132
140
|
}, input.publishNsec.bytes);
|
|
133
141
|
// kind 30509 ā NIP-C1 Identity Proof (optional, links the APK signing
|
|
134
142
|
// certificate to the publisher Nostr identity). The signature is produced
|
|
@@ -29,7 +29,7 @@ function loadZapstoreRepository(configDir) {
|
|
|
29
29
|
const repository = parsed?.repository;
|
|
30
30
|
if (typeof repository !== "string" || repository.trim().length === 0)
|
|
31
31
|
return undefined;
|
|
32
|
-
return repository.trim();
|
|
32
|
+
return normalizeRepositoryUrl(repository.trim());
|
|
33
33
|
}
|
|
34
34
|
/** Load and validate the Zapstore publisher configuration used for publication. */
|
|
35
35
|
function loadZapstorePublisherConfig(configDir) {
|
|
@@ -48,9 +48,20 @@ function loadZapstorePublisherConfig(configDir) {
|
|
|
48
48
|
throw new ZapstoreConfigError(`${exports.ZAPSTORE_CONFIG_FILENAME} must contain a YAML mapping`, configPath);
|
|
49
49
|
}
|
|
50
50
|
const config = parsed;
|
|
51
|
-
const
|
|
52
|
-
validateRepositoryUrl(
|
|
51
|
+
const rawRepository = requireString(config.repository, "repository", configPath);
|
|
52
|
+
validateRepositoryUrl(rawRepository, configPath);
|
|
53
|
+
const repository = normalizeRepositoryUrl(rawRepository);
|
|
53
54
|
const pubkey = requireString(config.pubkey, "pubkey", configPath);
|
|
55
|
+
const name = optionalString(config.name, "name", configPath);
|
|
56
|
+
const summary = optionalString(config.summary, "summary", configPath);
|
|
57
|
+
const description = optionalString(config.description, "description", configPath, false);
|
|
58
|
+
const tags = optionalTags(config.tags, configPath);
|
|
59
|
+
const license = optionalString(config.license, "license", configPath);
|
|
60
|
+
const website = optionalString(config.website, "website", configPath);
|
|
61
|
+
const releaseNotesPath = optionalString(config.release_notes, "release_notes", configPath);
|
|
62
|
+
const releaseNotes = releaseNotesPath === undefined
|
|
63
|
+
? undefined
|
|
64
|
+
: readReleaseNotes(releaseNotesPath, configDir, configPath);
|
|
54
65
|
let pubkeyHex;
|
|
55
66
|
try {
|
|
56
67
|
pubkeyHex = (0, nostr_1.npubToPubkeyHex)(pubkey);
|
|
@@ -58,7 +69,18 @@ function loadZapstorePublisherConfig(configDir) {
|
|
|
58
69
|
catch (error) {
|
|
59
70
|
throw new ZapstoreConfigError(`Invalid publisher npub in ${exports.ZAPSTORE_CONFIG_FILENAME}: ${error instanceof Error ? error.message : String(error)}`, configPath);
|
|
60
71
|
}
|
|
61
|
-
return {
|
|
72
|
+
return {
|
|
73
|
+
repository,
|
|
74
|
+
pubkey,
|
|
75
|
+
pubkeyHex,
|
|
76
|
+
name,
|
|
77
|
+
summary,
|
|
78
|
+
description,
|
|
79
|
+
tags,
|
|
80
|
+
license,
|
|
81
|
+
website,
|
|
82
|
+
releaseNotes,
|
|
83
|
+
};
|
|
62
84
|
}
|
|
63
85
|
function requireString(value, field, configPath) {
|
|
64
86
|
if (typeof value !== "string" || value.trim().length === 0) {
|
|
@@ -66,6 +88,49 @@ function requireString(value, field, configPath) {
|
|
|
66
88
|
}
|
|
67
89
|
return value.trim();
|
|
68
90
|
}
|
|
91
|
+
function optionalString(value, field, configPath, trim = true) {
|
|
92
|
+
if (value === undefined)
|
|
93
|
+
return undefined;
|
|
94
|
+
if (typeof value !== "string") {
|
|
95
|
+
throw new ZapstoreConfigError(`${exports.ZAPSTORE_CONFIG_FILENAME} field ${field} must be a string`, configPath);
|
|
96
|
+
}
|
|
97
|
+
if (value.trim().length === 0 && field === "release_notes") {
|
|
98
|
+
throw new ZapstoreConfigError(`${exports.ZAPSTORE_CONFIG_FILENAME} field ${field} must not be empty`, configPath);
|
|
99
|
+
}
|
|
100
|
+
return trim ? value.trim() : value;
|
|
101
|
+
}
|
|
102
|
+
function optionalTags(value, configPath) {
|
|
103
|
+
if (value === undefined)
|
|
104
|
+
return undefined;
|
|
105
|
+
if (!Array.isArray(value)) {
|
|
106
|
+
throw new ZapstoreConfigError(`${exports.ZAPSTORE_CONFIG_FILENAME} field tags must be an array of non-empty strings`, configPath);
|
|
107
|
+
}
|
|
108
|
+
return value.map(tag => {
|
|
109
|
+
if (typeof tag !== "string" || tag.trim().length === 0) {
|
|
110
|
+
throw new ZapstoreConfigError(`${exports.ZAPSTORE_CONFIG_FILENAME} field tags must be an array of non-empty strings`, configPath);
|
|
111
|
+
}
|
|
112
|
+
return tag.trim();
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function readReleaseNotes(releaseNotesPath, configDir, configPath) {
|
|
116
|
+
const resolved = path_1.default.resolve(configDir, releaseNotesPath);
|
|
117
|
+
if (!fs_1.default.existsSync(resolved) || !fs_1.default.lstatSync(resolved).isFile()) {
|
|
118
|
+
throw new ZapstoreConfigError(`${exports.ZAPSTORE_CONFIG_FILENAME} release_notes file not found: ${resolved}`, configPath);
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
return fs_1.default.readFileSync(resolved, "utf8");
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
throw new ZapstoreConfigError(`Could not read ${exports.ZAPSTORE_CONFIG_FILENAME} release_notes file ${resolved}: ${error instanceof Error ? error.message : String(error)}`, configPath);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function normalizeRepositoryUrl(repository) {
|
|
128
|
+
const queryIndex = repository.indexOf("?");
|
|
129
|
+
const fragmentIndex = repository.indexOf("#");
|
|
130
|
+
if (queryIndex !== -1 || fragmentIndex !== -1)
|
|
131
|
+
return repository;
|
|
132
|
+
return repository.endsWith(".git") ? repository.slice(0, -4) : repository;
|
|
133
|
+
}
|
|
69
134
|
function validateRepositoryUrl(repository, configPath) {
|
|
70
135
|
let parsed;
|
|
71
136
|
try {
|