@relayfile/sdk 0.3.0 → 0.3.2

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.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { type AdminIngressStatusResponse, type AdminSyncStatusResponse, type BulkWriteInput, type BulkWriteResponse, type BackendStatusResponse, type AckResponse, type DeleteFileInput, type DeadLetterItem, type DeadLetterFeedResponse, type EventFeedResponse, type ExportJsonResponse, type ExportOptions, type FileQueryResponse, type FileReadResponse, type FilesystemEvent, type GetEventsOptions, type GetAdminIngressStatusOptions, type GetAdminSyncStatusOptions, type GetOperationsOptions, type GetSyncDeadLettersOptions, type GetSyncIngressStatusOptions, type GetSyncStatusOptions, type ListTreeOptions, type OperationFeedResponse, type OperationStatusResponse, type QueuedResponse, type QueryFilesOptions, type SyncIngressStatusResponse, type SyncStatusResponse, type TreeResponse, type WriteFileInput, type WriteQueuedResponse, type IngestWebhookInput, type WritebackItem, type AckWritebackInput, type AckWritebackResponse } from "./types.js";
1
+ import { type AdminIngressStatusResponse, type AdminSyncStatusResponse, type BulkWriteInput, type BulkWriteResponse, type BackendStatusResponse, type AckResponse, type CommitForkInput, type CommitForkResponse, type CreateForkInput, type DeleteFileInput, type DeadLetterItem, type DeadLetterFeedResponse, type DiscardForkInput, type EventFeedResponse, type ExportJsonResponse, type ExportOptions, type FileQueryResponse, type FileReadResponse, type FilesystemEvent, type GetEventsOptions, type GetAdminIngressStatusOptions, type GetAdminSyncStatusOptions, type GetOperationsOptions, type GetSyncDeadLettersOptions, type GetSyncIngressStatusOptions, type GetSyncStatusOptions, type ListTreeOptions, type OperationFeedResponse, type OperationStatusResponse, type QueuedResponse, type ReadFileInput, type QueryFilesOptions, type SyncIngressStatusResponse, type SyncStatusResponse, type TreeResponse, type WriteFileInput, type WriteQueuedResponse, type IngestWebhookInput, type WritebackItem, type AckWritebackInput, type AckWritebackResponse } from "./types.js";
2
+ import type { ForkHandle } from "@relayfile/core";
2
3
  /**
3
4
  * Bearer token or token factory used for Relayfile API requests.
4
5
  *
@@ -52,10 +53,14 @@ export declare class RelayFileClient {
52
53
  constructor(options: RelayFileClientOptions);
53
54
  listTree(workspaceId: string, options?: ListTreeOptions): Promise<TreeResponse>;
54
55
  readFile(workspaceId: string, path: string, correlationId?: string, signal?: AbortSignal): Promise<FileReadResponse>;
56
+ readFile(input: ReadFileInput): Promise<FileReadResponse>;
55
57
  queryFiles(workspaceId: string, options?: QueryFilesOptions): Promise<FileQueryResponse>;
56
58
  writeFile(input: WriteFileInput): Promise<WriteQueuedResponse>;
57
59
  bulkWrite(input: BulkWriteInput): Promise<BulkWriteResponse>;
58
60
  deleteFile(input: DeleteFileInput): Promise<WriteQueuedResponse>;
61
+ createFork(input: CreateForkInput): Promise<ForkHandle>;
62
+ discardFork(input: DiscardForkInput): Promise<void>;
63
+ commitFork(input: CommitForkInput): Promise<CommitForkResponse>;
59
64
  getEvents(workspaceId: string, options?: GetEventsOptions): Promise<EventFeedResponse>;
60
65
  exportWorkspace(options: ExportOptions): Promise<ExportJsonResponse | Blob>;
61
66
  connectWebSocket(workspaceId: string, options?: ConnectWebSocketOptions): WebSocketConnection;
package/dist/client.js CHANGED
@@ -80,6 +80,18 @@ function normalizeExportJsonResponse(payload) {
80
80
  files: Array.isArray(data.files) ? data.files : []
81
81
  };
82
82
  }
83
+ function normalizeErrorDetails(data, explicitDetails) {
84
+ if (explicitDetails && typeof explicitDetails === "object" && !Array.isArray(explicitDetails)) {
85
+ return explicitDetails;
86
+ }
87
+ const details = {};
88
+ for (const [key, value] of Object.entries(data)) {
89
+ if (key !== "code" && key !== "message" && key !== "correlationId") {
90
+ details[key] = value;
91
+ }
92
+ }
93
+ return Object.keys(details).length > 0 ? details : undefined;
94
+ }
83
95
  class RelayFileWebSocketConnection {
84
96
  socket;
85
97
  handlers = {
@@ -163,7 +175,8 @@ export class RelayFileClient {
163
175
  const query = buildQuery({
164
176
  path: options.path ?? "/",
165
177
  depth: options.depth,
166
- cursor: options.cursor
178
+ cursor: options.cursor,
179
+ forkId: options.forkId
167
180
  });
168
181
  return this.request({
169
182
  method: "GET",
@@ -172,13 +185,21 @@ export class RelayFileClient {
172
185
  signal: options.signal
173
186
  });
174
187
  }
175
- async readFile(workspaceId, path, correlationId, signal) {
176
- const query = buildQuery({ path });
188
+ async readFile(workspaceOrInput, path, correlationId, signal) {
189
+ const input = typeof workspaceOrInput === "string"
190
+ ? {
191
+ workspaceId: workspaceOrInput,
192
+ path: path ?? "",
193
+ correlationId,
194
+ signal
195
+ }
196
+ : workspaceOrInput;
197
+ const query = buildQuery({ path: input.path, forkId: input.forkId });
177
198
  return this.request({
178
199
  method: "GET",
179
- path: `/v1/workspaces/${encodeURIComponent(workspaceId)}/fs/file${query}`,
180
- correlationId,
181
- signal
200
+ path: `/v1/workspaces/${encodeURIComponent(input.workspaceId)}/fs/file${query}`,
201
+ correlationId: input.correlationId,
202
+ signal: input.signal
182
203
  });
183
204
  }
184
205
  async queryFiles(workspaceId, options = {}) {
@@ -197,6 +218,8 @@ export class RelayFileClient {
197
218
  params.set("cursor", options.cursor);
198
219
  if (options.limit !== undefined)
199
220
  params.set("limit", String(options.limit));
221
+ if (options.forkId !== undefined)
222
+ params.set("forkId", options.forkId);
200
223
  if (options.properties !== undefined) {
201
224
  for (const [key, value] of Object.entries(options.properties)) {
202
225
  if (key !== "" && value !== undefined) {
@@ -214,8 +237,8 @@ export class RelayFileClient {
214
237
  });
215
238
  }
216
239
  async writeFile(input) {
217
- const { workspaceId, path, correlationId, baseRevision, content, contentType, encoding, semantics, signal } = input;
218
- const query = buildQuery({ path });
240
+ const { workspaceId, path, correlationId, baseRevision, content, contentType, encoding, contentIdentity, signal } = input;
241
+ const query = buildQuery({ path, forkId: input.forkId });
219
242
  return this.request({
220
243
  method: "PUT",
221
244
  path: `/v1/workspaces/${encodeURIComponent(workspaceId)}/fs/file${query}`,
@@ -228,15 +251,17 @@ export class RelayFileClient {
228
251
  contentType: contentType ?? "text/markdown",
229
252
  content,
230
253
  encoding,
231
- semantics: input.semantics
254
+ semantics: input.semantics,
255
+ ...(contentIdentity ? { contentIdentity } : {})
232
256
  },
233
257
  signal
234
258
  });
235
259
  }
236
260
  async bulkWrite(input) {
261
+ const query = buildQuery({ forkId: input.forkId });
237
262
  const response = await this.performRequest({
238
263
  method: "POST",
239
- path: `/v1/workspaces/${encodeURIComponent(input.workspaceId)}/fs/bulk`,
264
+ path: `/v1/workspaces/${encodeURIComponent(input.workspaceId)}/fs/bulk${query}`,
240
265
  correlationId: input.correlationId,
241
266
  body: {
242
267
  files: input.files
@@ -246,7 +271,7 @@ export class RelayFileClient {
246
271
  return this.readPayload(response);
247
272
  }
248
273
  async deleteFile(input) {
249
- const query = buildQuery({ path: input.path });
274
+ const query = buildQuery({ path: input.path, forkId: input.forkId });
250
275
  return this.request({
251
276
  method: "DELETE",
252
277
  path: `/v1/workspaces/${encodeURIComponent(input.workspaceId)}/fs/file${query}`,
@@ -257,6 +282,37 @@ export class RelayFileClient {
257
282
  signal: input.signal
258
283
  });
259
284
  }
285
+ async createFork(input) {
286
+ const body = {
287
+ proposalId: input.proposalId
288
+ };
289
+ if (input.ttlSeconds !== undefined) {
290
+ body.ttlSeconds = input.ttlSeconds;
291
+ }
292
+ return this.request({
293
+ method: "POST",
294
+ path: `/v1/workspaces/${encodeURIComponent(input.workspaceId)}/forks`,
295
+ correlationId: input.correlationId,
296
+ body,
297
+ signal: input.signal
298
+ });
299
+ }
300
+ async discardFork(input) {
301
+ await this.performRequest({
302
+ method: "DELETE",
303
+ path: `/v1/workspaces/${encodeURIComponent(input.workspaceId)}/forks/${encodeURIComponent(input.forkId)}`,
304
+ correlationId: input.correlationId,
305
+ signal: input.signal
306
+ });
307
+ }
308
+ async commitFork(input) {
309
+ return this.request({
310
+ method: "POST",
311
+ path: `/v1/workspaces/${encodeURIComponent(input.workspaceId)}/forks/${encodeURIComponent(input.forkId)}/commit`,
312
+ correlationId: input.correlationId,
313
+ signal: input.signal
314
+ });
315
+ }
260
316
  async getEvents(workspaceId, options = {}) {
261
317
  const query = buildQuery({
262
318
  provider: options.provider,
@@ -648,7 +704,9 @@ export class RelayFileClient {
648
704
  };
649
705
  }
650
706
  throwForError(status, payload, headers) {
651
- const data = (payload ?? {});
707
+ const rawData = (payload ?? {});
708
+ const data = rawData;
709
+ const details = normalizeErrorDetails(rawData, data.details);
652
710
  if (status === 409 &&
653
711
  typeof data.expectedRevision === "string" &&
654
712
  typeof data.currentRevision === "string") {
@@ -656,7 +714,7 @@ export class RelayFileClient {
656
714
  code: data.code ?? "revision_conflict",
657
715
  message: data.message ?? "Revision conflict",
658
716
  correlationId: data.correlationId ?? "",
659
- details: data.details,
717
+ details,
660
718
  expectedRevision: data.expectedRevision,
661
719
  currentRevision: data.currentRevision,
662
720
  currentContentPreview: data.currentContentPreview
@@ -667,7 +725,7 @@ export class RelayFileClient {
667
725
  code: data.code,
668
726
  message: data.message ?? "Invalid resource state",
669
727
  correlationId: data.correlationId,
670
- details: data.details
728
+ details
671
729
  });
672
730
  }
673
731
  if (status === 429 && data.code === "queue_full") {
@@ -683,7 +741,7 @@ export class RelayFileClient {
683
741
  code: data.code,
684
742
  message: data.message ?? "Ingress queue full",
685
743
  correlationId: data.correlationId,
686
- details: data.details
744
+ details
687
745
  }, retryAfterSeconds);
688
746
  }
689
747
  if (status === 413) {
@@ -691,14 +749,14 @@ export class RelayFileClient {
691
749
  code: data.code ?? "payload_too_large",
692
750
  message: data.message ?? "Request payload exceeds configured limit",
693
751
  correlationId: data.correlationId,
694
- details: data.details
752
+ details
695
753
  });
696
754
  }
697
755
  throw new RelayFileApiError(status, {
698
756
  code: data.code ?? "api_error",
699
757
  message: data.message ?? `HTTP ${status}`,
700
758
  correlationId: data.correlationId,
701
- details: data.details
759
+ details
702
760
  });
703
761
  }
704
762
  }
package/dist/index.d.ts CHANGED
@@ -4,7 +4,8 @@ export { InvalidStateError, PayloadTooLargeError, QueueFullError, RelayFileApiEr
4
4
  export { IntegrationProvider, computeCanonicalPath } from "./provider.js";
5
5
  export type { WebhookInput, ListProviderFilesOptions, WatchProviderEventsOptions } from "./provider.js";
6
6
  export type { ConnectionProvider, NormalizedWebhook, ProxyHeaders, ProxyMethod, ProxyQuery, ProxyRequest, ProxyResponse, } from "./connection.js";
7
- export type { AckResponse, AckWritebackInput, AckWritebackResponse, AdminIngressAlert, AdminIngressAlertProfile, AdminIngressEffectiveAlertProfile, AdminIngressAlertSeverity, AdminIngressAlertThresholds, AdminIngressAlertTotals, AdminIngressAlertType, AdminIngressStatusResponse, AdminSyncAlert, AdminSyncAlertSeverity, AdminSyncAlertThresholds, AdminSyncAlertTotals, AdminSyncAlertType, AdminSyncStatusResponse, BackendStatusResponse, BulkWriteFile, BulkWriteInput, BulkWriteResponse, ConflictErrorResponse, DeleteFileInput, DeadLetterFeedResponse, DeadLetterItem, ErrorResponse, EventFeedResponse, ExportFormat, ExportJsonResponse, ExportOptions, FileQueryItem, FileQueryResponse, FileReadResponse, FileSemantics, FileWriteRequest, FilesystemEvent, FilesystemEventType, EventOrigin, GetEventsOptions, GetAdminSyncStatusOptions, GetAdminIngressStatusOptions, GetOperationsOptions, GetSyncDeadLettersOptions, GetSyncIngressStatusOptions, GetSyncStatusOptions, IngestWebhookInput, ListTreeOptions, OperationFeedResponse, OperationStatus, OperationStatusResponse, QueuedResponse, QueryFilesOptions, RelayFileJwtClaims, SyncIngressStatusResponse, SyncProviderStatus, SyncProviderStatusState, SyncRefreshRequest, SyncStatusResponse, TreeEntry, TreeResponse, WritebackActionType, WritebackState, WritebackItem, WriteFileInput, WriteQueuedResponse } from "./types.js";
7
+ export type { AckResponse, AckWritebackInput, AckWritebackResponse, AdminIngressAlert, AdminIngressAlertProfile, AdminIngressEffectiveAlertProfile, AdminIngressAlertSeverity, AdminIngressAlertThresholds, AdminIngressAlertTotals, AdminIngressAlertType, AdminIngressStatusResponse, AdminSyncAlert, AdminSyncAlertSeverity, AdminSyncAlertThresholds, AdminSyncAlertTotals, AdminSyncAlertType, AdminSyncStatusResponse, BackendStatusResponse, BulkWriteFile, BulkWriteInput, BulkWriteResponse, CommitForkInput, CommitForkResponse, ConflictErrorResponse, CreateForkInput, ContentIdentity, DeleteFileInput, DeadLetterFeedResponse, DeadLetterItem, DiscardForkInput, ErrorResponse, EventFeedResponse, ExportFormat, ExportJsonResponse, ExportOptions, FileQueryItem, FileQueryResponse, FileReadResponse, FileSemantics, FileWriteRequest, FilesystemEvent, FilesystemEventType, EventOrigin, GetEventsOptions, GetAdminSyncStatusOptions, GetAdminIngressStatusOptions, GetOperationsOptions, GetSyncDeadLettersOptions, GetSyncIngressStatusOptions, GetSyncStatusOptions, IngestWebhookInput, ListTreeOptions, OperationFeedResponse, OperationStatus, OperationStatusResponse, QueuedResponse, QueryFilesOptions, ReadFileInput, RelayFileJwtClaims, SyncIngressStatusResponse, SyncProviderStatus, SyncProviderStatusState, SyncRefreshRequest, SyncStatusResponse, TreeEntry, TreeResponse, WritebackActionType, WritebackState, WritebackItem, WriteFileInput, WriteQueuedResponse } from "./types.js";
8
+ export type { ForkHandle, ForkOptions } from "@relayfile/core";
8
9
  export { WritebackConsumer } from "./writeback-consumer.js";
9
10
  export type { WritebackHandler, WritebackConsumerOptions } from "./writeback-consumer.js";
10
11
  export * from "./integration-adapter.js";
package/dist/types.d.ts CHANGED
@@ -44,6 +44,17 @@ export interface FileSemantics {
44
44
  permissions?: string[];
45
45
  comments?: string[];
46
46
  }
47
+ /**
48
+ * Stable identity for an idempotent write, used by the server to coalesce
49
+ * duplicate deliveries (webhook retries, cross-product fan-in). Two writes
50
+ * with equal `(kind, key)` in the same workspace within the dedup window
51
+ * resolve to the same op. Callers typically derive `key` from the upstream
52
+ * event id, e.g. `github:push:<sha>`.
53
+ */
54
+ export interface ContentIdentity {
55
+ kind: string;
56
+ key: string;
57
+ }
47
58
  export interface FileReadResponse {
48
59
  path: string;
49
60
  revision: string;
@@ -60,16 +71,19 @@ export interface FileWriteRequest {
60
71
  content: string;
61
72
  encoding?: "utf-8" | "base64";
62
73
  semantics?: FileSemantics;
74
+ contentIdentity?: ContentIdentity;
63
75
  }
64
76
  export interface BulkWriteFile {
65
77
  path: string;
66
78
  contentType?: string;
67
79
  content: string;
68
80
  encoding?: "utf-8" | "base64";
81
+ contentIdentity?: ContentIdentity;
69
82
  }
70
83
  export interface BulkWriteInput {
71
84
  workspaceId: string;
72
85
  files: BulkWriteFile[];
86
+ forkId?: string;
73
87
  correlationId?: string;
74
88
  signal?: AbortSignal;
75
89
  }
@@ -311,6 +325,7 @@ export interface ListTreeOptions {
311
325
  path?: string;
312
326
  depth?: number;
313
327
  cursor?: string;
328
+ forkId?: string;
314
329
  correlationId?: string;
315
330
  signal?: AbortSignal;
316
331
  }
@@ -323,6 +338,14 @@ export interface QueryFilesOptions {
323
338
  properties?: Record<string, string>;
324
339
  cursor?: string;
325
340
  limit?: number;
341
+ forkId?: string;
342
+ correlationId?: string;
343
+ signal?: AbortSignal;
344
+ }
345
+ export interface ReadFileInput {
346
+ workspaceId: string;
347
+ path: string;
348
+ forkId?: string;
326
349
  correlationId?: string;
327
350
  signal?: AbortSignal;
328
351
  }
@@ -435,6 +458,8 @@ export interface WriteFileInput {
435
458
  contentType?: string;
436
459
  encoding?: "utf-8" | "base64";
437
460
  semantics?: FileSemantics;
461
+ forkId?: string;
462
+ contentIdentity?: ContentIdentity;
438
463
  correlationId?: string;
439
464
  signal?: AbortSignal;
440
465
  }
@@ -442,9 +467,34 @@ export interface DeleteFileInput {
442
467
  workspaceId: string;
443
468
  path: string;
444
469
  baseRevision: string;
470
+ forkId?: string;
471
+ correlationId?: string;
472
+ signal?: AbortSignal;
473
+ }
474
+ export interface CreateForkInput {
475
+ workspaceId: string;
476
+ proposalId: string;
477
+ ttlSeconds?: number;
478
+ correlationId?: string;
479
+ signal?: AbortSignal;
480
+ }
481
+ export interface DiscardForkInput {
482
+ workspaceId: string;
483
+ forkId: string;
445
484
  correlationId?: string;
446
485
  signal?: AbortSignal;
447
486
  }
487
+ export interface CommitForkInput {
488
+ workspaceId: string;
489
+ forkId: string;
490
+ correlationId?: string;
491
+ signal?: AbortSignal;
492
+ }
493
+ export interface CommitForkResponse {
494
+ revision: string;
495
+ writtenCount: number;
496
+ deletedCount: number;
497
+ }
448
498
  export interface IngestWebhookInput {
449
499
  workspaceId: string;
450
500
  provider: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relayfile/sdk",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
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",
@@ -14,6 +14,9 @@
14
14
  "test": "vitest run",
15
15
  "prepublishOnly": "npm run build"
16
16
  },
17
+ "dependencies": {
18
+ "@relayfile/core": "0.3.2"
19
+ },
17
20
  "devDependencies": {
18
21
  "typescript": "^5.7.3",
19
22
  "vitest": "^3.0.0"