@relayfile/sdk 0.10.52 → 0.10.53

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/client.js CHANGED
@@ -397,7 +397,13 @@ class RelayFileChangeSubscription {
397
397
  this.onChange = onChange;
398
398
  this.options = options;
399
399
  this.globPatterns = globs.map((pattern) => normalizeChangePattern(pattern));
400
- this.pathScopes = options?.pathScope?.length ? options.pathScope.map((pattern) => normalizeChangePattern(pattern)) : null;
400
+ // A wildcard-free pathScope entry (e.g. "/ramp/transactions") is a DIRECTORY
401
+ // SUBTREE scope: expand it into the exact path plus its "/**" subtree filter
402
+ // so it matches the directory and everything under it. A bare path is never
403
+ // exact-file; use a glob for that. See expandDirectoryScope for the rule.
404
+ this.pathScopes = options?.pathScope?.length
405
+ ? options.pathScope.flatMap((pattern) => expandDirectoryScope(normalizeChangePattern(pattern)))
406
+ : null;
401
407
  this.shouldCoalesce = (options?.coalesce ?? "fire-once") !== "none";
402
408
  this.coalesceMs = Math.max(0, Math.floor(options?.coalesceMs ?? DEFAULT_CHANGE_COALESCE_MS));
403
409
  }
@@ -747,6 +753,45 @@ function normalizeChangePattern(pattern) {
747
753
  }
748
754
  return segments;
749
755
  }
756
+ /**
757
+ * A wildcard-free `pathScope` entry is DEFINED as a DIRECTORY SUBTREE scope: it
758
+ * matches the path itself AND everything under it. To scope to an exact file,
759
+ * pass that exact path as a glob in the first `subscribe(globs, ...)` argument
760
+ * (the `globs` list is matched exactly unless an entry has a `*`/`**` wildcard);
761
+ * a bare `pathScope` path is never exact-file. This is a deliberate API-design
762
+ * choice (Option A), not an
763
+ * accident of the server matcher: it makes the common "watch this directory"
764
+ * intent Just Work and avoids a silent zero-event foot-gun.
765
+ *
766
+ * Why it MUST be subtree, and why "exact-file" is not a safe alternative:
767
+ * Relayfile creates descendants under file-looking paths (e.g.
768
+ * `/linear/issues/ENG-1.json/replies/draft.json`), so a bare path can never be
769
+ * assumed to name a leaf. Treating a wildcard-free entry as directory-scope is
770
+ * the only consistent rule.
771
+ *
772
+ * The data plane (see `webSocketPathMatches` in internal/httpapi/websocket.go)
773
+ * matches a `path=` filter against an event path with these semantics:
774
+ * - exact match: `path` == the event path, OR
775
+ * - trailing `**`: `/ramp/transactions/**` matches any STRICT descendant, OR
776
+ * - per-segment `*` wildcards, requiring equal segment counts otherwise.
777
+ * A bare directory prefix like `/ramp/transactions` (no wildcard) would
778
+ * therefore match ONLY an event whose path is exactly `/ramp/transactions` —
779
+ * ZERO children — so a caller scoping to `["/ramp/transactions"]` opens a WS
780
+ * that silently receives no events (proven: `path=/ramp/transactions` -> 0
781
+ * events; `/**` -> 6). The same count-must-match rule in `matchChangeSegments`
782
+ * makes the client-side filter agree, so the miss would be doubly silent.
783
+ *
784
+ * Implementation: for a wildcard-free entry, emit BOTH the exact path AND the
785
+ * `.../**` subtree filter — the union matches the directory node itself plus its
786
+ * entire subtree. Entries that already contain a `*`/`**` wildcard are honored
787
+ * verbatim — an intentionally precise glob is never broadened.
788
+ */
789
+ function expandDirectoryScope(segments) {
790
+ if (segments.some((segment) => segment === "*" || segment === "**")) {
791
+ return [segments];
792
+ }
793
+ return [segments, [...segments, "**"]];
794
+ }
750
795
  function normalizeChangePath(path) {
751
796
  const normalized = path.startsWith("/") ? path : `/${path}`;
752
797
  const trimmed = normalized.replace(/\/+$/, "");
package/dist/types.d.ts CHANGED
@@ -416,6 +416,22 @@ export interface LayoutManifest {
416
416
  export interface SubscribeOptions {
417
417
  coalesce?: "none" | "fire-once";
418
418
  coalesceMs?: number;
419
+ /**
420
+ * Narrow the server-side event stream to one or more path scopes.
421
+ *
422
+ * A **wildcard-free** entry is a DIRECTORY SUBTREE scope: `"/ramp/transactions"`
423
+ * matches that path AND everything under it. A bare path is NOT treated as an
424
+ * exact-file match — Relayfile creates descendants under file-looking paths
425
+ * (e.g. `/linear/issues/ENG-1.json/replies/draft.json`), so a wildcard-free
426
+ * scope always means "this directory and its subtree".
427
+ *
428
+ * To scope to something more precise, pass an explicit glob: a trailing `**`
429
+ * (`"/ramp/transactions/**"`) for a subtree, or per-segment `*` wildcards
430
+ * (`"/github/repos/acme/api/pulls/*"`); these are honored verbatim. For an
431
+ * exact single file, pass that exact path as a glob in the first `subscribe`
432
+ * argument (`subscribe(globs, ...)`, e.g. `["/linear/issues/ENG-1.json"]`) —
433
+ * the `globs` list is matched exactly unless it contains a `*`/`**` wildcard.
434
+ */
419
435
  pathScope?: string[];
420
436
  from?: "now" | "legacy";
421
437
  cursor?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relayfile/sdk",
3
- "version": "0.10.52",
3
+ "version": "0.10.53",
4
4
  "description": "TypeScript SDK for relayfile — real-time filesystem for humans and agents",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -59,15 +59,15 @@
59
59
  "prepublishOnly": "npm run build"
60
60
  },
61
61
  "dependencies": {
62
- "@relayfile/core": "0.10.52",
62
+ "@relayfile/core": "0.10.53",
63
63
  "ignore": "^7.0.5",
64
64
  "tar": "^7.5.10"
65
65
  },
66
66
  "optionalDependencies": {
67
- "@relayfile/mount-darwin-arm64": "0.10.52",
68
- "@relayfile/mount-darwin-x64": "0.10.52",
69
- "@relayfile/mount-linux-arm64": "0.10.52",
70
- "@relayfile/mount-linux-x64": "0.10.52"
67
+ "@relayfile/mount-darwin-arm64": "0.10.53",
68
+ "@relayfile/mount-darwin-x64": "0.10.53",
69
+ "@relayfile/mount-linux-arm64": "0.10.53",
70
+ "@relayfile/mount-linux-x64": "0.10.53"
71
71
  },
72
72
  "devDependencies": {
73
73
  "typescript": "^5.7.3",