@peers-app/peers-sdk 0.29.0 → 0.29.1

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.
@@ -8,7 +8,10 @@ export declare const PACKAGES_CONTRACT_VERSION = 1;
8
8
  export interface IPackagesHostCapabilities {
9
9
  /** The host can read package folders: import, create, clone, and reload from disk. */
10
10
  fileSystem: boolean;
11
- /** The host can create and link a remote repository (`linkRemoteRepo`). */
11
+ /**
12
+ * The host can create and link a remote repository (`linkRemoteRepo` without a URL). Recording
13
+ * a given URL needs no host tooling.
14
+ */
12
15
  linkRemote: boolean;
13
16
  /** The host can install the default packages into a context (`installDefaultPackages`). */
14
17
  installDefaults: boolean;
@@ -172,6 +175,14 @@ export declare function packageVersionPromotionTargets(versionTag: string | unde
172
175
  * @param target - Input as typed.
173
176
  */
174
177
  export declare function isPackageRepositoryUrl(target: string): boolean;
178
+ /**
179
+ * Whether a URL may be recorded as a package's source (`Packages.remoteRepo`) by
180
+ * `linkRemoteRepo`: an `https://` or `http://` URL, a `git@host:` remote, or a Git package
181
+ * remote, `peers://<groupId>/<repoId>`. No whitespace; at most 2,048 characters.
182
+ *
183
+ * @param url - The URL to record.
184
+ */
185
+ export declare function isPackageSourceUrl(url: string): boolean;
175
186
  /** Typed consumer proxy for System Packages v1. */
176
187
  export interface IPackagesConsumer extends IContractConsumerBase {
177
188
  tools: {
@@ -223,9 +234,14 @@ export interface IPackagesConsumer extends IContractConsumerBase {
223
234
  }): Promise<IPackageOperationResult>;
224
235
  /** Re-read every package folder on this device and reload packages whose bundles changed. */
225
236
  reloadAllPackages(): Promise<IReloadAllPackagesResult>;
226
- /** Create and link a remote repository for a local package. */
237
+ /**
238
+ * Record a package's source repository. With `remoteRepo` (see {@link isPackageSourceUrl}),
239
+ * record that URL; otherwise create a remote repository with the host's tooling and link it.
240
+ * A package that already has a source keeps it and the result carries a warning.
241
+ */
227
242
  linkRemoteRepo(input: {
228
243
  packageId: string;
244
+ remoteRepo?: string;
229
245
  }): Promise<IPackageOperationResult>;
230
246
  /** Remove a package from the group and unload it here. */
231
247
  uninstallPackage(input: {
@@ -4,6 +4,7 @@ exports.PACKAGES_CONTRACT_VERSION = void 0;
4
4
  exports.packageGroupVersionWarning = packageGroupVersionWarning;
5
5
  exports.packageVersionPromotionTargets = packageVersionPromotionTargets;
6
6
  exports.isPackageRepositoryUrl = isPackageRepositoryUrl;
7
+ exports.isPackageSourceUrl = isPackageSourceUrl;
7
8
  exports.getPackagesContractDefinition = getPackagesContractDefinition;
8
9
  const system_ids_1 = require("../../system-ids");
9
10
  const field_type_1 = require("../../types/field-type");
@@ -43,6 +44,29 @@ function packageVersionPromotionTargets(versionTag) {
43
44
  function isPackageRepositoryUrl(target) {
44
45
  return /^(https?:\/\/|git@[^:/\s]+:)/.test(target.trim());
45
46
  }
47
+ /**
48
+ * Whether a URL may be recorded as a package's source (`Packages.remoteRepo`) by
49
+ * `linkRemoteRepo`: an `https://` or `http://` URL, a `git@host:` remote, or a Git package
50
+ * remote, `peers://<groupId>/<repoId>`. No whitespace; at most 2,048 characters.
51
+ *
52
+ * @param url - The URL to record.
53
+ */
54
+ function isPackageSourceUrl(url) {
55
+ if (url.length > 2048 || /\s|\p{Cc}/u.test(url))
56
+ return false;
57
+ if (/^peers:\/\/[A-Za-z0-9]{25}\/[A-Za-z0-9]{25}$/.test(url))
58
+ return true;
59
+ if (/^git@[^:/\\]+:.+$/.test(url))
60
+ return true;
61
+ if (!/^https?:\/\//.test(url) || url.includes("\\"))
62
+ return false;
63
+ try {
64
+ return !!new URL(url).hostname;
65
+ }
66
+ catch {
67
+ return false;
68
+ }
69
+ }
46
70
  const field = (name, type, description, opts = {}) => ({ name, type, description, ...opts });
47
71
  const packageIdField = field("packageId", field_type_1.FieldType.id, "Package id.");
48
72
  const operationResultFields = [
@@ -162,8 +186,11 @@ function getPackagesContractDefinition() {
162
186
  },
163
187
  {
164
188
  name: "linkRemoteRepo",
165
- usageDescription: "Create a remote repository for a local package and link it.",
166
- inputFields: [packageIdField],
189
+ usageDescription: "Record a package's source repository: the given URL, or one created with the host's tooling. A package that already has a source keeps it.",
190
+ inputFields: [
191
+ packageIdField,
192
+ field("remoteRepo", field_type_1.FieldType.string, "Record this URL (https://, git@host:, or a Git package peers:// URL) instead of creating a repository.", { optional: true }),
193
+ ],
167
194
  outputFields: operationResultFields,
168
195
  },
169
196
  {
@@ -330,11 +330,9 @@ function createBinaryPeerSocket(connectionId, peer, protocol = "binary", receive
330
330
  }
331
331
  // ─── Drain / Backpressure ────────────────────────────────────────
332
332
  // Single persistent drain handler to avoid leaking listeners
333
- let drainResolve = null;
333
+ const drainWaiters = new Set();
334
334
  peer.on("drain", () => {
335
- if (drainResolve) {
336
- const resolve = drainResolve;
337
- drainResolve = null;
335
+ for (const resolve of drainWaiters) {
338
336
  resolve();
339
337
  }
340
338
  });
@@ -351,15 +349,18 @@ function createBinaryPeerSocket(connectionId, peer, protocol = "binary", receive
351
349
  // Backpressure: wait for buffer to drain before sending more
352
350
  while ((peer.getBufferedAmount?.() ?? 0) > config.highWaterMark) {
353
351
  await new Promise((resolve) => {
354
- drainResolve = resolve;
352
+ const resume = () => {
353
+ clearInterval(timer);
354
+ drainWaiters.delete(resume);
355
+ resolve();
356
+ };
355
357
  // Also poll as fallback (drain event might not fire on all transports)
356
358
  const timer = setInterval(() => {
357
359
  if ((peer.getBufferedAmount?.() ?? 0) <= config.lowWaterMark) {
358
- clearInterval(timer);
359
- drainResolve = null;
360
- resolve();
360
+ resume();
361
361
  }
362
362
  }, 5);
363
+ drainWaiters.add(resume);
363
364
  });
364
365
  }
365
366
  // A retired cipher must not fall back to a clear frame; the peer would close
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-sdk",
3
- "version": "0.29.0",
3
+ "version": "0.29.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/peers-app/peers-sdk.git"