@ericsanchezok/synergy-plugin-kit 2.2.1 → 2.2.3
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/build.js +17 -22
- package/dist/commands/pack.js +12 -0
- package/dist/commands/publish-market.d.ts +1 -1
- package/dist/commands/publish-market.js +5 -4
- package/dist/commands/sign.js +6 -5
- package/dist/commands/validate.js +36 -13
- package/dist/lib/artifact-assets.d.ts +17 -0
- package/dist/lib/artifact-assets.js +158 -0
- package/dist/lib/hash.js +3 -4
- package/dist/lib/market-entry.d.ts +4 -0
- package/dist/lib/market-entry.js +4 -0
- package/package.json +2 -2
package/dist/commands/build.js
CHANGED
|
@@ -5,6 +5,7 @@ import { PluginManifest } from "@ericsanchezok/synergy-plugin";
|
|
|
5
5
|
import { cmd } from "../cmd";
|
|
6
6
|
import { UI } from "../ui";
|
|
7
7
|
import { sha256File, sha256JSON } from "../lib/crypto";
|
|
8
|
+
import { collectPackagedAssets, copyPackagedAsset, hashPackagedFiles, rewritePackagedManifestPaths, } from "../lib/artifact-assets";
|
|
8
9
|
function ensureDir(dirPath) {
|
|
9
10
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
10
11
|
}
|
|
@@ -19,28 +20,12 @@ function copyDir(src, dest) {
|
|
|
19
20
|
fs.copyFileSync(srcPath, destPath);
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
|
-
function copyFilePreserve(pluginDir, distDir, relativePath) {
|
|
23
|
-
const normalized = relativePath.replace(/^\.\//, "");
|
|
24
|
-
const src = path.resolve(pluginDir, normalized);
|
|
25
|
-
if (!fs.existsSync(src) || !fs.statSync(src).isFile())
|
|
26
|
-
return;
|
|
27
|
-
const dest = path.join(distDir, normalized);
|
|
28
|
-
ensureDir(path.dirname(dest));
|
|
29
|
-
fs.copyFileSync(src, dest);
|
|
30
|
-
}
|
|
31
23
|
function findUiSource(pluginDir) {
|
|
32
24
|
const candidates = ["src/ui.tsx", "src/ui/index.tsx", "src/ui.ts", "src/ui/index.ts"];
|
|
33
25
|
return candidates.map((candidate) => path.join(pluginDir, candidate)).find((candidate) => fs.existsSync(candidate));
|
|
34
26
|
}
|
|
35
27
|
function packagedManifest(manifest) {
|
|
36
|
-
|
|
37
|
-
next.main = "./runtime/index.js";
|
|
38
|
-
if (next.contributes?.ui?.entry) {
|
|
39
|
-
next.contributes.ui.entry = next.contributes.ui.entry.replace(/^\.\//, "").replace(/^dist\//, "./");
|
|
40
|
-
if (!next.contributes.ui.entry.startsWith("."))
|
|
41
|
-
next.contributes.ui.entry = `./${next.contributes.ui.entry}`;
|
|
42
|
-
}
|
|
43
|
-
return next;
|
|
28
|
+
return rewritePackagedManifestPaths(manifest);
|
|
44
29
|
}
|
|
45
30
|
function permissionSummary(manifest) {
|
|
46
31
|
const perms = manifest.permissions ?? {};
|
|
@@ -136,6 +121,16 @@ export async function buildPluginProject(pluginDir) {
|
|
|
136
121
|
return false;
|
|
137
122
|
}
|
|
138
123
|
}
|
|
124
|
+
spinner("Copying declared assets");
|
|
125
|
+
try {
|
|
126
|
+
for (const asset of collectPackagedAssets(manifest)) {
|
|
127
|
+
copyPackagedAsset(pluginDir, distDir, asset);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
UI.error(error instanceof Error ? error.message : String(error));
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
139
134
|
spinner("Normalizing manifest");
|
|
140
135
|
const distManifest = packagedManifest(manifest);
|
|
141
136
|
const distManifestPath = path.join(distDir, "plugin.json");
|
|
@@ -157,10 +152,6 @@ export async function buildPluginProject(pluginDir) {
|
|
|
157
152
|
spinner("Copying assets");
|
|
158
153
|
copyDir(publicAssetsPath, path.join(distDir, "assets"));
|
|
159
154
|
}
|
|
160
|
-
for (const theme of manifest.contributes?.ui?.themes ?? [])
|
|
161
|
-
copyFilePreserve(pluginDir, distDir, theme.path);
|
|
162
|
-
for (const icon of manifest.contributes?.ui?.icons ?? [])
|
|
163
|
-
copyFilePreserve(pluginDir, distDir, icon.path);
|
|
164
155
|
spinner("Computing integrity hashes");
|
|
165
156
|
const integrity = {
|
|
166
157
|
manifest: sha256File(distManifestPath),
|
|
@@ -174,7 +165,11 @@ export async function buildPluginProject(pluginDir) {
|
|
|
174
165
|
if (fs.existsSync(uiIndex))
|
|
175
166
|
integrity.ui = sha256File(uiIndex);
|
|
176
167
|
}
|
|
177
|
-
|
|
168
|
+
const integrityPayload = {
|
|
169
|
+
...integrity,
|
|
170
|
+
files: hashPackagedFiles(distDir),
|
|
171
|
+
};
|
|
172
|
+
fs.writeFileSync(path.join(distDir, "integrity.json"), JSON.stringify(integrityPayload, null, 2));
|
|
178
173
|
UI.println(`${UI.Style.TEXT_SUCCESS}✔${UI.Style.TEXT_NORMAL} Built ${manifest.name} v${manifest.version} -> ${distDir}`);
|
|
179
174
|
UI.println(` ${UI.Style.TEXT_DIM}Output:${UI.Style.TEXT_NORMAL} ${distDir}`);
|
|
180
175
|
return true;
|
package/dist/commands/pack.js
CHANGED
|
@@ -4,6 +4,7 @@ import { PluginManifest } from "@ericsanchezok/synergy-plugin";
|
|
|
4
4
|
import { cmd } from "../cmd";
|
|
5
5
|
import { UI } from "../ui";
|
|
6
6
|
import { sha256File } from "../lib/crypto";
|
|
7
|
+
import { missingPackagedAssets } from "../lib/artifact-assets";
|
|
7
8
|
function formatSize(bytes) {
|
|
8
9
|
if (bytes < 1024)
|
|
9
10
|
return `${bytes} B`;
|
|
@@ -31,6 +32,17 @@ export function packPluginProject(pluginDir) {
|
|
|
31
32
|
if (!fs.existsSync(path.join(distDir, "plugin.json"))) {
|
|
32
33
|
throw new Error(`dist/plugin.json not found at ${distDir}. Run "synergy-plugin build" first.`);
|
|
33
34
|
}
|
|
35
|
+
for (const required of ["runtime/index.js", "integrity.json", "permissions.summary.json"]) {
|
|
36
|
+
if (!fs.existsSync(path.join(distDir, required))) {
|
|
37
|
+
throw new Error(`dist/${required} not found at ${distDir}. Run "synergy-plugin build" first.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const distManifest = PluginManifest.parse(JSON.parse(fs.readFileSync(path.join(distDir, "plugin.json"), "utf-8")));
|
|
41
|
+
const missing = missingPackagedAssets(distDir, distManifest);
|
|
42
|
+
if (missing.length > 0) {
|
|
43
|
+
const details = missing.map((asset) => ` - ${asset.label}: ${asset.packageRelative}`).join("\n");
|
|
44
|
+
throw new Error(`dist/ is missing manifest-declared plugin assets:\n${details}`);
|
|
45
|
+
}
|
|
34
46
|
const tgzName = `${safePackageName(manifest.name)}-${manifest.version}.synergy-plugin.tgz`;
|
|
35
47
|
const result = Bun.spawnSync(["tar", "-czf", tgzName, "-C", distDir, "."], { cwd: pluginDir });
|
|
36
48
|
if (result.exitCode !== 0) {
|
|
@@ -87,6 +87,7 @@ async function ensureGitHubReleaseAssets(input) {
|
|
|
87
87
|
}
|
|
88
88
|
async function runRegistryValidation(registryDir) {
|
|
89
89
|
await $ `bun install`.cwd(registryDir);
|
|
90
|
+
await $ `bun run build-registry`.cwd(registryDir);
|
|
90
91
|
await $ `bun run validate`.cwd(registryDir);
|
|
91
92
|
await $ `bun run build-registry --check`.cwd(registryDir);
|
|
92
93
|
}
|
|
@@ -158,10 +159,10 @@ export const PluginPublishMarketCommand = cmd({
|
|
|
158
159
|
default: false,
|
|
159
160
|
describe: "do not create/upload GitHub Release assets",
|
|
160
161
|
})
|
|
161
|
-
.option("
|
|
162
|
+
.option("pr", {
|
|
162
163
|
type: "boolean",
|
|
163
|
-
default:
|
|
164
|
-
describe: "
|
|
164
|
+
default: true,
|
|
165
|
+
describe: "open a PR after preparing registry changes; pass --no-pr to skip",
|
|
165
166
|
})
|
|
166
167
|
.option("changelog", {
|
|
167
168
|
type: "string",
|
|
@@ -212,7 +213,7 @@ export const PluginPublishMarketCommand = cmd({
|
|
|
212
213
|
registryDir,
|
|
213
214
|
pluginId: entry.id,
|
|
214
215
|
version: manifest.version,
|
|
215
|
-
noPr:
|
|
216
|
+
noPr: args.pr === false,
|
|
216
217
|
});
|
|
217
218
|
UI.println(`${UI.Style.TEXT_SUCCESS}✔${UI.Style.TEXT_NORMAL} Marketplace publishing request prepared for ${entry.id} v${manifest.version}`);
|
|
218
219
|
}
|
package/dist/commands/sign.js
CHANGED
|
@@ -6,7 +6,9 @@ import { PluginManifest } from "@ericsanchezok/synergy-plugin";
|
|
|
6
6
|
import { cmd } from "../cmd";
|
|
7
7
|
import { UI } from "../ui";
|
|
8
8
|
import { SIGNING_KEYS_DIR, SIGNING_KEY_FILE } from "../lib/paths";
|
|
9
|
-
import {
|
|
9
|
+
import { sha256File } from "../lib/crypto";
|
|
10
|
+
import { baseCapabilities } from "../lib/capability";
|
|
11
|
+
import { computeManifestHash, computePermissionsHash } from "../lib/hash";
|
|
10
12
|
function extractFromTarball(tarballPath, memberPath) {
|
|
11
13
|
const result = Bun.spawnSync(["tar", "-xOf", tarballPath, memberPath], { stdout: "pipe", stderr: "pipe" });
|
|
12
14
|
if (result.exitCode !== 0)
|
|
@@ -52,8 +54,7 @@ export async function signPluginTarball(tarballPath, options = {}) {
|
|
|
52
54
|
catch {
|
|
53
55
|
throw new Error("Failed to parse plugin.normalized.json from tarball");
|
|
54
56
|
}
|
|
55
|
-
|
|
56
|
-
if (!permissionsRaw) {
|
|
57
|
+
if (!extractFromTarball(tarballPath, "permissions.summary.json")) {
|
|
57
58
|
throw new Error("Failed to extract permissions.summary.json from tarball. Has the plugin been built?");
|
|
58
59
|
}
|
|
59
60
|
let keyFile = readKeyFile();
|
|
@@ -66,8 +67,8 @@ export async function signPluginTarball(tarballPath, options = {}) {
|
|
|
66
67
|
}
|
|
67
68
|
const payload = {
|
|
68
69
|
tarballHash,
|
|
69
|
-
manifestHash:
|
|
70
|
-
permissionsHash:
|
|
70
|
+
manifestHash: computeManifestHash(manifest),
|
|
71
|
+
permissionsHash: computePermissionsHash(manifest, baseCapabilities(manifest)),
|
|
71
72
|
};
|
|
72
73
|
const privateKey = await importPrivateKey(keyFile.privateKey);
|
|
73
74
|
const sigRaw = await subtle.sign("Ed25519", privateKey, new TextEncoder().encode(JSON.stringify(payload)));
|
|
@@ -10,6 +10,7 @@ import { computeRisk } from "../lib/risk";
|
|
|
10
10
|
import { validateRuntimePolicy } from "../lib/runtime-policy";
|
|
11
11
|
import { validateRuntimeDiscovery } from "../lib/runtime-discovery";
|
|
12
12
|
import { assertCanonicalPluginIdentity, importUrlForEntry, resolveEntryFromPluginDir } from "../lib/spec";
|
|
13
|
+
import { collectPackagedAssets, resolveUnder } from "../lib/artifact-assets";
|
|
13
14
|
function scanExports(source) {
|
|
14
15
|
const names = new Set();
|
|
15
16
|
const declRe = /^export\s+(?:const|function|class|interface|type|let|var)\s+(\w+)/gm;
|
|
@@ -183,21 +184,35 @@ export async function validatePluginProject(pluginPath, options = {}) {
|
|
|
183
184
|
checkExport("uiCommand", ui.commands);
|
|
184
185
|
}
|
|
185
186
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
187
|
+
}
|
|
188
|
+
const uiSource = findUiSource(pluginDir);
|
|
189
|
+
let packagedAssets = [];
|
|
190
|
+
try {
|
|
191
|
+
packagedAssets = collectPackagedAssets(m);
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
results.push({ type: "error", message: error instanceof Error ? error.message : String(error) });
|
|
195
|
+
}
|
|
196
|
+
for (const asset of packagedAssets) {
|
|
197
|
+
if (asset.label === "UI entry" && uiSource && !fs.existsSync(path.resolve(pluginDir, asset.sourceRelative)))
|
|
198
|
+
continue;
|
|
199
|
+
try {
|
|
200
|
+
const assetPath = resolveUnder(pluginDir, asset.sourceRelative);
|
|
201
|
+
if (!fs.existsSync(assetPath)) {
|
|
202
|
+
results.push({ type: "error", message: `${asset.label} ${asset.sourceRelative} not found` });
|
|
203
|
+
continue;
|
|
189
204
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
results.push({ type: "error", message: `theme "${theme.id}" path ${theme.path} not found` });
|
|
205
|
+
const stat = fs.statSync(assetPath);
|
|
206
|
+
if (asset.kind === "dir" && !stat.isDirectory()) {
|
|
207
|
+
results.push({ type: "error", message: `${asset.label} ${asset.sourceRelative} is not a directory` });
|
|
194
208
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (!fs.existsSync(path.resolve(pluginDir, route.entry))) {
|
|
198
|
-
results.push({ type: "error", message: `route "${route.path}" entry ${route.entry} not found` });
|
|
209
|
+
if (asset.kind === "file" && !stat.isFile()) {
|
|
210
|
+
results.push({ type: "error", message: `${asset.label} ${asset.sourceRelative} is not a file` });
|
|
199
211
|
}
|
|
200
212
|
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
results.push({ type: "error", message: error instanceof Error ? error.message : String(error) });
|
|
215
|
+
}
|
|
201
216
|
}
|
|
202
217
|
for (const tool of m.contributes?.tools ?? []) {
|
|
203
218
|
if (!tool.capabilities)
|
|
@@ -207,7 +222,10 @@ export async function validatePluginProject(pluginPath, options = {}) {
|
|
|
207
222
|
if (isValidJsonSchema(m.contributes.config.schema))
|
|
208
223
|
results.push({ type: "pass", message: "config schema valid" });
|
|
209
224
|
else
|
|
210
|
-
results.push({
|
|
225
|
+
results.push({
|
|
226
|
+
type: "warn",
|
|
227
|
+
message: 'config schema does not appear to be valid JSON Schema; wrap plugin settings in a top-level schema such as { "type": "object", "properties": { ... } }',
|
|
228
|
+
});
|
|
211
229
|
}
|
|
212
230
|
const pluginRisk = computeRisk(baseCapabilities(m), m);
|
|
213
231
|
results.push(...validateRuntimePolicy({ manifest: m, source: "local", trustTier: "declarative", risk: pluginRisk }));
|
|
@@ -225,9 +243,14 @@ export async function validatePluginProject(pluginPath, options = {}) {
|
|
|
225
243
|
try {
|
|
226
244
|
const mod = await import(importUrlForEntry(entryPath, Date.now()));
|
|
227
245
|
const descriptors = [];
|
|
246
|
+
const seenDescriptors = new Set();
|
|
228
247
|
for (const value of Object.values(mod)) {
|
|
229
248
|
if (value && typeof value === "object" && !Array.isArray(value) && "id" in value && "init" in value) {
|
|
230
|
-
|
|
249
|
+
const descriptor = value;
|
|
250
|
+
if (!seenDescriptors.has(descriptor)) {
|
|
251
|
+
descriptors.push(descriptor);
|
|
252
|
+
seenDescriptors.add(descriptor);
|
|
253
|
+
}
|
|
231
254
|
}
|
|
232
255
|
}
|
|
233
256
|
if (descriptors.length === 0) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PluginManifest } from "@ericsanchezok/synergy-plugin";
|
|
2
|
+
export interface PackagedAsset {
|
|
3
|
+
label: string;
|
|
4
|
+
kind: "file" | "dir";
|
|
5
|
+
sourceRelative: string;
|
|
6
|
+
packageRelative: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function isLocalManifestPath(value: string | undefined): value is string;
|
|
9
|
+
export declare function normalizeManifestPath(value: string): string;
|
|
10
|
+
export declare function packageRelativePath(value: string): string;
|
|
11
|
+
export declare function packageManifestPath(value: string): string;
|
|
12
|
+
export declare function collectPackagedAssets(manifest: PluginManifest): PackagedAsset[];
|
|
13
|
+
export declare function rewritePackagedManifestPaths(manifest: PluginManifest): PluginManifest;
|
|
14
|
+
export declare function resolveUnder(root: string, relativePath: string): string;
|
|
15
|
+
export declare function copyPackagedAsset(pluginDir: string, distDir: string, asset: PackagedAsset): void;
|
|
16
|
+
export declare function hashPackagedFiles(distDir: string): Record<string, string>;
|
|
17
|
+
export declare function missingPackagedAssets(distDir: string, manifest: PluginManifest): PackagedAsset[];
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { sha256File } from "./crypto";
|
|
4
|
+
function isExternalPath(value) {
|
|
5
|
+
return /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value) || value.startsWith("//");
|
|
6
|
+
}
|
|
7
|
+
export function isLocalManifestPath(value) {
|
|
8
|
+
return Boolean(value && !isExternalPath(value));
|
|
9
|
+
}
|
|
10
|
+
export function normalizeManifestPath(value) {
|
|
11
|
+
const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
12
|
+
if (!normalized || normalized === ".")
|
|
13
|
+
throw new Error("Manifest path cannot be empty");
|
|
14
|
+
if (path.posix.isAbsolute(normalized) || path.isAbsolute(value)) {
|
|
15
|
+
throw new Error(`Manifest path must be relative: ${value}`);
|
|
16
|
+
}
|
|
17
|
+
const parts = normalized.split("/");
|
|
18
|
+
if (parts.includes(".."))
|
|
19
|
+
throw new Error(`Manifest path cannot escape the plugin directory: ${value}`);
|
|
20
|
+
return path.posix.normalize(normalized);
|
|
21
|
+
}
|
|
22
|
+
export function packageRelativePath(value) {
|
|
23
|
+
const normalized = normalizeManifestPath(value);
|
|
24
|
+
return normalized.startsWith("dist/") ? normalized.slice("dist/".length) : normalized;
|
|
25
|
+
}
|
|
26
|
+
export function packageManifestPath(value) {
|
|
27
|
+
return `./${packageRelativePath(value)}`;
|
|
28
|
+
}
|
|
29
|
+
function addAsset(assets, input) {
|
|
30
|
+
if (!isLocalManifestPath(input.path))
|
|
31
|
+
return;
|
|
32
|
+
assets.push({
|
|
33
|
+
label: input.label,
|
|
34
|
+
kind: input.kind,
|
|
35
|
+
sourceRelative: normalizeManifestPath(input.path),
|
|
36
|
+
packageRelative: packageRelativePath(input.path),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function addSandboxEntries(assets, label, entries) {
|
|
40
|
+
for (const entry of entries ?? []) {
|
|
41
|
+
addAsset(assets, {
|
|
42
|
+
label: `${label}${entry.id ? ` "${entry.id}"` : ""} sandbox entry`,
|
|
43
|
+
kind: "file",
|
|
44
|
+
path: entry.sandboxEntry,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function collectPackagedAssets(manifest) {
|
|
49
|
+
const assets = [];
|
|
50
|
+
for (const skill of manifest.contributes?.skills ?? []) {
|
|
51
|
+
addAsset(assets, { label: `skill "${skill.name}" directory`, kind: "dir", path: skill.dir });
|
|
52
|
+
}
|
|
53
|
+
if (manifest.lifecycle?.install)
|
|
54
|
+
addAsset(assets, { label: "install lifecycle script", kind: "file", path: manifest.lifecycle.install });
|
|
55
|
+
if (manifest.lifecycle?.uninstall)
|
|
56
|
+
addAsset(assets, { label: "uninstall lifecycle script", kind: "file", path: manifest.lifecycle.uninstall });
|
|
57
|
+
if (manifest.lifecycle?.update)
|
|
58
|
+
addAsset(assets, { label: "update lifecycle script", kind: "file", path: manifest.lifecycle.update });
|
|
59
|
+
const ui = manifest.contributes?.ui;
|
|
60
|
+
if (ui?.entry)
|
|
61
|
+
addAsset(assets, { label: "UI entry", kind: "file", path: ui.entry });
|
|
62
|
+
for (const route of ui?.routes ?? []) {
|
|
63
|
+
addAsset(assets, { label: `route "${route.path}" entry`, kind: "file", path: route.entry });
|
|
64
|
+
}
|
|
65
|
+
addSandboxEntries(assets, "workspace panel", ui?.workspacePanels);
|
|
66
|
+
addSandboxEntries(assets, "global panel", ui?.globalPanels);
|
|
67
|
+
addSandboxEntries(assets, "settings", ui?.settings);
|
|
68
|
+
for (const theme of ui?.themes ?? [])
|
|
69
|
+
addAsset(assets, { label: `theme "${theme.id}"`, kind: "file", path: theme.path });
|
|
70
|
+
for (const icon of ui?.icons ?? [])
|
|
71
|
+
addAsset(assets, { label: `icon "${icon.name}"`, kind: "file", path: icon.path });
|
|
72
|
+
const seen = new Set();
|
|
73
|
+
return assets.filter((asset) => {
|
|
74
|
+
const key = `${asset.kind}:${asset.packageRelative}`;
|
|
75
|
+
if (seen.has(key))
|
|
76
|
+
return false;
|
|
77
|
+
seen.add(key);
|
|
78
|
+
return true;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
export function rewritePackagedManifestPaths(manifest) {
|
|
82
|
+
const next = structuredClone(manifest);
|
|
83
|
+
next.main = "./runtime/index.js";
|
|
84
|
+
const ui = next.contributes?.ui;
|
|
85
|
+
if (!ui)
|
|
86
|
+
return next;
|
|
87
|
+
if (ui.entry)
|
|
88
|
+
ui.entry = packageManifestPath(ui.entry);
|
|
89
|
+
for (const route of ui.routes ?? [])
|
|
90
|
+
route.entry = packageManifestPath(route.entry);
|
|
91
|
+
for (const panel of ui.workspacePanels ?? []) {
|
|
92
|
+
if (panel.sandboxEntry)
|
|
93
|
+
panel.sandboxEntry = packageManifestPath(panel.sandboxEntry);
|
|
94
|
+
}
|
|
95
|
+
for (const panel of ui.globalPanels ?? []) {
|
|
96
|
+
if (panel.sandboxEntry)
|
|
97
|
+
panel.sandboxEntry = packageManifestPath(panel.sandboxEntry);
|
|
98
|
+
}
|
|
99
|
+
for (const settings of ui.settings ?? []) {
|
|
100
|
+
if (settings.sandboxEntry)
|
|
101
|
+
settings.sandboxEntry = packageManifestPath(settings.sandboxEntry);
|
|
102
|
+
}
|
|
103
|
+
return next;
|
|
104
|
+
}
|
|
105
|
+
export function resolveUnder(root, relativePath) {
|
|
106
|
+
const normalized = normalizeManifestPath(relativePath);
|
|
107
|
+
const resolved = path.resolve(root, normalized);
|
|
108
|
+
const relative = path.relative(root, resolved);
|
|
109
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
110
|
+
throw new Error(`Manifest path cannot escape ${root}: ${relativePath}`);
|
|
111
|
+
}
|
|
112
|
+
return resolved;
|
|
113
|
+
}
|
|
114
|
+
export function copyPackagedAsset(pluginDir, distDir, asset) {
|
|
115
|
+
const src = resolveUnder(pluginDir, asset.sourceRelative);
|
|
116
|
+
const dest = resolveUnder(distDir, asset.packageRelative);
|
|
117
|
+
if (!fs.existsSync(src))
|
|
118
|
+
throw new Error(`${asset.label} not found at ${asset.sourceRelative}`);
|
|
119
|
+
const stat = fs.statSync(src);
|
|
120
|
+
if (asset.kind === "dir") {
|
|
121
|
+
if (!stat.isDirectory())
|
|
122
|
+
throw new Error(`${asset.label} must be a directory: ${asset.sourceRelative}`);
|
|
123
|
+
if (path.resolve(src) === path.resolve(dest))
|
|
124
|
+
return;
|
|
125
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
126
|
+
fs.cpSync(src, dest, { recursive: true });
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (!stat.isFile())
|
|
130
|
+
throw new Error(`${asset.label} must be a file: ${asset.sourceRelative}`);
|
|
131
|
+
if (path.resolve(src) === path.resolve(dest))
|
|
132
|
+
return;
|
|
133
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
134
|
+
fs.copyFileSync(src, dest);
|
|
135
|
+
}
|
|
136
|
+
function walkFiles(root, dir, output) {
|
|
137
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
138
|
+
const filepath = path.join(dir, entry.name);
|
|
139
|
+
if (entry.isDirectory()) {
|
|
140
|
+
walkFiles(root, filepath, output);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (!entry.isFile())
|
|
144
|
+
continue;
|
|
145
|
+
const relative = path.relative(root, filepath).split(path.sep).join("/");
|
|
146
|
+
if (relative === "integrity.json")
|
|
147
|
+
continue;
|
|
148
|
+
output[relative] = sha256File(filepath);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export function hashPackagedFiles(distDir) {
|
|
152
|
+
const files = {};
|
|
153
|
+
walkFiles(distDir, distDir, files);
|
|
154
|
+
return Object.fromEntries(Object.entries(files).sort(([a], [b]) => a.localeCompare(b)));
|
|
155
|
+
}
|
|
156
|
+
export function missingPackagedAssets(distDir, manifest) {
|
|
157
|
+
return collectPackagedAssets(manifest).filter((asset) => !fs.existsSync(resolveUnder(distDir, asset.packageRelative)));
|
|
158
|
+
}
|
package/dist/lib/hash.js
CHANGED
|
@@ -3,12 +3,11 @@ export function computePermissionsHash(manifest, capabilities) {
|
|
|
3
3
|
const normalized = {
|
|
4
4
|
capabilities: [...capabilities].sort(),
|
|
5
5
|
permissions: manifest.permissions ?? {},
|
|
6
|
-
contributes: manifest.contributes
|
|
7
|
-
|
|
6
|
+
contributes: manifest.contributes ?? {},
|
|
7
|
+
lifecycle: manifest.lifecycle ?? {},
|
|
8
8
|
};
|
|
9
9
|
return sha256Content(JSON.stringify(sortKeys(normalized)));
|
|
10
10
|
}
|
|
11
11
|
export function computeManifestHash(manifest) {
|
|
12
|
-
|
|
13
|
-
return sha256Content(JSON.stringify(sortKeys(identity)));
|
|
12
|
+
return sha256Content(JSON.stringify(sortKeys(manifest)));
|
|
14
13
|
}
|
package/dist/lib/market-entry.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@ericsanchezok/synergy-plugin-kit",
|
|
4
|
-
"version": "2.2.
|
|
4
|
+
"version": "2.2.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"dist"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@ericsanchezok/synergy-plugin": "2.2.
|
|
37
|
+
"@ericsanchezok/synergy-plugin": "2.2.2",
|
|
38
38
|
"yargs": "18.0.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|