pakstr 0.23.0 → 0.24.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.
@@ -15,6 +15,18 @@ const pakstrConfig_1 = require("../core/pakstrConfig");
15
15
  const nsite_1 = require("../core/nsite");
16
16
  const zapStore_1 = require("../core/zapStore");
17
17
  const zapstoreConfig_1 = require("../core/zapstoreConfig");
18
+ function imageContentType(filePath) {
19
+ switch (path_1.default.extname(filePath).toLowerCase()) {
20
+ case ".png": return "image/png";
21
+ case ".jpg":
22
+ case ".jpeg": return "image/jpeg";
23
+ case ".gif": return "image/gif";
24
+ case ".webp": return "image/webp";
25
+ case ".svg": return "image/svg+xml";
26
+ case ".ico": return "image/x-icon";
27
+ default: return "application/octet-stream";
28
+ }
29
+ }
18
30
  /** `pakstr publish` — optionally upload the signed APK and publish its Zapstore events. */
19
31
  async function publishCommand(configPath, options = {}) {
20
32
  if (options.blossomFetch && options.fetchImpl) {
@@ -108,6 +120,15 @@ async function publishCommand(configPath, options = {}) {
108
120
  console.log("Publishing as:", publishNsec.envVar);
109
121
  console.log("Publisher npub:", npub);
110
122
  console.log("Relay:", config.publish.relay);
123
+ const iconUrl = zapstoreConfig?.icon
124
+ ? (await (0, blossom_1.uploadToBlossom)({
125
+ serverUrl: config.publish.blossom,
126
+ filePath: zapstoreConfig.icon,
127
+ secret: publishNsec.bytes,
128
+ contentType: imageContentType(zapstoreConfig.icon),
129
+ fetchImpl,
130
+ })).url
131
+ : undefined;
111
132
  app = {
112
133
  appId: config.app.appId,
113
134
  appName: zapstoreConfig?.name ?? config.app.appName,
@@ -118,6 +139,7 @@ async function publishCommand(configPath, options = {}) {
118
139
  tags: zapstoreConfig?.tags,
119
140
  license: zapstoreConfig?.license,
120
141
  website: zapstoreConfig?.website,
142
+ icon: iconUrl,
121
143
  releaseNotes: zapstoreConfig?.releaseNotes,
122
144
  repository: zapstoreConfig?.repository ?? (0, zapstoreConfig_1.loadZapstoreRepository)(config.configDir),
123
145
  };
@@ -77,6 +77,7 @@ async function buildAppMetadataEvent(input) {
77
77
  const npub = (0, nostr_1.pubkeyHexToNpub)(pubkeyHex);
78
78
  const communities = input.communities?.length ? input.communities : [exports.DEFAULT_COMMUNITY];
79
79
  const summaryTags = input.app.summary !== undefined ? [["summary", input.app.summary]] : [];
80
+ const iconTags = input.app.icon !== undefined ? [["icon", input.app.icon]] : [];
80
81
  const topicTags = (input.app.tags ?? []).map(tag => ["t", tag]);
81
82
  const licenseTags = input.app.license !== undefined ? [["license", input.app.license]] : [];
82
83
  const websiteTags = input.app.website !== undefined ? [["url", input.app.website]] : [];
@@ -88,6 +89,7 @@ async function buildAppMetadataEvent(input) {
88
89
  ["d", input.app.appId],
89
90
  ["name", input.app.appName],
90
91
  ...summaryTags,
92
+ ...iconTags,
91
93
  ["f", PLATFORM],
92
94
  ...communities.map(c => ["h", c]),
93
95
  ...topicTags,
@@ -58,6 +58,10 @@ function loadZapstorePublisherConfig(configDir) {
58
58
  const tags = optionalTags(config.tags, configPath);
59
59
  const license = optionalString(config.license, "license", configPath);
60
60
  const website = optionalString(config.website, "website", configPath);
61
+ const iconPath = optionalString(config.icon, "icon", configPath);
62
+ const icon = iconPath === undefined
63
+ ? undefined
64
+ : resolveIconPath(iconPath, configDir, configPath);
61
65
  const releaseNotesPath = optionalString(config.release_notes, "release_notes", configPath);
62
66
  const releaseNotes = releaseNotesPath === undefined
63
67
  ? undefined
@@ -79,6 +83,7 @@ function loadZapstorePublisherConfig(configDir) {
79
83
  tags,
80
84
  license,
81
85
  website,
86
+ icon,
82
87
  releaseNotes,
83
88
  };
84
89
  }
@@ -94,7 +99,7 @@ function optionalString(value, field, configPath, trim = true) {
94
99
  if (typeof value !== "string") {
95
100
  throw new ZapstoreConfigError(`${exports.ZAPSTORE_CONFIG_FILENAME} field ${field} must be a string`, configPath);
96
101
  }
97
- if (value.trim().length === 0 && field === "release_notes") {
102
+ if (value.trim().length === 0 && (field === "release_notes" || field === "icon")) {
98
103
  throw new ZapstoreConfigError(`${exports.ZAPSTORE_CONFIG_FILENAME} field ${field} must not be empty`, configPath);
99
104
  }
100
105
  return trim ? value.trim() : value;
@@ -112,6 +117,19 @@ function optionalTags(value, configPath) {
112
117
  return tag.trim();
113
118
  });
114
119
  }
120
+ function resolveIconPath(iconPath, configDir, configPath) {
121
+ const resolved = path_1.default.resolve(configDir, iconPath);
122
+ if (!fs_1.default.existsSync(resolved) || !fs_1.default.lstatSync(resolved).isFile()) {
123
+ throw new ZapstoreConfigError(`${exports.ZAPSTORE_CONFIG_FILENAME} icon file not found: ${resolved}`, configPath);
124
+ }
125
+ try {
126
+ fs_1.default.accessSync(resolved, fs_1.default.constants.R_OK);
127
+ }
128
+ catch (error) {
129
+ throw new ZapstoreConfigError(`Could not read ${exports.ZAPSTORE_CONFIG_FILENAME} icon file ${resolved}: ${error instanceof Error ? error.message : String(error)}`, configPath);
130
+ }
131
+ return resolved;
132
+ }
115
133
  function readReleaseNotes(releaseNotesPath, configDir, configPath) {
116
134
  const resolved = path_1.default.resolve(configDir, releaseNotesPath);
117
135
  if (!fs_1.default.existsSync(resolved) || !fs_1.default.lstatSync(resolved).isFile()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pakstr",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "description": "CLI for packaging Nostr web apps into Android APKs",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",