@syncular/transport-http 0.0.6-185 → 0.0.6-201

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.
@@ -0,0 +1,189 @@
1
+ import { encodeSnapshotScopes, resolveRequestUrl, SNAPSHOT_SCOPES_HEADER, } from './shared.js';
2
+ export const HTTP_TRANSPORT_SOURCE = Symbol.for('@syncular/transport-http/source');
3
+ function isApiClientLike(value) {
4
+ return (typeof value === 'object' &&
5
+ value !== null &&
6
+ typeof value.GET === 'function' &&
7
+ typeof value.POST === 'function');
8
+ }
9
+ function isJsonContentType(contentType) {
10
+ return contentType?.includes('application/json') === true;
11
+ }
12
+ async function parseErrorBody(response) {
13
+ if (!isJsonContentType(response.headers.get('content-type'))) {
14
+ return undefined;
15
+ }
16
+ try {
17
+ return (await response.json());
18
+ }
19
+ catch {
20
+ return undefined;
21
+ }
22
+ }
23
+ async function parseJsonBody(response) {
24
+ if (!isJsonContentType(response.headers.get('content-type'))) {
25
+ return undefined;
26
+ }
27
+ try {
28
+ return (await response.json());
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ }
34
+ function createAuthRetryResolver(defaultAuthLifecycle) {
35
+ let refreshInFlight = null;
36
+ const runRefreshSingleFlight = async (lifecycle, context) => {
37
+ if (!lifecycle.refreshToken)
38
+ return false;
39
+ if (!refreshInFlight) {
40
+ refreshInFlight = Promise.resolve(lifecycle.refreshToken(context))
41
+ .then((result) => Boolean(result))
42
+ .finally(() => {
43
+ refreshInFlight = null;
44
+ });
45
+ }
46
+ return refreshInFlight;
47
+ };
48
+ return async (context, options) => {
49
+ if (options?.onAuthError) {
50
+ return Boolean(await options.onAuthError());
51
+ }
52
+ const lifecycle = options?.authLifecycle ?? defaultAuthLifecycle;
53
+ if (!lifecycle)
54
+ return false;
55
+ await lifecycle.onAuthExpired?.(context);
56
+ const refreshResult = await runRefreshSingleFlight(lifecycle, context);
57
+ if (lifecycle.retryWithFreshToken) {
58
+ return Boolean(await lifecycle.retryWithFreshToken({ ...context, refreshResult }));
59
+ }
60
+ return refreshResult;
61
+ };
62
+ }
63
+ function createRequestHeaders(baseOptions, extraHeaders) {
64
+ return Promise.resolve(baseOptions.getHeaders?.()).then((dynamicHeaders) => {
65
+ const headers = new Headers(extraHeaders);
66
+ if (dynamicHeaders) {
67
+ for (const [key, value] of Object.entries(dynamicHeaders)) {
68
+ headers.set(key, value);
69
+ }
70
+ }
71
+ if (!headers.has('x-syncular-transport-path')) {
72
+ headers.set('x-syncular-transport-path', baseOptions.transportPath ?? 'direct');
73
+ }
74
+ return headers;
75
+ });
76
+ }
77
+ function createFetchApiClient(baseOptions) {
78
+ const fetchImpl = baseOptions.fetch ?? globalThis.fetch;
79
+ const request = async (args) => {
80
+ const headers = await createRequestHeaders(baseOptions, args.headers);
81
+ let body;
82
+ if (args.body !== undefined) {
83
+ headers.set('content-type', 'application/json');
84
+ body = JSON.stringify(args.body);
85
+ }
86
+ const response = await fetchImpl(resolveRequestUrl(baseOptions.baseUrl, args.path), {
87
+ method: args.method,
88
+ headers,
89
+ body,
90
+ ...(args.signal ? { signal: args.signal } : {}),
91
+ });
92
+ if (!response.ok) {
93
+ return {
94
+ response,
95
+ error: await parseErrorBody(response),
96
+ };
97
+ }
98
+ if (args.parseAs === 'blob') {
99
+ return {
100
+ response,
101
+ data: (await response.blob()),
102
+ };
103
+ }
104
+ return {
105
+ response,
106
+ data: await parseJsonBody(response),
107
+ };
108
+ };
109
+ return {
110
+ sync: (requestBody, signal) => request({
111
+ method: 'POST',
112
+ path: '/sync',
113
+ body: requestBody,
114
+ signal,
115
+ }),
116
+ initiateUpload: (args, signal) => request({
117
+ method: 'POST',
118
+ path: '/sync/blobs/upload',
119
+ body: args,
120
+ signal,
121
+ }),
122
+ completeUpload: (hash, signal) => request({
123
+ method: 'POST',
124
+ path: `/sync/blobs/${encodeURIComponent(hash)}/complete`,
125
+ signal,
126
+ }),
127
+ getDownloadUrl: (hash, signal) => request({
128
+ method: 'GET',
129
+ path: `/sync/blobs/${encodeURIComponent(hash)}/url`,
130
+ signal,
131
+ }),
132
+ getSnapshotChunk: (chunkId, scopeValues, signal) => request({
133
+ method: 'GET',
134
+ path: `/sync/snapshot-chunks/${encodeURIComponent(chunkId)}`,
135
+ headers: (() => {
136
+ const encodedScopes = encodeSnapshotScopes(scopeValues);
137
+ return encodedScopes
138
+ ? {
139
+ [SNAPSHOT_SCOPES_HEADER]: encodedScopes,
140
+ }
141
+ : undefined;
142
+ })(),
143
+ signal,
144
+ parseAs: 'blob',
145
+ }),
146
+ };
147
+ }
148
+ function createTypedTransportClient(client) {
149
+ return {
150
+ sync: (request, signal) => client.POST('/sync', {
151
+ body: request,
152
+ ...(signal ? { signal } : {}),
153
+ }),
154
+ initiateUpload: (args, signal) => client.POST('/sync/blobs/upload', {
155
+ body: args,
156
+ ...(signal ? { signal } : {}),
157
+ }),
158
+ completeUpload: (hash, signal) => client.POST('/sync/blobs/{hash}/complete', {
159
+ params: { path: { hash } },
160
+ ...(signal ? { signal } : {}),
161
+ }),
162
+ getDownloadUrl: (hash, signal) => client.GET('/sync/blobs/{hash}/url', {
163
+ params: { path: { hash } },
164
+ ...(signal ? { signal } : {}),
165
+ }),
166
+ getSnapshotChunk: (chunkId, scopeValues, signal) => client.GET('/sync/snapshot-chunks/{chunkId}', {
167
+ params: { path: { chunkId } },
168
+ parseAs: 'blob',
169
+ headers: (() => {
170
+ const encodedScopes = encodeSnapshotScopes(scopeValues);
171
+ return encodedScopes
172
+ ? {
173
+ [SNAPSHOT_SCOPES_HEADER]: encodedScopes,
174
+ }
175
+ : undefined;
176
+ })(),
177
+ ...(signal ? { signal } : {}),
178
+ }),
179
+ };
180
+ }
181
+ export function createTransportApiClient(source) {
182
+ return isApiClientLike(source)
183
+ ? createTypedTransportClient(source)
184
+ : createFetchApiClient(source);
185
+ }
186
+ export function createTransportAuthRetryResolver(source) {
187
+ return createAuthRetryResolver(isApiClientLike(source) ? undefined : source.authLifecycle);
188
+ }
189
+ //# sourceMappingURL=transport-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport-client.js","sourceRoot":"","sources":["../src/transport-client.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncular/transport-http",
3
- "version": "0.0.6-185",
3
+ "version": "0.0.6-201",
4
4
  "description": "HTTP transport for Syncular client-server communication",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Benjamin Kniffler",
@@ -23,6 +23,7 @@
23
23
  "fetch"
24
24
  ],
25
25
  "private": false,
26
+ "sideEffects": false,
26
27
  "publishConfig": {
27
28
  "access": "public"
28
29
  },
@@ -34,6 +35,13 @@
34
35
  "types": "./dist/index.d.ts",
35
36
  "default": "./dist/index.js"
36
37
  }
38
+ },
39
+ "./blob": {
40
+ "bun": "./src/blob.ts",
41
+ "import": {
42
+ "types": "./dist/blob.d.ts",
43
+ "default": "./dist/blob.js"
44
+ }
37
45
  }
38
46
  },
39
47
  "scripts": {
@@ -43,7 +51,7 @@
43
51
  "release": "bunx syncular-publish"
44
52
  },
45
53
  "dependencies": {
46
- "@syncular/core": "0.0.6-185",
54
+ "@syncular/core": "0.0.6-201",
47
55
  "openapi-fetch": "^0.17.0"
48
56
  },
49
57
  "devDependencies": {
@@ -0,0 +1,39 @@
1
+ import createClient from 'openapi-fetch';
2
+ import type { paths } from './generated/api';
3
+ import type { ClientOptions } from './shared';
4
+
5
+ export type SyncClient = ReturnType<typeof createClient<paths>>;
6
+
7
+ /**
8
+ * Create a typed API client for the full Syncular API.
9
+ *
10
+ * Returns an openapi-fetch client with full type safety for all endpoints.
11
+ */
12
+ export function createApiClient(options: ClientOptions): SyncClient {
13
+ const client = createClient<paths>({
14
+ baseUrl: options.baseUrl,
15
+ ...(options.fetch && { fetch: options.fetch }),
16
+ });
17
+
18
+ const getHeaders = options.getHeaders;
19
+ const transportPath = options.transportPath ?? 'direct';
20
+
21
+ client.use({
22
+ async onRequest({ request }) {
23
+ if (getHeaders) {
24
+ const headers = await getHeaders();
25
+ for (const [key, value] of Object.entries(headers)) {
26
+ request.headers.set(key, value);
27
+ }
28
+ }
29
+
30
+ if (!request.headers.has('x-syncular-transport-path')) {
31
+ request.headers.set('x-syncular-transport-path', transportPath);
32
+ }
33
+
34
+ return request;
35
+ },
36
+ });
37
+
38
+ return client;
39
+ }
package/src/blob.ts ADDED
@@ -0,0 +1,95 @@
1
+ import type { SyncTransport, SyncTransportBlobs } from '@syncular/core';
2
+ import { SyncTransportError } from '@syncular/core';
3
+ import { executeWithAuthRetry, getErrorMessage } from './shared';
4
+ import {
5
+ createTransportApiClient,
6
+ createTransportAuthRetryResolver,
7
+ HTTP_TRANSPORT_SOURCE,
8
+ type HttpTransportSource,
9
+ } from './transport-client';
10
+
11
+ type HttpTransportWithSource = SyncTransport & {
12
+ [HTTP_TRANSPORT_SOURCE]?: HttpTransportSource;
13
+ };
14
+
15
+ export function createHttpTransportBlobs(
16
+ source: HttpTransportSource
17
+ ): SyncTransportBlobs {
18
+ const client = createTransportApiClient(source);
19
+ const resolveAuthRetry = createTransportAuthRetryResolver(source);
20
+
21
+ return {
22
+ async initiateUpload(args) {
23
+ const { data, error, response } = await executeWithAuthRetry(
24
+ (signal) => client.initiateUpload(args, signal),
25
+ undefined,
26
+ 'blobInitiateUpload',
27
+ resolveAuthRetry
28
+ );
29
+
30
+ if (error || !data) {
31
+ throw new SyncTransportError(
32
+ `Blob upload init failed: ${getErrorMessage(error) || response.statusText}`,
33
+ response.status
34
+ );
35
+ }
36
+
37
+ return data;
38
+ },
39
+
40
+ async completeUpload(hash) {
41
+ const { data, error } = await executeWithAuthRetry(
42
+ (signal) => client.completeUpload(hash, signal),
43
+ undefined,
44
+ 'blobCompleteUpload',
45
+ resolveAuthRetry
46
+ );
47
+
48
+ if (error || !data) {
49
+ return {
50
+ ok: false,
51
+ error: getErrorMessage(error) || 'Complete upload failed',
52
+ };
53
+ }
54
+
55
+ return data;
56
+ },
57
+
58
+ async getDownloadUrl(hash) {
59
+ const { data, error, response } = await executeWithAuthRetry(
60
+ (signal) => client.getDownloadUrl(hash, signal),
61
+ undefined,
62
+ 'blobGetDownloadUrl',
63
+ resolveAuthRetry
64
+ );
65
+
66
+ if (error || !data) {
67
+ throw new SyncTransportError(
68
+ `Get download URL failed: ${getErrorMessage(error) || response.statusText}`,
69
+ response.status
70
+ );
71
+ }
72
+
73
+ return data;
74
+ },
75
+ };
76
+ }
77
+
78
+ export function ensureHttpTransportBlobs(
79
+ transport: SyncTransport
80
+ ): SyncTransportBlobs | null {
81
+ if (transport.blobs) {
82
+ return transport.blobs;
83
+ }
84
+
85
+ const source = (transport as HttpTransportWithSource)[HTTP_TRANSPORT_SOURCE];
86
+ if (!source) {
87
+ return null;
88
+ }
89
+
90
+ const blobs = createHttpTransportBlobs(source);
91
+ transport.blobs = blobs;
92
+ return blobs;
93
+ }
94
+
95
+ export type { SyncClient } from './api-client';
@@ -739,18 +739,20 @@ export interface operations {
739
739
  "application/json": {
740
740
  clientId: string;
741
741
  push?: {
742
- clientCommitId: string;
743
- operations: {
744
- table: string;
745
- row_id: string;
746
- /** @enum {string} */
747
- op: "upsert" | "delete";
748
- payload: {
749
- [key: string]: unknown;
750
- } | null;
751
- base_version?: number | null;
742
+ commits: {
743
+ clientCommitId: string;
744
+ operations: {
745
+ table: string;
746
+ row_id: string;
747
+ /** @enum {string} */
748
+ op: "upsert" | "delete";
749
+ payload: {
750
+ [key: string]: unknown;
751
+ } | null;
752
+ base_version?: number | null;
753
+ }[];
754
+ schemaVersion: number;
752
755
  }[];
753
- schemaVersion: number;
754
756
  };
755
757
  pull?: {
756
758
  limitCommits: number;
@@ -791,28 +793,33 @@ export interface operations {
791
793
  push?: {
792
794
  /** @constant */
793
795
  ok: true;
794
- /** @enum {string} */
795
- status: "applied" | "cached" | "rejected";
796
- commitSeq?: number;
797
- results: ({
798
- opIndex: number;
799
- /** @constant */
800
- status: "applied";
801
- } | {
802
- opIndex: number;
796
+ commits: {
803
797
  /** @constant */
804
- status: "conflict";
805
- message: string;
806
- server_version: number;
807
- server_row: unknown;
808
- } | {
809
- opIndex: number;
810
- /** @constant */
811
- status: "error";
812
- error: string;
813
- code?: string;
814
- retriable?: boolean;
815
- })[];
798
+ ok: true;
799
+ /** @enum {string} */
800
+ status: "applied" | "cached" | "rejected";
801
+ commitSeq?: number;
802
+ results: ({
803
+ opIndex: number;
804
+ /** @constant */
805
+ status: "applied";
806
+ } | {
807
+ opIndex: number;
808
+ /** @constant */
809
+ status: "conflict";
810
+ message: string;
811
+ server_version: number;
812
+ server_row: unknown;
813
+ } | {
814
+ opIndex: number;
815
+ /** @constant */
816
+ status: "error";
817
+ error: string;
818
+ code?: string;
819
+ retriable?: boolean;
820
+ })[];
821
+ clientCommitId: string;
822
+ }[];
816
823
  };
817
824
  pull?: {
818
825
  /** @constant */
@@ -898,7 +905,9 @@ export interface operations {
898
905
  };
899
906
  getSyncSnapshotChunksByChunkId: {
900
907
  parameters: {
901
- query?: never;
908
+ query?: {
909
+ scopes?: string;
910
+ };
902
911
  header?: never;
903
912
  path: {
904
913
  chunkId: string;