@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.
Files changed (57) hide show
  1. package/README.md +74 -1
  2. package/dist/data/files/file-read-stream.js +7 -0
  3. package/dist/data/files/file.types.d.ts +6 -0
  4. package/dist/data/files/file.types.js +18 -0
  5. package/dist/data/files/files.test.js +50 -7
  6. package/dist/data/package-version-resolver.test.js +1 -0
  7. package/dist/data/package-versions.d.ts +3 -0
  8. package/dist/data/package-versions.js +5 -0
  9. package/dist/data/packages.d.ts +6 -0
  10. package/dist/data/packages.js +8 -0
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.js +1 -0
  13. package/dist/package-installer/index.d.ts +10 -0
  14. package/dist/package-installer/index.js +26 -0
  15. package/dist/package-installer/package-author-signing.d.ts +48 -0
  16. package/dist/package-installer/package-author-signing.js +73 -0
  17. package/dist/package-installer/package-author-signing.test.d.ts +1 -0
  18. package/dist/package-installer/package-author-signing.test.js +189 -0
  19. package/dist/package-installer/package-cloner.d.ts +16 -0
  20. package/dist/package-installer/package-cloner.js +115 -0
  21. package/dist/package-installer/package-cloner.test.d.ts +1 -0
  22. package/dist/package-installer/package-cloner.test.js +276 -0
  23. package/dist/package-installer/package-creator.d.ts +22 -0
  24. package/dist/package-installer/package-creator.js +154 -0
  25. package/dist/package-installer/package-creator.test.d.ts +1 -0
  26. package/dist/package-installer/package-creator.test.js +354 -0
  27. package/dist/package-installer/package-installer.d.ts +32 -0
  28. package/dist/package-installer/package-installer.js +247 -0
  29. package/dist/package-installer/package-installer.test.d.ts +1 -0
  30. package/dist/package-installer/package-installer.test.js +666 -0
  31. package/dist/package-installer/package-propagation.d.ts +29 -0
  32. package/dist/package-installer/package-propagation.js +363 -0
  33. package/dist/package-installer/package-propagation.test.d.ts +1 -0
  34. package/dist/package-installer/package-propagation.test.js +1145 -0
  35. package/dist/package-installer/package-publisher.d.ts +50 -0
  36. package/dist/package-installer/package-publisher.js +67 -0
  37. package/dist/package-installer/package-publisher.test.d.ts +1 -0
  38. package/dist/package-installer/package-publisher.test.js +142 -0
  39. package/dist/package-installer/package-remote-checker.d.ts +54 -0
  40. package/dist/package-installer/package-remote-checker.js +186 -0
  41. package/dist/package-installer/package-remote-checker.test.d.ts +1 -0
  42. package/dist/package-installer/package-remote-checker.test.js +263 -0
  43. package/dist/package-installer/package-seed-installer.d.ts +45 -0
  44. package/dist/package-installer/package-seed-installer.js +108 -0
  45. package/dist/package-installer/package-seed-installer.test.d.ts +1 -0
  46. package/dist/package-installer/package-seed-installer.test.js +123 -0
  47. package/dist/package-installer/package-tarball.d.ts +35 -0
  48. package/dist/package-installer/package-tarball.js +57 -0
  49. package/dist/package-installer/package-tarball.test.d.ts +1 -0
  50. package/dist/package-installer/package-tarball.test.js +75 -0
  51. package/dist/package-installer/types.d.ts +110 -0
  52. package/dist/package-installer/types.js +2 -0
  53. package/dist/rpc-types.d.ts +14 -0
  54. package/dist/rpc-types.js +6 -0
  55. package/dist/system-ids.d.ts +1 -0
  56. package/dist/system-ids.js +2 -1
  57. 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 {};