@relayfile/sdk 0.9.3 → 0.9.5
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 +61 -13
- package/dist/sync.d.ts +21 -1
- package/dist/sync.js +51 -16
- package/package.json +6 -6
package/dist/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RelayFileSync } from "./sync.js";
|
|
1
|
+
import { RelayFileSync, normalizeFilesystemEvent } from "./sync.js";
|
|
2
2
|
import { InvalidStateError, PayloadTooLargeError, QueueFullError, RelayFileApiError, RevisionConflictError } from "./errors.js";
|
|
3
3
|
/** Default base URL for the hosted Relayfile API */
|
|
4
4
|
export const DEFAULT_RELAYFILE_BASE_URL = "https://api.relayfile.dev";
|
|
@@ -12,6 +12,7 @@ const DEFAULT_CHANGE_COALESCE_MS = 200;
|
|
|
12
12
|
const DEFAULT_CHANGE_LOG_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
13
13
|
const DEFAULT_CHANGE_LOG_MAX_ENTRIES = 10_000;
|
|
14
14
|
const CLIENT_TOKEN_STREAM_KEY = "__client__";
|
|
15
|
+
const STABLE_FALLBACK_CHANGE_TIMESTAMP = "1970-01-01T00:00:00.000Z";
|
|
15
16
|
const changeStreamManagers = new WeakMap();
|
|
16
17
|
const changeLogCaches = new WeakMap();
|
|
17
18
|
const changeLogSettings = new WeakMap();
|
|
@@ -161,7 +162,7 @@ class RelayFileWebSocketConnection {
|
|
|
161
162
|
if (raw.path !== undefined && typeof raw.path !== "string") {
|
|
162
163
|
throw new Error("Invalid WebSocket event: 'path' must be a string.");
|
|
163
164
|
}
|
|
164
|
-
parsed = raw;
|
|
165
|
+
parsed = normalizeFilesystemEvent(raw);
|
|
165
166
|
}
|
|
166
167
|
catch (error) {
|
|
167
168
|
const parseError = error instanceof Error ? error : new Error("Failed to parse WebSocket event payload.");
|
|
@@ -882,23 +883,66 @@ function toChangeEvent(client, record, contextOverride) {
|
|
|
882
883
|
}
|
|
883
884
|
};
|
|
884
885
|
}
|
|
885
|
-
function normalizeWireChangeEvent(payload) {
|
|
886
|
+
function normalizeWireChangeEvent(payload, workspaceId) {
|
|
886
887
|
const data = (payload ?? {});
|
|
887
888
|
const resource = (data.resource ?? {});
|
|
888
889
|
const summary = (data.summary ?? {});
|
|
890
|
+
const path = typeof resource.path === "string"
|
|
891
|
+
? resource.path
|
|
892
|
+
: typeof data.path === "string"
|
|
893
|
+
? data.path
|
|
894
|
+
: "";
|
|
895
|
+
const inferredResource = inferResourceMetadata(path, resource);
|
|
896
|
+
const occurredAt = typeof data.occurredAt === "string"
|
|
897
|
+
? data.occurredAt
|
|
898
|
+
: typeof data.timestamp === "string"
|
|
899
|
+
? data.timestamp
|
|
900
|
+
: typeof data.ts === "string"
|
|
901
|
+
? data.ts
|
|
902
|
+
: STABLE_FALLBACK_CHANGE_TIMESTAMP;
|
|
903
|
+
const eventId = typeof data.id === "string"
|
|
904
|
+
? data.id
|
|
905
|
+
: typeof data.eventId === "string"
|
|
906
|
+
? data.eventId
|
|
907
|
+
: [
|
|
908
|
+
"relayfile",
|
|
909
|
+
workspaceId ?? "",
|
|
910
|
+
typeof data.type === "string" ? data.type : "relayfile.changed",
|
|
911
|
+
path,
|
|
912
|
+
typeof data.revision === "string" ? data.revision : "",
|
|
913
|
+
occurredAt
|
|
914
|
+
].join(":");
|
|
915
|
+
const provider = typeof resource.provider === "string"
|
|
916
|
+
? resource.provider
|
|
917
|
+
: typeof data.provider === "string"
|
|
918
|
+
? data.provider
|
|
919
|
+
: inferredResource.provider;
|
|
889
920
|
return {
|
|
890
|
-
id:
|
|
891
|
-
workspace: typeof data.workspace === "string" ? data.workspace : "",
|
|
921
|
+
id: eventId,
|
|
922
|
+
workspace: typeof data.workspace === "string" ? data.workspace : workspaceId ?? "",
|
|
892
923
|
agentId: typeof data.agentId === "string" ? data.agentId : undefined,
|
|
893
924
|
type: "relayfile.changed",
|
|
894
|
-
occurredAt
|
|
925
|
+
occurredAt,
|
|
895
926
|
resource: {
|
|
896
|
-
path
|
|
897
|
-
kind: typeof resource.kind === "string"
|
|
898
|
-
|
|
899
|
-
|
|
927
|
+
path,
|
|
928
|
+
kind: typeof resource.kind === "string"
|
|
929
|
+
? resource.kind
|
|
930
|
+
: typeof data.kind === "string"
|
|
931
|
+
? data.kind
|
|
932
|
+
: typeof data.resourceType === "string"
|
|
933
|
+
? data.resourceType
|
|
934
|
+
: inferredResource.kind,
|
|
935
|
+
id: typeof resource.id === "string"
|
|
936
|
+
? resource.id
|
|
937
|
+
: typeof data.resourceId === "string"
|
|
938
|
+
? data.resourceId
|
|
939
|
+
: typeof data.providerObjectId === "string"
|
|
940
|
+
? data.providerObjectId
|
|
941
|
+
: inferredResource.id,
|
|
942
|
+
provider
|
|
900
943
|
},
|
|
901
944
|
summary: {
|
|
945
|
+
...buildChangeSummary(path, data),
|
|
902
946
|
...(typeof summary.title === "string" ? { title: summary.title } : {}),
|
|
903
947
|
...(typeof summary.status === "string" ? { status: summary.status } : {}),
|
|
904
948
|
...(typeof summary.priority === "string" ? { priority: summary.priority } : {}),
|
|
@@ -1167,12 +1211,16 @@ export class RelayFileClient {
|
|
|
1167
1211
|
cursor: options.cursor,
|
|
1168
1212
|
limit: options.limit
|
|
1169
1213
|
});
|
|
1170
|
-
|
|
1214
|
+
const response = await this.request({
|
|
1171
1215
|
method: "GET",
|
|
1172
1216
|
path: `/v1/workspaces/${encodeURIComponent(workspaceId)}/fs/events${query}`,
|
|
1173
1217
|
correlationId: options.correlationId,
|
|
1174
1218
|
signal: options.signal
|
|
1175
1219
|
});
|
|
1220
|
+
return {
|
|
1221
|
+
events: (response.events ?? []).map((event) => normalizeFilesystemEvent(event)),
|
|
1222
|
+
nextCursor: response.nextCursor ?? null
|
|
1223
|
+
};
|
|
1176
1224
|
}
|
|
1177
1225
|
subscribe(globs, onChange, options) {
|
|
1178
1226
|
const setup = this.resolveWorkspaceId(options?.aclToken)
|
|
@@ -1241,7 +1289,7 @@ export class RelayFileClient {
|
|
|
1241
1289
|
});
|
|
1242
1290
|
records = mergeChangeRecords([
|
|
1243
1291
|
...cached,
|
|
1244
|
-
...(payload.events ?? []).map((event) => this.cacheWireChangeEvent(workspaceId, normalizeWireChangeEvent(event), effectiveContext))
|
|
1292
|
+
...(payload.events ?? []).map((event) => this.cacheWireChangeEvent(workspaceId, normalizeWireChangeEvent(event, workspaceId), effectiveContext))
|
|
1245
1293
|
]);
|
|
1246
1294
|
}
|
|
1247
1295
|
catch (error) {
|
|
@@ -1271,7 +1319,7 @@ export class RelayFileClient {
|
|
|
1271
1319
|
});
|
|
1272
1320
|
records = mergeChangeRecords([
|
|
1273
1321
|
...cached,
|
|
1274
|
-
...(payload.events ?? []).map((event) => this.cacheWireChangeEvent(workspaceId, normalizeWireChangeEvent(event), effectiveContext))
|
|
1322
|
+
...(payload.events ?? []).map((event) => this.cacheWireChangeEvent(workspaceId, normalizeWireChangeEvent(event, workspaceId), effectiveContext))
|
|
1275
1323
|
]).slice(-safeLimit);
|
|
1276
1324
|
}
|
|
1277
1325
|
catch (error) {
|
package/dist/sync.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RelayFileClient } from "./client.js";
|
|
2
|
-
import type { FilesystemEvent } from "./types.js";
|
|
2
|
+
import type { EventOrigin, FilesystemEvent } from "./types.js";
|
|
3
3
|
/**
|
|
4
4
|
* WebSocket auth source for {@link RelayFileSyncOptions.token}.
|
|
5
5
|
*
|
|
@@ -73,6 +73,26 @@ type RelayFileSyncHandlerMap = {
|
|
|
73
73
|
close: (event: CloseEvent) => void;
|
|
74
74
|
pong: (event: RelayFileSyncPong) => void;
|
|
75
75
|
};
|
|
76
|
+
interface RelayFileSyncWireEvent {
|
|
77
|
+
type: string;
|
|
78
|
+
id?: string;
|
|
79
|
+
eventId?: string;
|
|
80
|
+
path?: string;
|
|
81
|
+
revision?: string;
|
|
82
|
+
digest?: string;
|
|
83
|
+
contentHash?: string;
|
|
84
|
+
origin?: EventOrigin;
|
|
85
|
+
provider?: string;
|
|
86
|
+
correlationId?: string;
|
|
87
|
+
timestamp?: string;
|
|
88
|
+
ts?: string;
|
|
89
|
+
occurredAt?: string;
|
|
90
|
+
resource?: {
|
|
91
|
+
path?: unknown;
|
|
92
|
+
provider?: unknown;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export declare function normalizeFilesystemEvent(message?: RelayFileSyncWireEvent | null): FilesystemEvent;
|
|
76
96
|
export declare function normalizeError(event: unknown): unknown;
|
|
77
97
|
export declare class RelayFileSync {
|
|
78
98
|
private readonly client;
|
package/dist/sync.js
CHANGED
|
@@ -54,6 +54,7 @@ const DEFAULT_RECONNECT_MAX_DELAY_MS = 5000;
|
|
|
54
54
|
// for long-running consumers like the factory daemon.
|
|
55
55
|
const DEFAULT_WS_REPROBE_MIN_DELAY_MS = 5000;
|
|
56
56
|
const DEFAULT_WS_REPROBE_MAX_DELAY_MS = 300000;
|
|
57
|
+
const STABLE_FALLBACK_TIMESTAMP = "1970-01-01T00:00:00.000Z";
|
|
57
58
|
// Cap on the dedupe cache used in polling mode. Sized large enough that no
|
|
58
59
|
// realistic workspace burst can churn through it within one poll interval
|
|
59
60
|
// (the events API is itself capped at ~1000 per page), small enough to keep
|
|
@@ -122,25 +123,51 @@ function createAbortError() {
|
|
|
122
123
|
return err;
|
|
123
124
|
}
|
|
124
125
|
function buildEventId(message) {
|
|
126
|
+
const path = readWirePath(message);
|
|
127
|
+
const timestamp = readWireTimestamp(message);
|
|
128
|
+
const type = readWireString(message?.type) ?? "file.updated";
|
|
125
129
|
return [
|
|
126
130
|
"ws",
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
message
|
|
130
|
-
|
|
131
|
+
normalizeFilesystemEventType(type),
|
|
132
|
+
path,
|
|
133
|
+
readWireString(message?.revision) ?? "",
|
|
134
|
+
timestamp
|
|
131
135
|
].join(":");
|
|
132
136
|
}
|
|
133
|
-
function
|
|
137
|
+
function readWireString(value) {
|
|
138
|
+
return typeof value === "string" ? value : undefined;
|
|
139
|
+
}
|
|
140
|
+
function readWirePath(message) {
|
|
141
|
+
return readWireString(message?.path) ?? readWireString(message?.resource?.path) ?? "";
|
|
142
|
+
}
|
|
143
|
+
function readWireProvider(message) {
|
|
144
|
+
return readWireString(message?.provider) ?? readWireString(message?.resource?.provider);
|
|
145
|
+
}
|
|
146
|
+
function readWireTimestamp(message) {
|
|
147
|
+
return readWireString(message?.timestamp)
|
|
148
|
+
?? readWireString(message?.ts)
|
|
149
|
+
?? readWireString(message?.occurredAt)
|
|
150
|
+
?? STABLE_FALLBACK_TIMESTAMP;
|
|
151
|
+
}
|
|
152
|
+
function normalizeFilesystemEventType(type) {
|
|
153
|
+
return type === "relayfile.changed" || type === "relayfile.changed.summary"
|
|
154
|
+
? "file.updated"
|
|
155
|
+
: type;
|
|
156
|
+
}
|
|
157
|
+
export function normalizeFilesystemEvent(message) {
|
|
158
|
+
const path = readWirePath(message);
|
|
159
|
+
const type = readWireString(message?.type) ?? "file.updated";
|
|
160
|
+
const digest = readWireString(message?.digest);
|
|
134
161
|
return {
|
|
135
|
-
eventId: message
|
|
136
|
-
type:
|
|
137
|
-
path
|
|
138
|
-
revision: message
|
|
139
|
-
contentHash: message
|
|
140
|
-
origin: message
|
|
141
|
-
provider: message
|
|
142
|
-
correlationId: message
|
|
143
|
-
timestamp: message
|
|
162
|
+
eventId: readWireString(message?.eventId) ?? readWireString(message?.id) ?? buildEventId(message),
|
|
163
|
+
type: normalizeFilesystemEventType(type),
|
|
164
|
+
path,
|
|
165
|
+
revision: readWireString(message?.revision) ?? digest ?? "",
|
|
166
|
+
contentHash: readWireString(message?.contentHash) ?? digest,
|
|
167
|
+
origin: message?.origin,
|
|
168
|
+
provider: readWireProvider(message),
|
|
169
|
+
correlationId: readWireString(message?.correlationId),
|
|
170
|
+
timestamp: readWireTimestamp(message)
|
|
144
171
|
};
|
|
145
172
|
}
|
|
146
173
|
// Exported for testing. `ErrorEvent` is a DOM/browser global that is not
|
|
@@ -684,6 +711,14 @@ export class RelayFileSync {
|
|
|
684
711
|
this.emit("error", new Error("Invalid WebSocket payload: 'revision' must be a string."));
|
|
685
712
|
return;
|
|
686
713
|
}
|
|
714
|
+
if (parsed.resource !== undefined && (typeof parsed.resource !== "object" || parsed.resource === null)) {
|
|
715
|
+
this.emit("error", new Error("Invalid WebSocket payload: 'resource' must be an object."));
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if (parsed.resource?.path !== undefined && typeof parsed.resource.path !== "string") {
|
|
719
|
+
this.emit("error", new Error("Invalid WebSocket payload: 'resource.path' must be a string."));
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
687
722
|
if (parsed.timestamp !== undefined && typeof parsed.timestamp !== "string") {
|
|
688
723
|
this.emit("error", new Error("Invalid WebSocket payload: 'timestamp' must be a string."));
|
|
689
724
|
return;
|
|
@@ -701,8 +736,8 @@ export class RelayFileSync {
|
|
|
701
736
|
return;
|
|
702
737
|
}
|
|
703
738
|
const normalized = normalizeFilesystemEvent(parsed);
|
|
704
|
-
if (
|
|
705
|
-
this.cursor =
|
|
739
|
+
if (normalized.eventId) {
|
|
740
|
+
this.cursor = normalized.eventId;
|
|
706
741
|
}
|
|
707
742
|
debugLog("event", { type: normalized.type, path: normalized.path, revision: normalized.revision });
|
|
708
743
|
this.emitFilesystemEvent(normalized);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relayfile/sdk",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
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.9.
|
|
62
|
+
"@relayfile/core": "0.9.5",
|
|
63
63
|
"ignore": "^7.0.5",
|
|
64
64
|
"tar": "^7.5.10"
|
|
65
65
|
},
|
|
66
66
|
"optionalDependencies": {
|
|
67
|
-
"@relayfile/mount-darwin-arm64": "0.9.
|
|
68
|
-
"@relayfile/mount-darwin-x64": "0.9.
|
|
69
|
-
"@relayfile/mount-linux-arm64": "0.9.
|
|
70
|
-
"@relayfile/mount-linux-x64": "0.9.
|
|
67
|
+
"@relayfile/mount-darwin-arm64": "0.9.5",
|
|
68
|
+
"@relayfile/mount-darwin-x64": "0.9.5",
|
|
69
|
+
"@relayfile/mount-linux-arm64": "0.9.5",
|
|
70
|
+
"@relayfile/mount-linux-x64": "0.9.5"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {},
|
|
73
73
|
"devDependencies": {
|