@peers-app/peers-sdk 0.18.8 → 0.19.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/README.md +74 -1
- package/dist/data/files/file-read-stream.js +7 -0
- package/dist/data/files/file.types.d.ts +6 -0
- package/dist/data/files/file.types.js +18 -0
- package/dist/data/files/files.test.js +50 -7
- package/dist/data/package-version-resolver.test.js +1 -0
- package/dist/data/package-versions.d.ts +3 -0
- package/dist/data/package-versions.js +5 -0
- package/dist/data/packages.d.ts +6 -0
- package/dist/data/packages.js +8 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/package-installer/index.d.ts +10 -0
- package/dist/package-installer/index.js +26 -0
- package/dist/package-installer/package-author-signing.d.ts +48 -0
- package/dist/package-installer/package-author-signing.js +73 -0
- package/dist/package-installer/package-author-signing.test.d.ts +1 -0
- package/dist/package-installer/package-author-signing.test.js +189 -0
- package/dist/package-installer/package-cloner.d.ts +16 -0
- package/dist/package-installer/package-cloner.js +115 -0
- package/dist/package-installer/package-cloner.test.d.ts +1 -0
- package/dist/package-installer/package-cloner.test.js +276 -0
- package/dist/package-installer/package-creator.d.ts +22 -0
- package/dist/package-installer/package-creator.js +154 -0
- package/dist/package-installer/package-creator.test.d.ts +1 -0
- package/dist/package-installer/package-creator.test.js +354 -0
- package/dist/package-installer/package-installer.d.ts +32 -0
- package/dist/package-installer/package-installer.js +247 -0
- package/dist/package-installer/package-installer.test.d.ts +1 -0
- package/dist/package-installer/package-installer.test.js +666 -0
- package/dist/package-installer/package-propagation.d.ts +29 -0
- package/dist/package-installer/package-propagation.js +363 -0
- package/dist/package-installer/package-propagation.test.d.ts +1 -0
- package/dist/package-installer/package-propagation.test.js +1145 -0
- package/dist/package-installer/package-publisher.d.ts +50 -0
- package/dist/package-installer/package-publisher.js +67 -0
- package/dist/package-installer/package-publisher.test.d.ts +1 -0
- package/dist/package-installer/package-publisher.test.js +142 -0
- package/dist/package-installer/package-remote-checker.d.ts +54 -0
- package/dist/package-installer/package-remote-checker.js +186 -0
- package/dist/package-installer/package-remote-checker.test.d.ts +1 -0
- package/dist/package-installer/package-remote-checker.test.js +263 -0
- package/dist/package-installer/package-seed-installer.d.ts +45 -0
- package/dist/package-installer/package-seed-installer.js +108 -0
- package/dist/package-installer/package-seed-installer.test.d.ts +1 -0
- package/dist/package-installer/package-seed-installer.test.js +123 -0
- package/dist/package-installer/package-tarball.d.ts +35 -0
- package/dist/package-installer/package-tarball.js +57 -0
- package/dist/package-installer/package-tarball.test.d.ts +1 -0
- package/dist/package-installer/package-tarball.test.js +75 -0
- package/dist/package-installer/types.d.ts +110 -0
- package/dist/package-installer/types.js +2 -0
- package/dist/rpc-types.d.ts +14 -0
- package/dist/rpc-types.js +6 -0
- package/dist/system-ids.d.ts +1 -0
- package/dist/system-ids.js +2 -1
- package/package.json +3 -2
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.installPackageFromBundles = installPackageFromBundles;
|
|
4
|
+
exports.installAllPackageBundles = installAllPackageBundles;
|
|
5
|
+
exports.saveBundleFile = saveBundleFile;
|
|
6
|
+
exports.getPackageInfo = getPackageInfo;
|
|
7
|
+
const file_types_1 = require("../data/files/file.types");
|
|
8
|
+
const files_1 = require("../data/files/files");
|
|
9
|
+
const package_version_resolver_1 = require("../data/package-version-resolver");
|
|
10
|
+
const package_versions_1 = require("../data/package-versions");
|
|
11
|
+
const packages_1 = require("../data/packages");
|
|
12
|
+
const persistent_vars_1 = require("../data/persistent-vars");
|
|
13
|
+
const utils_1 = require("../utils");
|
|
14
|
+
/**
|
|
15
|
+
* Install or update a dev PackageVersion from locally-built bundle files.
|
|
16
|
+
*
|
|
17
|
+
* This is the core workhorse of the package-installer module. It reads
|
|
18
|
+
* bundles from `dist/`, hashes them for deduplication, saves File records
|
|
19
|
+
* only when content has changed, upserts the PackageVersion, and optionally
|
|
20
|
+
* triggers the package loader and device version resolution.
|
|
21
|
+
*/
|
|
22
|
+
async function installPackageFromBundles(dataContext, deps, packageId, opts) {
|
|
23
|
+
const versionTag = opts?.versionTag ?? "dev";
|
|
24
|
+
// 1. Resolve local path
|
|
25
|
+
const localPath = opts?.localPath ?? (await getPackageLocalPath(packageId, dataContext, deps));
|
|
26
|
+
if (!(await deps.fs.exists(localPath))) {
|
|
27
|
+
throw new Error(`Package directory does not exist: ${localPath}`);
|
|
28
|
+
}
|
|
29
|
+
// 2. Read package info
|
|
30
|
+
const info = await getPackageInfo(deps, localPath);
|
|
31
|
+
const version = info.version ?? "0.0.1";
|
|
32
|
+
// 3. Read bundle files
|
|
33
|
+
const distDir = deps.resolvePath(localPath, "dist");
|
|
34
|
+
const packageBundlePath = deps.resolvePath(distDir, "package.bundle.js");
|
|
35
|
+
if (!(await deps.fs.exists(packageBundlePath))) {
|
|
36
|
+
throw new Error(`Required bundle not found: ${packageBundlePath}. Run 'npm run build' first.`);
|
|
37
|
+
}
|
|
38
|
+
const packageBundle = await deps.fs.readFile(packageBundlePath);
|
|
39
|
+
const routesBundlePath = deps.resolvePath(distDir, "routes.bundle.js");
|
|
40
|
+
const routesBundle = (await deps.fs.exists(routesBundlePath))
|
|
41
|
+
? await deps.fs.readFile(routesBundlePath)
|
|
42
|
+
: undefined;
|
|
43
|
+
const uiBundlePath = deps.resolvePath(distDir, "uis.bundle.js");
|
|
44
|
+
const uiBundle = (await deps.fs.exists(uiBundlePath))
|
|
45
|
+
? await deps.fs.readFile(uiBundlePath)
|
|
46
|
+
: undefined;
|
|
47
|
+
// 4. Hash each bundle (must match file-system hashes for signature consistency)
|
|
48
|
+
const packageHash = (0, file_types_1.computeFileHash)(packageBundle);
|
|
49
|
+
const routesHash = routesBundle ? (0, file_types_1.computeFileHash)(routesBundle) : undefined;
|
|
50
|
+
const uiHash = uiBundle ? (0, file_types_1.computeFileHash)(uiBundle) : undefined;
|
|
51
|
+
// 5. Compute PV hash
|
|
52
|
+
const pvHash = (0, package_versions_1.computePackageVersionHash)(version, "", packageHash, routesHash, uiHash);
|
|
53
|
+
// 6. Early exit if unchanged
|
|
54
|
+
const pvTable = (0, package_versions_1.PackageVersions)(dataContext);
|
|
55
|
+
const existingPv = await pvTable.findOne({ packageId, versionTag });
|
|
56
|
+
if (existingPv && existingPv.packageVersionHash === pvHash) {
|
|
57
|
+
return {
|
|
58
|
+
packageVersion: existingPv,
|
|
59
|
+
loaded: undefined,
|
|
60
|
+
unchanged: true,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// 7. Save bundle files (deduped by hash)
|
|
64
|
+
const packageFile = await saveBundleFile(dataContext, packageBundle, `${packageId}-package.bundle.js`, packageId);
|
|
65
|
+
let routesFile;
|
|
66
|
+
if (routesBundle) {
|
|
67
|
+
routesFile = await saveBundleFile(dataContext, routesBundle, `${packageId}-routes.bundle.js`, packageId);
|
|
68
|
+
}
|
|
69
|
+
let uiFile;
|
|
70
|
+
if (uiBundle) {
|
|
71
|
+
uiFile = await saveBundleFile(dataContext, uiBundle, `${packageId}-uis.bundle.js`, packageId);
|
|
72
|
+
}
|
|
73
|
+
// 8. Upsert PackageVersion
|
|
74
|
+
const now = new Date().toISOString();
|
|
75
|
+
const pv = {
|
|
76
|
+
packageVersionId: existingPv?.packageVersionId ?? (0, utils_1.newid)(),
|
|
77
|
+
packageId,
|
|
78
|
+
version,
|
|
79
|
+
versionTag,
|
|
80
|
+
packageVersionHash: pvHash,
|
|
81
|
+
packageBundleFileId: packageFile.fileId,
|
|
82
|
+
packageBundleFileHash: packageFile.fileHash,
|
|
83
|
+
routesBundleFileId: routesFile?.fileId,
|
|
84
|
+
routesBundleFileHash: routesFile?.fileHash,
|
|
85
|
+
uiBundleFileId: uiFile?.fileId,
|
|
86
|
+
uiBundleFileHash: uiFile?.fileHash,
|
|
87
|
+
appNavs: existingPv?.appNavs,
|
|
88
|
+
history: existingPv?.history,
|
|
89
|
+
signature: existingPv?.signature ?? "",
|
|
90
|
+
createdBy: existingPv?.createdBy ?? packageId,
|
|
91
|
+
createdAt: now,
|
|
92
|
+
};
|
|
93
|
+
const savedPv = await pvTable.signAndSave(pv);
|
|
94
|
+
// 9. Load package (unless skipLoad)
|
|
95
|
+
let loaded;
|
|
96
|
+
if (!opts?.skipLoad) {
|
|
97
|
+
const pkg = await (0, packages_1.Packages)(dataContext).get(packageId);
|
|
98
|
+
if (pkg) {
|
|
99
|
+
loaded = await dataContext.packageLoader.loadPackage(pkg, {
|
|
100
|
+
force: true,
|
|
101
|
+
localPath,
|
|
102
|
+
packageVersionId: savedPv.packageVersionId,
|
|
103
|
+
});
|
|
104
|
+
// Extract appNavs from loaded instance onto the PV
|
|
105
|
+
if (loaded?.appNavs && JSON.stringify(loaded.appNavs) !== JSON.stringify(savedPv.appNavs)) {
|
|
106
|
+
savedPv.appNavs = loaded.appNavs;
|
|
107
|
+
await pvTable.signAndSave(savedPv);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// 10. Update package description if info provides one and it differs
|
|
112
|
+
const pkg = await (0, packages_1.Packages)(dataContext).get(packageId);
|
|
113
|
+
if (pkg && info.description && info.description !== pkg.description) {
|
|
114
|
+
await (0, packages_1.Packages)(dataContext).signAndSave({
|
|
115
|
+
...pkg,
|
|
116
|
+
description: info.description,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
// 11. Activate on device (unless skipResolve)
|
|
120
|
+
if (!opts?.skipResolve && pkg) {
|
|
121
|
+
await (0, package_version_resolver_1.updatePackagePrefs)(packageId, { activePackageVersionId: savedPv.packageVersionId }, dataContext);
|
|
122
|
+
await (0, package_version_resolver_1.resolveDevicePackageVersion)(pkg, dataContext, {
|
|
123
|
+
force: true,
|
|
124
|
+
localPath,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return { packageVersion: savedPv, loaded, unchanged: false };
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Install bundles for all non-disabled packages in a data context.
|
|
131
|
+
* Errors are caught per-package so one failure doesn't block others.
|
|
132
|
+
*/
|
|
133
|
+
async function installAllPackageBundles(dataContext, deps) {
|
|
134
|
+
const packages = await (0, packages_1.Packages)(dataContext).list();
|
|
135
|
+
for (const pkg of packages) {
|
|
136
|
+
if (pkg.disabled)
|
|
137
|
+
continue;
|
|
138
|
+
try {
|
|
139
|
+
await installPackageFromBundles(dataContext, deps, pkg.packageId);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
console.error(`[package-installer] Failed to install bundles for ${pkg.name} (${pkg.packageId}):`, err);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Save a bundle's content as a File record, deduplicating by hash.
|
|
148
|
+
*
|
|
149
|
+
* If a File with the same hash already exists for this bundle name,
|
|
150
|
+
* the existing record is reused — no new File is created. This eliminates
|
|
151
|
+
* the "new File record on every dev reload" bug.
|
|
152
|
+
*/
|
|
153
|
+
async function saveBundleFile(dataContext, content, name, _packageId) {
|
|
154
|
+
const fileHash = (0, file_types_1.computeFileHash)(content);
|
|
155
|
+
const filesTable = (0, files_1.Files)(dataContext);
|
|
156
|
+
// Check if a file with this hash already exists for this bundle name
|
|
157
|
+
const existing = await filesTable.findOne({ name, fileHash });
|
|
158
|
+
if (existing) {
|
|
159
|
+
return { fileId: existing.fileId, fileHash: existing.fileHash };
|
|
160
|
+
}
|
|
161
|
+
const fileId = (0, utils_1.newid)();
|
|
162
|
+
const metadata = {
|
|
163
|
+
fileId,
|
|
164
|
+
name,
|
|
165
|
+
fileSize: content.length,
|
|
166
|
+
mimeType: "application/javascript",
|
|
167
|
+
};
|
|
168
|
+
const saved = await filesTable.saveFile(metadata, content);
|
|
169
|
+
return { fileId: saved.fileId, fileHash: saved.fileHash };
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Read package metadata from a local package directory.
|
|
173
|
+
* Uses `deps.fs.readJson` instead of `require()` — no cache issues.
|
|
174
|
+
*/
|
|
175
|
+
async function getPackageInfo(deps, localPath) {
|
|
176
|
+
const pkgJsonPath = deps.resolvePath(localPath, "package.json");
|
|
177
|
+
const pkgJson = await deps.fs.readJson(pkgJsonPath);
|
|
178
|
+
const info = {
|
|
179
|
+
packageId: pkgJson.peers?.packageId,
|
|
180
|
+
name: pkgJson.name ?? "unknown",
|
|
181
|
+
version: pkgJson.version,
|
|
182
|
+
description: pkgJson.description,
|
|
183
|
+
};
|
|
184
|
+
// Try to get a richer description from README
|
|
185
|
+
const readmePath = deps.resolvePath(localPath, "README.md");
|
|
186
|
+
if (await deps.fs.exists(readmePath)) {
|
|
187
|
+
try {
|
|
188
|
+
const readme = await deps.fs.readFile(readmePath);
|
|
189
|
+
const firstParagraph = extractReadmeDescription(readme);
|
|
190
|
+
if (firstParagraph) {
|
|
191
|
+
info.description = firstParagraph;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// README read failure is non-fatal
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Try to detect remoteRepo from git
|
|
199
|
+
try {
|
|
200
|
+
const { stdout } = await deps.shell.exec("git remote get-url origin", {
|
|
201
|
+
cwd: localPath,
|
|
202
|
+
});
|
|
203
|
+
const url = stdout.trim();
|
|
204
|
+
if (url) {
|
|
205
|
+
info.remoteRepo = url;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// No git remote is fine
|
|
210
|
+
}
|
|
211
|
+
return info;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Resolve the local filesystem path for a package based on device vars.
|
|
215
|
+
* Awaits the loading promise to ensure the DB-stored value is available
|
|
216
|
+
* before reading. Falls back to `{packagesRootDir}`.
|
|
217
|
+
*/
|
|
218
|
+
async function getPackageLocalPath(packageId, dataContext, deps) {
|
|
219
|
+
const pvar = (0, persistent_vars_1.groupDeviceVar)(`packageLocalPath_${packageId}`, {
|
|
220
|
+
defaultValue: deps.packagesRootDir,
|
|
221
|
+
dataContext,
|
|
222
|
+
});
|
|
223
|
+
await pvar.loadingPromise;
|
|
224
|
+
return deps.resolvePath(pvar() ?? deps.packagesRootDir);
|
|
225
|
+
}
|
|
226
|
+
/** Extract the first non-heading paragraph from a README as a description. */
|
|
227
|
+
function extractReadmeDescription(readme) {
|
|
228
|
+
const lines = readme.split("\n");
|
|
229
|
+
let foundContent = false;
|
|
230
|
+
const descLines = [];
|
|
231
|
+
for (const line of lines) {
|
|
232
|
+
// Skip headings and blank lines at the start
|
|
233
|
+
if (!foundContent) {
|
|
234
|
+
if (line.startsWith("#") || line.trim() === "")
|
|
235
|
+
continue;
|
|
236
|
+
foundContent = true;
|
|
237
|
+
}
|
|
238
|
+
// Stop at the next heading or blank line after content
|
|
239
|
+
if (foundContent) {
|
|
240
|
+
if (line.startsWith("#") || (line.trim() === "" && descLines.length > 0))
|
|
241
|
+
break;
|
|
242
|
+
descLines.push(line.trim());
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const desc = descLines.join(" ").trim();
|
|
246
|
+
return desc || undefined;
|
|
247
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|