@syncular/core 0.1.2 → 0.2.0

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.
Files changed (231) hide show
  1. package/README.md +5 -19
  2. package/dist/blob-ref.d.ts +21 -0
  3. package/dist/blob-ref.js +97 -0
  4. package/dist/bytes.d.ts +45 -0
  5. package/dist/bytes.js +242 -0
  6. package/dist/canonical-json.d.ts +10 -0
  7. package/dist/canonical-json.js +18 -0
  8. package/dist/constants.d.ts +5 -0
  9. package/dist/constants.js +5 -0
  10. package/dist/crypto.d.ts +60 -0
  11. package/dist/crypto.js +251 -0
  12. package/dist/errors.d.ts +14 -0
  13. package/dist/errors.js +17 -0
  14. package/dist/frames.d.ts +24 -0
  15. package/dist/frames.js +42 -0
  16. package/dist/index.d.ts +18 -26
  17. package/dist/index.js +18 -43
  18. package/dist/message.d.ts +157 -0
  19. package/dist/message.js +786 -0
  20. package/dist/realtime.d.ts +92 -0
  21. package/dist/realtime.js +222 -0
  22. package/dist/render.d.ts +22 -0
  23. package/dist/render.js +273 -0
  24. package/dist/row-codec.d.ts +39 -0
  25. package/dist/row-codec.js +176 -0
  26. package/dist/segment.d.ts +18 -0
  27. package/dist/segment.js +105 -0
  28. package/dist/stream.d.ts +20 -0
  29. package/dist/stream.js +102 -0
  30. package/package.json +7 -59
  31. package/src/blob-ref.ts +135 -0
  32. package/src/bytes.ts +301 -0
  33. package/src/canonical-json.ts +20 -0
  34. package/src/constants.ts +7 -0
  35. package/src/crypto.ts +335 -0
  36. package/src/errors.ts +18 -0
  37. package/src/frames.ts +47 -0
  38. package/src/index.ts +18 -43
  39. package/src/message.ts +1024 -0
  40. package/src/realtime.ts +340 -0
  41. package/src/render.ts +325 -0
  42. package/src/row-codec.ts +265 -0
  43. package/src/segment.ts +150 -0
  44. package/src/stream.ts +128 -0
  45. package/dist/blobs.d.ts +0 -153
  46. package/dist/blobs.d.ts.map +0 -1
  47. package/dist/blobs.js +0 -47
  48. package/dist/blobs.js.map +0 -1
  49. package/dist/column-codecs.d.ts +0 -59
  50. package/dist/column-codecs.d.ts.map +0 -1
  51. package/dist/column-codecs.js +0 -159
  52. package/dist/column-codecs.js.map +0 -1
  53. package/dist/conflict.d.ts +0 -22
  54. package/dist/conflict.d.ts.map +0 -1
  55. package/dist/conflict.js +0 -81
  56. package/dist/conflict.js.map +0 -1
  57. package/dist/database.d.ts +0 -7
  58. package/dist/database.d.ts.map +0 -1
  59. package/dist/database.js +0 -14
  60. package/dist/database.js.map +0 -1
  61. package/dist/dialect.d.ts +0 -7
  62. package/dist/dialect.d.ts.map +0 -1
  63. package/dist/dialect.js +0 -2
  64. package/dist/dialect.js.map +0 -1
  65. package/dist/error-responses.d.ts +0 -393
  66. package/dist/error-responses.d.ts.map +0 -1
  67. package/dist/error-responses.js +0 -393
  68. package/dist/error-responses.js.map +0 -1
  69. package/dist/http/api-client.d.ts +0 -11
  70. package/dist/http/api-client.d.ts.map +0 -1
  71. package/dist/http/api-client.js +0 -30
  72. package/dist/http/api-client.js.map +0 -1
  73. package/dist/http/blob.d.ts +0 -6
  74. package/dist/http/blob.d.ts.map +0 -1
  75. package/dist/http/blob.js +0 -46
  76. package/dist/http/blob.js.map +0 -1
  77. package/dist/http/generated/api.d.ts +0 -5241
  78. package/dist/http/generated/api.d.ts.map +0 -1
  79. package/dist/http/generated/api.js +0 -6
  80. package/dist/http/generated/api.js.map +0 -1
  81. package/dist/http/index.d.ts +0 -18
  82. package/dist/http/index.d.ts.map +0 -1
  83. package/dist/http/index.js +0 -212
  84. package/dist/http/index.js.map +0 -1
  85. package/dist/http/shared.d.ts +0 -45
  86. package/dist/http/shared.d.ts.map +0 -1
  87. package/dist/http/shared.js +0 -122
  88. package/dist/http/shared.js.map +0 -1
  89. package/dist/http/transport-client.d.ts +0 -31
  90. package/dist/http/transport-client.d.ts.map +0 -1
  91. package/dist/http/transport-client.js +0 -193
  92. package/dist/http/transport-client.js.map +0 -1
  93. package/dist/index.d.ts.map +0 -1
  94. package/dist/index.js.map +0 -1
  95. package/dist/kysely-column-codecs.d.ts +0 -31
  96. package/dist/kysely-column-codecs.d.ts.map +0 -1
  97. package/dist/kysely-column-codecs.js +0 -452
  98. package/dist/kysely-column-codecs.js.map +0 -1
  99. package/dist/logger.d.ts +0 -29
  100. package/dist/logger.d.ts.map +0 -1
  101. package/dist/logger.js +0 -26
  102. package/dist/logger.js.map +0 -1
  103. package/dist/proxy/index.d.ts +0 -54
  104. package/dist/proxy/index.d.ts.map +0 -1
  105. package/dist/proxy/index.js +0 -7
  106. package/dist/proxy/index.js.map +0 -1
  107. package/dist/schemas/blobs.d.ts +0 -82
  108. package/dist/schemas/blobs.d.ts.map +0 -1
  109. package/dist/schemas/blobs.js +0 -64
  110. package/dist/schemas/blobs.js.map +0 -1
  111. package/dist/schemas/common.d.ts +0 -100
  112. package/dist/schemas/common.d.ts.map +0 -1
  113. package/dist/schemas/common.js +0 -64
  114. package/dist/schemas/common.js.map +0 -1
  115. package/dist/schemas/index.d.ts +0 -7
  116. package/dist/schemas/index.d.ts.map +0 -1
  117. package/dist/schemas/index.js +0 -7
  118. package/dist/schemas/index.js.map +0 -1
  119. package/dist/schemas/sync.d.ts +0 -1065
  120. package/dist/schemas/sync.d.ts.map +0 -1
  121. package/dist/schemas/sync.js +0 -361
  122. package/dist/schemas/sync.js.map +0 -1
  123. package/dist/scopes/index.d.ts +0 -113
  124. package/dist/scopes/index.d.ts.map +0 -1
  125. package/dist/scopes/index.js +0 -103
  126. package/dist/scopes/index.js.map +0 -1
  127. package/dist/sentry.d.ts +0 -31
  128. package/dist/sentry.d.ts.map +0 -1
  129. package/dist/sentry.js +0 -147
  130. package/dist/sentry.js.map +0 -1
  131. package/dist/snapshot-chunks.d.ts +0 -110
  132. package/dist/snapshot-chunks.d.ts.map +0 -1
  133. package/dist/snapshot-chunks.js +0 -682
  134. package/dist/snapshot-chunks.js.map +0 -1
  135. package/dist/sync-packs.d.ts +0 -33
  136. package/dist/sync-packs.d.ts.map +0 -1
  137. package/dist/sync-packs.js +0 -821
  138. package/dist/sync-packs.js.map +0 -1
  139. package/dist/telemetry.d.ts +0 -114
  140. package/dist/telemetry.d.ts.map +0 -1
  141. package/dist/telemetry.js +0 -114
  142. package/dist/telemetry.js.map +0 -1
  143. package/dist/transforms.d.ts +0 -146
  144. package/dist/transforms.d.ts.map +0 -1
  145. package/dist/transforms.js +0 -155
  146. package/dist/transforms.js.map +0 -1
  147. package/dist/types.d.ts +0 -210
  148. package/dist/types.d.ts.map +0 -1
  149. package/dist/types.js +0 -20
  150. package/dist/types.js.map +0 -1
  151. package/dist/utils/bytes.d.ts +0 -4
  152. package/dist/utils/bytes.d.ts.map +0 -1
  153. package/dist/utils/bytes.js +0 -66
  154. package/dist/utils/bytes.js.map +0 -1
  155. package/dist/utils/compression.d.ts +0 -22
  156. package/dist/utils/compression.d.ts.map +0 -1
  157. package/dist/utils/compression.js +0 -129
  158. package/dist/utils/compression.js.map +0 -1
  159. package/dist/utils/crypto.d.ts +0 -13
  160. package/dist/utils/crypto.d.ts.map +0 -1
  161. package/dist/utils/crypto.js +0 -107
  162. package/dist/utils/crypto.js.map +0 -1
  163. package/dist/utils/id.d.ts +0 -3
  164. package/dist/utils/id.d.ts.map +0 -1
  165. package/dist/utils/id.js +0 -25
  166. package/dist/utils/id.js.map +0 -1
  167. package/dist/utils/index.d.ts +0 -10
  168. package/dist/utils/index.d.ts.map +0 -1
  169. package/dist/utils/index.js +0 -10
  170. package/dist/utils/index.js.map +0 -1
  171. package/dist/utils/internal-runtime.d.ts +0 -16
  172. package/dist/utils/internal-runtime.d.ts.map +0 -1
  173. package/dist/utils/internal-runtime.js +0 -14
  174. package/dist/utils/internal-runtime.js.map +0 -1
  175. package/dist/utils/object.d.ts +0 -2
  176. package/dist/utils/object.d.ts.map +0 -1
  177. package/dist/utils/object.js +0 -4
  178. package/dist/utils/object.js.map +0 -1
  179. package/dist/utils/realtime-change-index.d.ts +0 -9
  180. package/dist/utils/realtime-change-index.d.ts.map +0 -1
  181. package/dist/utils/realtime-change-index.js +0 -36
  182. package/dist/utils/realtime-change-index.js.map +0 -1
  183. package/dist/utils/realtime-connection-registry.d.ts +0 -40
  184. package/dist/utils/realtime-connection-registry.d.ts.map +0 -1
  185. package/dist/utils/realtime-connection-registry.js +0 -237
  186. package/dist/utils/realtime-connection-registry.js.map +0 -1
  187. package/dist/utils/table-registry.d.ts +0 -10
  188. package/dist/utils/table-registry.d.ts.map +0 -1
  189. package/dist/utils/table-registry.js +0 -54
  190. package/dist/utils/table-registry.js.map +0 -1
  191. package/dist/utils/url.d.ts +0 -3
  192. package/dist/utils/url.d.ts.map +0 -1
  193. package/dist/utils/url.js +0 -20
  194. package/dist/utils/url.js.map +0 -1
  195. package/src/blobs.ts +0 -219
  196. package/src/column-codecs.ts +0 -262
  197. package/src/conflict.ts +0 -92
  198. package/src/database.ts +0 -21
  199. package/src/dialect.ts +0 -8
  200. package/src/error-responses.ts +0 -423
  201. package/src/http/api-client.ts +0 -39
  202. package/src/http/blob.ts +0 -95
  203. package/src/http/generated/api.ts +0 -5241
  204. package/src/http/index.ts +0 -365
  205. package/src/http/shared.ts +0 -220
  206. package/src/http/transport-client.ts +0 -314
  207. package/src/kysely-column-codecs.ts +0 -585
  208. package/src/logger.ts +0 -38
  209. package/src/proxy/index.ts +0 -57
  210. package/src/schemas/blobs.ts +0 -110
  211. package/src/schemas/common.ts +0 -89
  212. package/src/schemas/index.ts +0 -7
  213. package/src/schemas/sync.ts +0 -517
  214. package/src/scopes/index.ts +0 -237
  215. package/src/sentry.ts +0 -200
  216. package/src/snapshot-chunks.ts +0 -964
  217. package/src/sync-packs.ts +0 -1096
  218. package/src/telemetry.ts +0 -243
  219. package/src/transforms.ts +0 -256
  220. package/src/types.ts +0 -267
  221. package/src/utils/bytes.ts +0 -75
  222. package/src/utils/compression.ts +0 -187
  223. package/src/utils/crypto.ts +0 -146
  224. package/src/utils/id.ts +0 -34
  225. package/src/utils/index.ts +0 -9
  226. package/src/utils/internal-runtime.ts +0 -39
  227. package/src/utils/object.ts +0 -3
  228. package/src/utils/realtime-change-index.ts +0 -44
  229. package/src/utils/realtime-connection-registry.ts +0 -286
  230. package/src/utils/table-registry.ts +0 -67
  231. package/src/utils/url.ts +0 -29
package/src/types.ts DELETED
@@ -1,267 +0,0 @@
1
- /**
2
- * @syncular/core - Shared types for sync infrastructure
3
- *
4
- * Non-protocol types: conflict detection, transport interfaces.
5
- * Protocol types (SyncOp, SyncPushRequest, etc.) live in ./schemas/sync.ts
6
- */
7
-
8
- import type { SyncCombinedRequest, SyncCombinedResponse } from './schemas/sync';
9
-
10
- // ============================================================================
11
- // Conflict Detection Types
12
- // ============================================================================
13
-
14
- /**
15
- * Result of a conflict check - no conflict
16
- */
17
- interface ConflictCheckResultOk {
18
- hasConflict: false;
19
- }
20
-
21
- /**
22
- * Result of a conflict check - conflict detected
23
- */
24
- interface ConflictCheckResultConflict {
25
- hasConflict: true;
26
- /** Fields with conflicting changes */
27
- conflictingFields: string[];
28
- /** Current server row state */
29
- serverRow: Record<string, unknown>;
30
- /** Current server version */
31
- serverVersion: number;
32
- }
33
-
34
- /**
35
- * Union type for conflict check results
36
- */
37
- export type ConflictCheckResult =
38
- | ConflictCheckResultOk
39
- | ConflictCheckResultConflict;
40
-
41
- /**
42
- * Result of a field-level merge - can merge
43
- */
44
- export interface MergeResultOk {
45
- canMerge: true;
46
- /** Merged payload combining client and server changes */
47
- mergedPayload: Record<string, unknown>;
48
- }
49
-
50
- /**
51
- * Result of a field-level merge - cannot merge
52
- */
53
- export interface MergeResultConflict {
54
- canMerge: false;
55
- /** Fields that cannot be auto-merged */
56
- conflictingFields: string[];
57
- }
58
-
59
- /**
60
- * Union type for merge results
61
- */
62
- export type MergeResult = MergeResultOk | MergeResultConflict;
63
-
64
- // ============================================================================
65
- // Identity Types
66
- // ============================================================================
67
-
68
- /**
69
- * Canonical identity base shape used across Syncular auth-bound APIs.
70
- */
71
- export interface SyncIdentityBase {
72
- actorId: string;
73
- }
74
-
75
- // ============================================================================
76
- // Transport Types
77
- // ============================================================================
78
-
79
- /**
80
- * Options for transport operations.
81
- * Provides hooks for auth errors and cancellation support.
82
- */
83
- export type SyncAuthOperation =
84
- | 'sync'
85
- | 'authLeaseIssue'
86
- | 'snapshotChunk'
87
- | 'snapshotChunkStream'
88
- | 'blobInitiateUpload'
89
- | 'blobCompleteUpload'
90
- | 'blobGetDownloadUrl';
91
-
92
- export interface SyncAuthErrorContext {
93
- operation: SyncAuthOperation;
94
- status: 401 | 403;
95
- }
96
-
97
- export interface SyncAuthRetryContext extends SyncAuthErrorContext {
98
- refreshResult: boolean;
99
- }
100
-
101
- export interface SyncAuthLifecycle {
102
- /**
103
- * Called when a request receives 401/403.
104
- * Useful for marking auth/session as stale in app state.
105
- */
106
- onAuthExpired?: (context: SyncAuthErrorContext) => Promise<void> | void;
107
- /**
108
- * Refresh credentials.
109
- * Return true when refresh succeeded and a retry should be considered.
110
- */
111
- refreshToken?: (context: SyncAuthErrorContext) => Promise<boolean> | boolean;
112
- /**
113
- * Final retry decision after refresh completes.
114
- * Defaults to `refreshResult` when omitted.
115
- */
116
- retryWithFreshToken?: (
117
- context: SyncAuthRetryContext
118
- ) => Promise<boolean> | boolean;
119
- }
120
-
121
- export interface SyncTransportOptions {
122
- /**
123
- * First-class auth lifecycle callbacks.
124
- * Use this to centralize auth refresh behavior.
125
- */
126
- authLifecycle?: SyncAuthLifecycle;
127
- /**
128
- * Abort signal for cancellation support.
129
- */
130
- signal?: AbortSignal;
131
- }
132
-
133
- export type SyncSnapshotChunkReadMode = 'stream' | 'bytes';
134
-
135
- export type SyncGzipDecompressionMode = 'stream' | 'buffered';
136
-
137
- export type SyncBootstrapApplyMode = 'single-transaction' | 'per-subscription';
138
-
139
- /**
140
- * Declares transport/runtime capabilities that affect how the client should
141
- * fetch, decode, and apply bootstrap snapshots.
142
- */
143
- export interface SyncTransportCapabilities {
144
- /**
145
- * Preferred snapshot chunk download mode for this runtime.
146
- * - `stream`: consume the response body as a stream when available
147
- * - `bytes`: buffer the whole response body before decode/apply
148
- */
149
- snapshotChunkReadMode?: SyncSnapshotChunkReadMode;
150
- /**
151
- * Preferred gzip decode mode for this runtime.
152
- * - `stream`: use `DecompressionStream`
153
- * - `buffered`: gunzip a materialized byte buffer
154
- */
155
- gzipDecompressionMode?: SyncGzipDecompressionMode;
156
- /**
157
- * Hint that chunked bootstrap snapshots should be materialized into rows
158
- * before plugin/app consumption.
159
- */
160
- preferMaterializedSnapshots?: boolean;
161
- /**
162
- * Preferred default transaction strategy for bootstrap apply.
163
- */
164
- preferredBootstrapApplyMode?: SyncBootstrapApplyMode;
165
- /**
166
- * Preferred scheduler yield delay (ms) between bootstrap snapshot apply
167
- * batches. `0` yields on the next macrotask, `false` disables yielding.
168
- */
169
- preferredSnapshotApplyYieldMs?: number | false;
170
- }
171
-
172
- /**
173
- * Blob transport operations (optional extension to SyncTransport).
174
- * When present, enables blob upload/download through the same transport.
175
- */
176
- export interface SyncTransportBlobs {
177
- /**
178
- * Initiate a blob upload.
179
- * Returns presigned URL info or indicates blob already exists (dedup).
180
- */
181
- initiateUpload(args: {
182
- hash: string;
183
- size: number;
184
- mimeType: string;
185
- }): Promise<{
186
- exists: boolean;
187
- uploadUrl?: string;
188
- uploadMethod?: 'PUT' | 'POST';
189
- uploadHeaders?: Record<string, string>;
190
- }>;
191
-
192
- /**
193
- * Complete a blob upload.
194
- * Call this after uploading to the presigned URL.
195
- */
196
- completeUpload(hash: string): Promise<{ ok: boolean; error?: string }>;
197
-
198
- /**
199
- * Get a presigned download URL.
200
- */
201
- getDownloadUrl(hash: string): Promise<{
202
- url: string;
203
- expiresAt: string;
204
- }>;
205
- }
206
-
207
- /**
208
- * Transport interface for sync operations.
209
- */
210
- export interface SyncTransport {
211
- /**
212
- * Optional runtime/transport capabilities.
213
- */
214
- capabilities?: SyncTransportCapabilities;
215
- /**
216
- * Combined push+pull in a single round-trip.
217
- */
218
- sync(
219
- request: SyncCombinedRequest,
220
- options?: SyncTransportOptions
221
- ): Promise<SyncCombinedResponse>;
222
-
223
- /**
224
- * Download an encoded bootstrap snapshot chunk.
225
- */
226
- fetchSnapshotChunk(
227
- request: {
228
- chunkId: string;
229
- scopeValues?: Record<string, string | string[]>;
230
- },
231
- options?: SyncTransportOptions
232
- ): Promise<Uint8Array>;
233
-
234
- /**
235
- * Optional streaming snapshot chunk download.
236
- *
237
- * When implemented, clients can decode and apply large bootstrap chunks
238
- * incrementally without materializing the entire chunk in memory.
239
- */
240
- fetchSnapshotChunkStream?(
241
- request: {
242
- chunkId: string;
243
- scopeValues?: Record<string, string | string[]>;
244
- },
245
- options?: SyncTransportOptions
246
- ): Promise<ReadableStream<Uint8Array>>;
247
-
248
- /**
249
- * Optional blob operations.
250
- * When present, enables blob upload/download functionality.
251
- */
252
- blobs?: SyncTransportBlobs;
253
- }
254
-
255
- /**
256
- * Transport error with additional context
257
- */
258
- export class SyncTransportError extends Error {
259
- constructor(
260
- message: string,
261
- public readonly status?: number,
262
- public readonly code?: string
263
- ) {
264
- super(message);
265
- this.name = 'SyncTransportError';
266
- }
267
- }
@@ -1,75 +0,0 @@
1
- import { getBunRuntime } from './internal-runtime';
2
-
3
- export function bytesToReadableStream(
4
- bytes: Uint8Array
5
- ): ReadableStream<Uint8Array> {
6
- return new ReadableStream<Uint8Array>({
7
- start(controller) {
8
- controller.enqueue(bytes);
9
- controller.close();
10
- },
11
- });
12
- }
13
-
14
- export function concatByteChunks(chunks: readonly Uint8Array[]): Uint8Array {
15
- if (chunks.length === 0) {
16
- return new Uint8Array();
17
- }
18
- if (chunks.length === 1) {
19
- return chunks[0] ?? new Uint8Array();
20
- }
21
-
22
- let total = 0;
23
- for (const chunk of chunks) {
24
- total += chunk.length;
25
- }
26
-
27
- const merged = new Uint8Array(total);
28
- let offset = 0;
29
- for (const chunk of chunks) {
30
- merged.set(chunk, offset);
31
- offset += chunk.length;
32
- }
33
- return merged;
34
- }
35
-
36
- export async function readAllBytesFromStream(
37
- stream: ReadableStream<Uint8Array>
38
- ): Promise<Uint8Array> {
39
- const bun = getBunRuntime();
40
- if (bun?.readableStreamToBytes) {
41
- const bytes = await bun.readableStreamToBytes(stream);
42
- return bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
43
- }
44
-
45
- const reader = stream.getReader();
46
- const chunks: Uint8Array[] = [];
47
- let total = 0;
48
-
49
- try {
50
- while (true) {
51
- const { done, value } = await reader.read();
52
- if (done) break;
53
- if (!value) continue;
54
- chunks.push(value);
55
- total += value.length;
56
- }
57
- } finally {
58
- reader.releaseLock();
59
- }
60
-
61
- if (chunks.length === 0) {
62
- return new Uint8Array();
63
- }
64
- if (chunks.length === 1) {
65
- return chunks[0] ?? new Uint8Array();
66
- }
67
-
68
- const merged = new Uint8Array(total);
69
- let offset = 0;
70
- for (const chunk of chunks) {
71
- merged.set(chunk, offset);
72
- offset += chunk.length;
73
- }
74
- return merged;
75
- }
@@ -1,187 +0,0 @@
1
- import { gunzipSync as gunzipSyncFflate } from 'fflate';
2
- import { bytesToReadableStream, readAllBytesFromStream } from './bytes';
3
- import { getBunRuntime, usesNodeRuntimeModules } from './internal-runtime';
4
-
5
- type CompressionResult = Uint8Array | ArrayBuffer | ArrayLike<number>;
6
-
7
- interface NodeZlibModule {
8
- gzip(
9
- payload: Uint8Array,
10
- callback: (error: Error | null, compressed: CompressionResult) => void
11
- ): void;
12
- gzip(
13
- payload: Uint8Array,
14
- options: { level?: number },
15
- callback: (error: Error | null, compressed: CompressionResult) => void
16
- ): void;
17
- gunzip(
18
- payload: Uint8Array,
19
- callback: (error: Error | null, decompressed: CompressionResult) => void
20
- ): void;
21
- }
22
-
23
- let nodeZlibModulePromise: Promise<NodeZlibModule | null> | null = null;
24
-
25
- export interface GzipBytesOptions {
26
- level?: number;
27
- }
28
-
29
- function importNodeModule(specifier: string): Promise<object> {
30
- return new Function('specifier', 'return import(specifier);')(
31
- specifier
32
- ) as Promise<object>;
33
- }
34
-
35
- function tryImportNodeModule(specifier: string): Promise<object | null> {
36
- try {
37
- return importNodeModule(specifier);
38
- } catch {
39
- return Promise.resolve(null);
40
- }
41
- }
42
-
43
- function isNodeZlibModule(module: object | null): module is NodeZlibModule {
44
- if (!module) {
45
- return false;
46
- }
47
-
48
- const candidate = module as Partial<NodeZlibModule>;
49
- return (
50
- typeof candidate.gzip === 'function' &&
51
- typeof candidate.gunzip === 'function'
52
- );
53
- }
54
-
55
- function toUint8Array(value: CompressionResult): Uint8Array {
56
- if (value instanceof Uint8Array) {
57
- return value;
58
- }
59
- if (value instanceof ArrayBuffer) {
60
- return new Uint8Array(value);
61
- }
62
- return Uint8Array.from(value);
63
- }
64
-
65
- async function getNodeZlibModule(): Promise<NodeZlibModule | null> {
66
- if (!usesNodeRuntimeModules()) {
67
- return null;
68
- }
69
- if (!nodeZlibModulePromise) {
70
- nodeZlibModulePromise = tryImportNodeModule('node:zlib')
71
- .then((module) => (isNodeZlibModule(module) ? module : null))
72
- .catch(() => null);
73
- }
74
- return nodeZlibModulePromise;
75
- }
76
-
77
- /**
78
- * Gzip-compress a byte array using the fastest native implementation available
79
- * in the current runtime.
80
- */
81
- export async function gzipBytes(
82
- payload: Uint8Array,
83
- options: GzipBytesOptions = {}
84
- ): Promise<Uint8Array> {
85
- const bun = getBunRuntime();
86
- if (bun?.gzipSync) {
87
- return bun.gzipSync(payload, options);
88
- }
89
-
90
- const nodeZlib = await getNodeZlibModule();
91
- if (nodeZlib?.gzip) {
92
- return await new Promise<Uint8Array>((resolve, reject) => {
93
- const callback = (error: Error | null, compressed: CompressionResult) => {
94
- if (error) {
95
- reject(error);
96
- return;
97
- }
98
- resolve(toUint8Array(compressed));
99
- };
100
- if (options.level !== undefined) {
101
- nodeZlib.gzip(payload, { level: options.level }, callback);
102
- } else {
103
- nodeZlib.gzip(payload, callback);
104
- }
105
- });
106
- }
107
-
108
- if (typeof CompressionStream !== 'undefined') {
109
- const stream = bytesToReadableStream(payload).pipeThrough(
110
- new CompressionStream('gzip') as TransformStream<Uint8Array, Uint8Array>
111
- );
112
- return readAllBytesFromStream(stream);
113
- }
114
-
115
- throw new Error(
116
- 'Failed to gzip bytes, no compression implementation available'
117
- );
118
- }
119
-
120
- /**
121
- * Gzip-decompress a byte array using the fastest implementation available in
122
- * the current runtime, with a pure-JS fallback for runtimes like Expo/Hermes.
123
- */
124
- export async function gunzipBytes(payload: Uint8Array): Promise<Uint8Array> {
125
- const bun = getBunRuntime();
126
- if (bun?.gunzipSync) {
127
- return bun.gunzipSync(payload);
128
- }
129
-
130
- const nodeZlib = await getNodeZlibModule();
131
- if (nodeZlib?.gunzip) {
132
- return await new Promise<Uint8Array>((resolve, reject) => {
133
- nodeZlib.gunzip(payload, (error, decompressed) => {
134
- if (error) {
135
- reject(error);
136
- return;
137
- }
138
- resolve(toUint8Array(decompressed));
139
- });
140
- });
141
- }
142
-
143
- if (typeof DecompressionStream !== 'undefined') {
144
- const stream = bytesToReadableStream(payload).pipeThrough(
145
- new DecompressionStream('gzip') as TransformStream<Uint8Array, Uint8Array>
146
- );
147
- return readAllBytesFromStream(stream);
148
- }
149
-
150
- try {
151
- return gunzipSyncFflate(payload);
152
- } catch (error) {
153
- throw new Error(
154
- 'Failed to gunzip bytes, no decompression implementation available',
155
- { cause: error }
156
- );
157
- }
158
- }
159
-
160
- /**
161
- * Gzip-compress bytes and return a stream. When streaming compression is not
162
- * available, falls back to eager compression and includes byteLength metadata.
163
- */
164
- export async function gzipBytesToStream(payload: Uint8Array): Promise<{
165
- stream: ReadableStream<Uint8Array>;
166
- byteLength?: number;
167
- }> {
168
- const bun = getBunRuntime();
169
- const nodeZlib = await getNodeZlibModule();
170
-
171
- if (!bun?.gzipSync && !nodeZlib && typeof CompressionStream !== 'undefined') {
172
- const source = bytesToReadableStream(payload);
173
- const gzipStream = new CompressionStream('gzip') as TransformStream<
174
- Uint8Array,
175
- Uint8Array
176
- >;
177
- return {
178
- stream: source.pipeThrough(gzipStream),
179
- };
180
- }
181
-
182
- const compressed = await gzipBytes(payload);
183
- return {
184
- stream: bytesToReadableStream(compressed),
185
- byteLength: compressed.length,
186
- };
187
- }
@@ -1,146 +0,0 @@
1
- import { concatByteChunks } from './bytes';
2
- import { getBunRuntime, usesNodeRuntimeModules } from './internal-runtime';
3
-
4
- const textEncoder = new TextEncoder();
5
-
6
- interface NodeHash {
7
- update(data: string | Uint8Array): NodeHash;
8
- digest(encoding: 'hex'): string;
9
- }
10
-
11
- interface NodeCryptoModule {
12
- createHash(algorithm: string): NodeHash;
13
- }
14
-
15
- function toHex(bytes: Uint8Array): string {
16
- return Array.from(bytes)
17
- .map((byte) => byte.toString(16).padStart(2, '0'))
18
- .join('');
19
- }
20
-
21
- function toDigestBufferSource(payload: Uint8Array): Uint8Array<ArrayBuffer> {
22
- if (payload.buffer instanceof ArrayBuffer) {
23
- return new Uint8Array(
24
- payload.buffer,
25
- payload.byteOffset,
26
- payload.byteLength
27
- );
28
- }
29
-
30
- const owned = new Uint8Array(payload.byteLength);
31
- owned.set(payload);
32
- return owned;
33
- }
34
-
35
- let nodeCryptoModulePromise: Promise<NodeCryptoModule | null> | null = null;
36
-
37
- function importNodeModule(specifier: string): Promise<object> {
38
- return new Function('specifier', 'return import(specifier);')(
39
- specifier
40
- ) as Promise<object>;
41
- }
42
-
43
- function tryImportNodeModule(specifier: string): Promise<object | null> {
44
- try {
45
- return importNodeModule(specifier);
46
- } catch {
47
- return Promise.resolve(null);
48
- }
49
- }
50
-
51
- function isNodeCryptoModule(module: object | null): module is NodeCryptoModule {
52
- if (!module) {
53
- return false;
54
- }
55
- return typeof (module as Partial<NodeCryptoModule>).createHash === 'function';
56
- }
57
-
58
- async function getNodeCryptoModule(): Promise<NodeCryptoModule | null> {
59
- if (!usesNodeRuntimeModules()) {
60
- return null;
61
- }
62
- if (!nodeCryptoModulePromise) {
63
- nodeCryptoModulePromise = tryImportNodeModule('node:crypto')
64
- .then((module) => (isNodeCryptoModule(module) ? module : null))
65
- .catch(() => null);
66
- }
67
- return nodeCryptoModulePromise;
68
- }
69
-
70
- export interface IncrementalSha256 {
71
- update(chunk: Uint8Array): void;
72
- digestHex(): Promise<string>;
73
- }
74
-
75
- export async function createIncrementalSha256(): Promise<IncrementalSha256> {
76
- const bun = getBunRuntime();
77
- if (bun?.CryptoHasher) {
78
- const hasher = new bun.CryptoHasher('sha256');
79
- return {
80
- update(chunk) {
81
- hasher.update(chunk);
82
- },
83
- async digestHex() {
84
- return hasher.digest('hex');
85
- },
86
- };
87
- }
88
-
89
- const nodeCrypto = await getNodeCryptoModule();
90
- if (nodeCrypto?.createHash) {
91
- const hasher = nodeCrypto.createHash('sha256');
92
- return {
93
- update(chunk) {
94
- hasher.update(chunk);
95
- },
96
- async digestHex() {
97
- return hasher.digest('hex');
98
- },
99
- };
100
- }
101
-
102
- const chunks: Uint8Array[] = [];
103
- return {
104
- update(chunk) {
105
- if (chunk.length === 0) return;
106
- chunks.push(chunk.slice());
107
- },
108
- async digestHex() {
109
- return sha256Hex(concatByteChunks(chunks));
110
- },
111
- };
112
- }
113
-
114
- /**
115
- * Cross-runtime SHA-256 digest helper.
116
- *
117
- * Uses native Bun/Node implementations on server runtimes, with Web Crypto
118
- * fallback for browser and worker environments.
119
- */
120
- export async function sha256Hex(input: string | Uint8Array): Promise<string> {
121
- const payload = typeof input === 'string' ? textEncoder.encode(input) : input;
122
-
123
- const bun = getBunRuntime();
124
- if (bun?.CryptoHasher) {
125
- const hasher = new bun.CryptoHasher('sha256');
126
- hasher.update(payload);
127
- return hasher.digest('hex');
128
- }
129
-
130
- const nodeCrypto = await getNodeCryptoModule();
131
- if (nodeCrypto?.createHash) {
132
- return nodeCrypto.createHash('sha256').update(payload).digest('hex');
133
- }
134
-
135
- if (typeof crypto !== 'undefined' && crypto.subtle) {
136
- const digestBuffer = await crypto.subtle.digest(
137
- 'SHA-256',
138
- toDigestBufferSource(payload)
139
- );
140
- return toHex(new Uint8Array(digestBuffer));
141
- }
142
-
143
- throw new Error(
144
- 'Failed to create SHA-256 hash, no crypto implementation available'
145
- );
146
- }
package/src/utils/id.ts DELETED
@@ -1,34 +0,0 @@
1
- export function generateId(): string {
2
- type SyncularCryptoSource = {
3
- randomUUID?: () => string;
4
- getRandomValues?: <T extends ArrayBufferView>(array: T) => T;
5
- };
6
- const globalWithCrypto = globalThis as typeof globalThis & {
7
- crypto?: SyncularCryptoSource;
8
- };
9
- const cryptoObj: SyncularCryptoSource | undefined =
10
- typeof crypto !== 'undefined'
11
- ? (crypto as SyncularCryptoSource)
12
- : globalWithCrypto.crypto;
13
- if (cryptoObj && typeof cryptoObj.randomUUID === 'function') {
14
- return cryptoObj.randomUUID();
15
- }
16
-
17
- if (cryptoObj && typeof cryptoObj.getRandomValues === 'function') {
18
- const bytes = new Uint8Array(16);
19
- cryptoObj.getRandomValues(bytes);
20
- // UUID v4 variant bits.
21
- bytes[6] = (bytes[6]! & 0x0f) | 0x40;
22
- bytes[8] = (bytes[8]! & 0x3f) | 0x80;
23
- const hex = Array.from(bytes)
24
- .map((byte) => byte.toString(16).padStart(2, '0'))
25
- .join('');
26
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
27
- }
28
-
29
- return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
30
- }
31
-
32
- export function randomId(): string {
33
- return generateId();
34
- }