@peers-app/peers-sdk 0.19.10 → 0.19.12

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.
@@ -7,4 +7,5 @@ export * from "./package-publisher";
7
7
  export * from "./package-remote-checker";
8
8
  export * from "./package-seed-installer";
9
9
  export * from "./package-tarball";
10
+ export * from "./peers-core-installer";
10
11
  export * from "./types";
@@ -23,4 +23,5 @@ __exportStar(require("./package-publisher"), exports);
23
23
  __exportStar(require("./package-remote-checker"), exports);
24
24
  __exportStar(require("./package-seed-installer"), exports);
25
25
  __exportStar(require("./package-tarball"), exports);
26
+ __exportStar(require("./peers-core-installer"), exports);
26
27
  __exportStar(require("./types"), exports);
@@ -5,8 +5,13 @@
5
5
  * Called at group creation time (not on every startup).
6
6
  */
7
7
  import type { DataContext } from "../context/data-context";
8
- /** Options for seeding a package into a data context. */
9
- export interface ISeedPackageOpts {
8
+ import { type IPackage } from "../data/packages";
9
+ /**
10
+ * Metadata needed to create (or backfill) a {@link IPackage} record, without
11
+ * any version/bundle data. Shared by {@link ensurePackageRecord} and
12
+ * {@link seedPackageInContext}.
13
+ */
14
+ export interface IEnsurePackageRecordOpts {
10
15
  packageId: string;
11
16
  name: string;
12
17
  description?: string;
@@ -16,6 +21,9 @@ export interface ISeedPackageOpts {
16
21
  followVersionTags?: string;
17
22
  updateUrl?: string;
18
23
  remoteRepo?: string;
24
+ }
25
+ /** Options for seeding a package into a data context. */
26
+ export interface ISeedPackageOpts extends IEnsurePackageRecordOpts {
19
27
  /** Bundle data for the initial PackageVersion. */
20
28
  version: string;
21
29
  versionTag: string;
@@ -43,3 +51,11 @@ export interface ISeedPackageOpts {
43
51
  * @returns true if seeding occurred, false if package already existed.
44
52
  */
45
53
  export declare function seedPackageInContext(dataContext: DataContext, opts: ISeedPackageOpts): Promise<boolean>;
54
+ /**
55
+ * Ensure a {@link IPackage} record exists in `dataContext`, creating it from
56
+ * `opts` when absent and backfilling any missing metadata defaults when
57
+ * present. Does not create or modify any PackageVersion.
58
+ *
59
+ * @returns The current (created or existing) Package record.
60
+ */
61
+ export declare function ensurePackageRecord(dataContext: DataContext, opts: IEnsurePackageRecordOpts): Promise<IPackage>;
@@ -7,6 +7,7 @@
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.seedPackageInContext = seedPackageInContext;
10
+ exports.ensurePackageRecord = ensurePackageRecord;
10
11
  const package_versions_1 = require("../data/package-versions");
11
12
  const packages_1 = require("../data/packages");
12
13
  const utils_1 = require("../utils");
@@ -21,20 +22,35 @@ const package_installer_1 = require("./package-installer");
21
22
  * @returns true if seeding occurred, false if package already existed.
22
23
  */
23
24
  async function seedPackageInContext(dataContext, opts) {
24
- const pkgTable = (0, packages_1.Packages)(dataContext);
25
- const existingPkg = await pkgTable.get(opts.packageId);
25
+ const existingPkg = await (0, packages_1.Packages)(dataContext).get(opts.packageId);
26
26
  if (existingPkg) {
27
27
  // Package already exists — check if it has any version installed
28
- const pvTable = (0, package_versions_1.PackageVersions)(dataContext);
29
- const existingPvs = await pvTable.list({ packageId: opts.packageId });
28
+ const existingPvs = await (0, package_versions_1.PackageVersions)(dataContext).list({ packageId: opts.packageId });
30
29
  if (existingPvs.length > 0) {
31
30
  return false;
32
31
  }
33
- // Package record exists but no versions — install the initial PV
34
- applyMissingSeedDefaults(existingPkg, opts);
35
- return await installSeedVersion(dataContext, existingPkg, opts);
36
32
  }
37
- // Create Package record with provided defaults
33
+ // Create the Package record (or backfill missing defaults on an existing one),
34
+ // then install the initial PackageVersion from the bundle.
35
+ const pkg = await ensurePackageRecord(dataContext, opts);
36
+ return await installSeedVersion(dataContext, pkg, opts);
37
+ }
38
+ /**
39
+ * Ensure a {@link IPackage} record exists in `dataContext`, creating it from
40
+ * `opts` when absent and backfilling any missing metadata defaults when
41
+ * present. Does not create or modify any PackageVersion.
42
+ *
43
+ * @returns The current (created or existing) Package record.
44
+ */
45
+ async function ensurePackageRecord(dataContext, opts) {
46
+ const pkgTable = (0, packages_1.Packages)(dataContext);
47
+ const existing = await pkgTable.get(opts.packageId);
48
+ if (existing) {
49
+ if (applyMissingRecordDefaults(existing, opts)) {
50
+ return await pkgTable.signAndSave(existing, { saveAsSnapshot: true });
51
+ }
52
+ return existing;
53
+ }
38
54
  const pkg = {
39
55
  packageId: opts.packageId,
40
56
  name: opts.name,
@@ -48,17 +64,24 @@ async function seedPackageInContext(dataContext, opts) {
48
64
  remoteRepo: opts.remoteRepo,
49
65
  signature: "",
50
66
  };
51
- await pkgTable.signAndSave(pkg, { saveAsSnapshot: true });
52
- await installSeedVersion(dataContext, pkg, opts);
53
- return true;
67
+ return pkgTable.signAndSave(pkg, { saveAsSnapshot: true });
54
68
  }
55
- function applyMissingSeedDefaults(pkg, opts) {
56
- pkg.description ||= opts.description ?? "";
57
- pkg.publishPublicKey ||= opts.publishPublicKey ?? "";
58
- pkg.versionFollowRange ||= opts.versionFollowRange;
59
- pkg.followVersionTags ||= opts.followVersionTags;
60
- pkg.updateUrl ||= opts.updateUrl;
61
- pkg.remoteRepo ||= opts.remoteRepo;
69
+ /** Backfill missing metadata onto an existing record. Returns true if changed. */
70
+ function applyMissingRecordDefaults(pkg, opts) {
71
+ let changed = false;
72
+ const setIfEmpty = (key, value) => {
73
+ if (!pkg[key] && value) {
74
+ pkg[key] = value;
75
+ changed = true;
76
+ }
77
+ };
78
+ setIfEmpty("description", opts.description);
79
+ setIfEmpty("publishPublicKey", opts.publishPublicKey);
80
+ setIfEmpty("versionFollowRange", opts.versionFollowRange);
81
+ setIfEmpty("followVersionTags", opts.followVersionTags);
82
+ setIfEmpty("updateUrl", opts.updateUrl);
83
+ setIfEmpty("remoteRepo", opts.remoteRepo);
84
+ return changed;
62
85
  }
63
86
  async function installSeedVersion(dataContext, pkg, opts) {
64
87
  // Save bundle files (deduped by hash)
@@ -0,0 +1,49 @@
1
+ /**
2
+ * peers-core install orchestrator.
3
+ *
4
+ * Ensures the `peers-core` package is installed into a given data context using
5
+ * a layered strategy:
6
+ *
7
+ * 1. No-op if peers-core already has a version in the context.
8
+ * 2. Copy from another context that already has it (used for new groups).
9
+ * 3. Install from the remote S3 `updateUrl` (used for new users; group fallback).
10
+ * 4. Seed the bundled copy shipped with the host app (offline fallback).
11
+ *
12
+ * Callers shape the strategy by what they pass: new users provide no
13
+ * `copyFromContexts` (remote-first), while new groups pass sibling contexts so
14
+ * they copy first and fall back to the remote `updateUrl`.
15
+ */
16
+ import type { DataContext } from "../context/data-context";
17
+ import type { UserContext } from "../context/user-context";
18
+ import type { IHttpDeps } from "./types";
19
+ /** Bundled peers-core sources shipped with the host app (offline fallback). */
20
+ export interface IPeersCoreBundle {
21
+ version: string;
22
+ versionTag: string;
23
+ packageBundleCode: string;
24
+ routesBundleCode?: string;
25
+ uiBundleCode?: string;
26
+ }
27
+ /** Options controlling {@link ensurePeersCore}'s install strategy. */
28
+ export interface IEnsurePeersCoreOpts {
29
+ /** HTTP interface for remote install. Omit to disable the remote path. */
30
+ http?: IHttpDeps;
31
+ /** S3 `updateUrl` for peers-core. Required for the remote path. */
32
+ updateUrl?: string;
33
+ /** Contexts to copy an already-installed peers-core from (e.g. personal + other groups). */
34
+ copyFromContexts?: DataContext[];
35
+ /** Bundled sources used as the offline fallback. */
36
+ bundle?: IPeersCoreBundle;
37
+ /** When true, try the bundled seed before the remote install (dev electron). */
38
+ preferBundle?: boolean;
39
+ /** Version tag to follow for the remote install. Defaults to `"stable"`. */
40
+ followTag?: string;
41
+ }
42
+ /**
43
+ * Ensure peers-core is installed (with a usable, loaded version) in
44
+ * `dataContext`, applying the layered strategy described in the module docs.
45
+ *
46
+ * @returns true if an install occurred, false if it was already present or no
47
+ * strategy succeeded.
48
+ */
49
+ export declare function ensurePeersCore(userContext: UserContext, dataContext: DataContext, opts: IEnsurePeersCoreOpts): Promise<boolean>;
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ /**
3
+ * peers-core install orchestrator.
4
+ *
5
+ * Ensures the `peers-core` package is installed into a given data context using
6
+ * a layered strategy:
7
+ *
8
+ * 1. No-op if peers-core already has a version in the context.
9
+ * 2. Copy from another context that already has it (used for new groups).
10
+ * 3. Install from the remote S3 `updateUrl` (used for new users; group fallback).
11
+ * 4. Seed the bundled copy shipped with the host app (offline fallback).
12
+ *
13
+ * Callers shape the strategy by what they pass: new users provide no
14
+ * `copyFromContexts` (remote-first), while new groups pass sibling contexts so
15
+ * they copy first and fall back to the remote `updateUrl`.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.ensurePeersCore = ensurePeersCore;
19
+ const package_version_resolver_1 = require("../data/package-version-resolver");
20
+ const package_versions_1 = require("../data/package-versions");
21
+ const packages_1 = require("../data/packages");
22
+ const packages_utils_1 = require("../data/packages.utils");
23
+ const system_ids_1 = require("../system-ids");
24
+ const package_remote_checker_1 = require("./package-remote-checker");
25
+ const package_seed_installer_1 = require("./package-seed-installer");
26
+ const PEERS_CORE_NAME = "peers-core";
27
+ const PEERS_CORE_DESCRIPTION = "Core system package for Peers platform";
28
+ /**
29
+ * Ensure peers-core is installed (with a usable, loaded version) in
30
+ * `dataContext`, applying the layered strategy described in the module docs.
31
+ *
32
+ * @returns true if an install occurred, false if it was already present or no
33
+ * strategy succeeded.
34
+ */
35
+ async function ensurePeersCore(userContext, dataContext, opts) {
36
+ const existingPvs = await (0, package_versions_1.PackageVersions)(dataContext).list({ packageId: system_ids_1.peersCorePackageId });
37
+ if (existingPvs.length > 0)
38
+ return false;
39
+ if (await tryCopyFromContexts(dataContext, opts.copyFromContexts)) {
40
+ return true;
41
+ }
42
+ const tryRemote = () => tryRemoteInstall(userContext, dataContext, opts);
43
+ const tryBundle = () => tryBundleSeed(userContext, dataContext, opts);
44
+ const strategies = opts.preferBundle ? [tryBundle, tryRemote] : [tryRemote, tryBundle];
45
+ for (const strategy of strategies) {
46
+ if (await strategy())
47
+ return true;
48
+ }
49
+ return false;
50
+ }
51
+ /** Copy peers-core from the first sibling context that already has it, then load it. */
52
+ async function tryCopyFromContexts(dataContext, copyFromContexts) {
53
+ if (!copyFromContexts?.length)
54
+ return false;
55
+ for (const source of copyFromContexts) {
56
+ if (source.dataContextId === dataContext.dataContextId)
57
+ continue;
58
+ try {
59
+ const sourcePkg = await (0, packages_1.Packages)(source).get(system_ids_1.peersCorePackageId);
60
+ if (!sourcePkg?.activePackageVersionId)
61
+ continue;
62
+ await (0, packages_utils_1.copyPackageToAnotherDataContext)(system_ids_1.peersCorePackageId, source, dataContext);
63
+ const copied = await (0, packages_1.Packages)(dataContext).get(system_ids_1.peersCorePackageId);
64
+ if (copied) {
65
+ await (0, package_version_resolver_1.resolveDevicePackageVersion)(copied, dataContext, { force: true });
66
+ }
67
+ return true;
68
+ }
69
+ catch (err) {
70
+ console.warn(`[peers-core-installer] copy from ${source.dataContextId} failed:`, err);
71
+ }
72
+ }
73
+ return false;
74
+ }
75
+ /** Create the Package record (if needed) and install the latest remote version. */
76
+ async function tryRemoteInstall(userContext, dataContext, opts) {
77
+ if (!opts.http || !opts.updateUrl)
78
+ return false;
79
+ try {
80
+ const pkg = await (0, package_seed_installer_1.ensurePackageRecord)(dataContext, {
81
+ packageId: system_ids_1.peersCorePackageId,
82
+ name: PEERS_CORE_NAME,
83
+ description: PEERS_CORE_DESCRIPTION,
84
+ createdBy: userContext.userId,
85
+ publishPublicKey: system_ids_1.peersCorePublishPublicKey,
86
+ versionFollowRange: "latest",
87
+ followVersionTags: "stable",
88
+ updateUrl: opts.updateUrl,
89
+ remoteRepo: system_ids_1.peersCorePackageRepoUrl,
90
+ });
91
+ const result = await (0, package_remote_checker_1.checkAndInstallPackageRemoteVersion)(dataContext, pkg, opts.followTag ?? "stable", opts.http);
92
+ return !!result;
93
+ }
94
+ catch (err) {
95
+ console.warn("[peers-core-installer] remote install failed:", err);
96
+ return false;
97
+ }
98
+ }
99
+ /** Seed the bundled peers-core sources as an offline fallback. */
100
+ async function tryBundleSeed(userContext, dataContext, opts) {
101
+ if (!opts.bundle)
102
+ return false;
103
+ try {
104
+ return await (0, package_seed_installer_1.seedPackageInContext)(dataContext, {
105
+ packageId: system_ids_1.peersCorePackageId,
106
+ name: PEERS_CORE_NAME,
107
+ description: PEERS_CORE_DESCRIPTION,
108
+ createdBy: userContext.userId,
109
+ publishPublicKey: system_ids_1.peersCorePublishPublicKey,
110
+ versionFollowRange: "latest",
111
+ followVersionTags: "stable",
112
+ updateUrl: opts.updateUrl,
113
+ remoteRepo: system_ids_1.peersCorePackageRepoUrl,
114
+ version: opts.bundle.version,
115
+ versionTag: opts.bundle.versionTag,
116
+ packageBundleCode: opts.bundle.packageBundleCode,
117
+ routesBundleCode: opts.bundle.routesBundleCode,
118
+ uiBundleCode: opts.bundle.uiBundleCode,
119
+ loadAfterSeed: true,
120
+ });
121
+ }
122
+ catch (err) {
123
+ console.warn("[peers-core-installer] bundle seed failed:", err);
124
+ return false;
125
+ }
126
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-sdk",
3
- "version": "0.19.10",
3
+ "version": "0.19.12",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/peers-app/peers-sdk.git"