@syncular/server 0.0.1-60

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 (211) hide show
  1. package/dist/blobs/adapters/database.d.ts +83 -0
  2. package/dist/blobs/adapters/database.d.ts.map +1 -0
  3. package/dist/blobs/adapters/database.js +180 -0
  4. package/dist/blobs/adapters/database.js.map +1 -0
  5. package/dist/blobs/adapters/s3.d.ts +82 -0
  6. package/dist/blobs/adapters/s3.d.ts.map +1 -0
  7. package/dist/blobs/adapters/s3.js +170 -0
  8. package/dist/blobs/adapters/s3.js.map +1 -0
  9. package/dist/blobs/index.d.ts +9 -0
  10. package/dist/blobs/index.d.ts.map +1 -0
  11. package/dist/blobs/index.js +9 -0
  12. package/dist/blobs/index.js.map +1 -0
  13. package/dist/blobs/manager.d.ts +195 -0
  14. package/dist/blobs/manager.d.ts.map +1 -0
  15. package/dist/blobs/manager.js +440 -0
  16. package/dist/blobs/manager.js.map +1 -0
  17. package/dist/blobs/migrate.d.ts +27 -0
  18. package/dist/blobs/migrate.d.ts.map +1 -0
  19. package/dist/blobs/migrate.js +119 -0
  20. package/dist/blobs/migrate.js.map +1 -0
  21. package/dist/blobs/types.d.ts +54 -0
  22. package/dist/blobs/types.d.ts.map +1 -0
  23. package/dist/blobs/types.js +5 -0
  24. package/dist/blobs/types.js.map +1 -0
  25. package/dist/clients.d.ts +14 -0
  26. package/dist/clients.d.ts.map +1 -0
  27. package/dist/clients.js +7 -0
  28. package/dist/clients.js.map +1 -0
  29. package/dist/compaction.d.ts +27 -0
  30. package/dist/compaction.d.ts.map +1 -0
  31. package/dist/compaction.js +49 -0
  32. package/dist/compaction.js.map +1 -0
  33. package/dist/dialect/index.d.ts +5 -0
  34. package/dist/dialect/index.d.ts.map +1 -0
  35. package/dist/dialect/index.js +5 -0
  36. package/dist/dialect/index.js.map +1 -0
  37. package/dist/dialect/types.d.ts +170 -0
  38. package/dist/dialect/types.d.ts.map +1 -0
  39. package/dist/dialect/types.js +8 -0
  40. package/dist/dialect/types.js.map +1 -0
  41. package/dist/helpers/conflict.d.ts +52 -0
  42. package/dist/helpers/conflict.d.ts.map +1 -0
  43. package/dist/helpers/conflict.js +49 -0
  44. package/dist/helpers/conflict.js.map +1 -0
  45. package/dist/helpers/emitted-change.d.ts +56 -0
  46. package/dist/helpers/emitted-change.d.ts.map +1 -0
  47. package/dist/helpers/emitted-change.js +46 -0
  48. package/dist/helpers/emitted-change.js.map +1 -0
  49. package/dist/helpers/index.d.ts +10 -0
  50. package/dist/helpers/index.d.ts.map +1 -0
  51. package/dist/helpers/index.js +10 -0
  52. package/dist/helpers/index.js.map +1 -0
  53. package/dist/helpers/paginate.d.ts +49 -0
  54. package/dist/helpers/paginate.d.ts.map +1 -0
  55. package/dist/helpers/paginate.js +54 -0
  56. package/dist/helpers/paginate.js.map +1 -0
  57. package/dist/helpers/scope-strings.d.ts +74 -0
  58. package/dist/helpers/scope-strings.d.ts.map +1 -0
  59. package/dist/helpers/scope-strings.js +82 -0
  60. package/dist/helpers/scope-strings.js.map +1 -0
  61. package/dist/index.d.ts +28 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +27 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/migrate.d.ts +14 -0
  66. package/dist/migrate.d.ts.map +1 -0
  67. package/dist/migrate.js +13 -0
  68. package/dist/migrate.js.map +1 -0
  69. package/dist/proxy/handler.d.ts +42 -0
  70. package/dist/proxy/handler.d.ts.map +1 -0
  71. package/dist/proxy/handler.js +99 -0
  72. package/dist/proxy/handler.js.map +1 -0
  73. package/dist/proxy/index.d.ts +9 -0
  74. package/dist/proxy/index.d.ts.map +1 -0
  75. package/dist/proxy/index.js +14 -0
  76. package/dist/proxy/index.js.map +1 -0
  77. package/dist/proxy/mutation-detector.d.ts +31 -0
  78. package/dist/proxy/mutation-detector.d.ts.map +1 -0
  79. package/dist/proxy/mutation-detector.js +61 -0
  80. package/dist/proxy/mutation-detector.js.map +1 -0
  81. package/dist/proxy/oplog.d.ts +30 -0
  82. package/dist/proxy/oplog.d.ts.map +1 -0
  83. package/dist/proxy/oplog.js +110 -0
  84. package/dist/proxy/oplog.js.map +1 -0
  85. package/dist/proxy/registry.d.ts +35 -0
  86. package/dist/proxy/registry.d.ts.map +1 -0
  87. package/dist/proxy/registry.js +49 -0
  88. package/dist/proxy/registry.js.map +1 -0
  89. package/dist/proxy/types.d.ts +44 -0
  90. package/dist/proxy/types.d.ts.map +1 -0
  91. package/dist/proxy/types.js +7 -0
  92. package/dist/proxy/types.js.map +1 -0
  93. package/dist/prune.d.ts +37 -0
  94. package/dist/prune.d.ts.map +1 -0
  95. package/dist/prune.js +112 -0
  96. package/dist/prune.js.map +1 -0
  97. package/dist/pull.d.ts +31 -0
  98. package/dist/pull.d.ts.map +1 -0
  99. package/dist/pull.js +414 -0
  100. package/dist/pull.js.map +1 -0
  101. package/dist/push.d.ts +33 -0
  102. package/dist/push.d.ts.map +1 -0
  103. package/dist/push.js +329 -0
  104. package/dist/push.js.map +1 -0
  105. package/dist/realtime/in-memory.d.ts +13 -0
  106. package/dist/realtime/in-memory.d.ts.map +1 -0
  107. package/dist/realtime/in-memory.js +28 -0
  108. package/dist/realtime/in-memory.js.map +1 -0
  109. package/dist/realtime/index.d.ts +3 -0
  110. package/dist/realtime/index.d.ts.map +1 -0
  111. package/dist/realtime/index.js +2 -0
  112. package/dist/realtime/index.js.map +1 -0
  113. package/dist/realtime/types.d.ts +50 -0
  114. package/dist/realtime/types.d.ts.map +1 -0
  115. package/dist/realtime/types.js +7 -0
  116. package/dist/realtime/types.js.map +1 -0
  117. package/dist/schema.d.ts +164 -0
  118. package/dist/schema.d.ts.map +1 -0
  119. package/dist/schema.js +10 -0
  120. package/dist/schema.js.map +1 -0
  121. package/dist/shapes/create-handler.d.ts +119 -0
  122. package/dist/shapes/create-handler.d.ts.map +1 -0
  123. package/dist/shapes/create-handler.js +327 -0
  124. package/dist/shapes/create-handler.js.map +1 -0
  125. package/dist/shapes/index.d.ts +4 -0
  126. package/dist/shapes/index.d.ts.map +1 -0
  127. package/dist/shapes/index.js +4 -0
  128. package/dist/shapes/index.js.map +1 -0
  129. package/dist/shapes/registry.d.ts +20 -0
  130. package/dist/shapes/registry.d.ts.map +1 -0
  131. package/dist/shapes/registry.js +88 -0
  132. package/dist/shapes/registry.js.map +1 -0
  133. package/dist/shapes/types.d.ts +204 -0
  134. package/dist/shapes/types.d.ts.map +1 -0
  135. package/dist/shapes/types.js +2 -0
  136. package/dist/shapes/types.js.map +1 -0
  137. package/dist/snapshot-chunks/adapters/s3.d.ts +63 -0
  138. package/dist/snapshot-chunks/adapters/s3.d.ts.map +1 -0
  139. package/dist/snapshot-chunks/adapters/s3.js +50 -0
  140. package/dist/snapshot-chunks/adapters/s3.js.map +1 -0
  141. package/dist/snapshot-chunks/db-metadata.d.ts +33 -0
  142. package/dist/snapshot-chunks/db-metadata.d.ts.map +1 -0
  143. package/dist/snapshot-chunks/db-metadata.js +169 -0
  144. package/dist/snapshot-chunks/db-metadata.js.map +1 -0
  145. package/dist/snapshot-chunks/index.d.ts +9 -0
  146. package/dist/snapshot-chunks/index.d.ts.map +1 -0
  147. package/dist/snapshot-chunks/index.js +9 -0
  148. package/dist/snapshot-chunks/index.js.map +1 -0
  149. package/dist/snapshot-chunks/types.d.ts +65 -0
  150. package/dist/snapshot-chunks/types.d.ts.map +1 -0
  151. package/dist/snapshot-chunks/types.js +8 -0
  152. package/dist/snapshot-chunks/types.js.map +1 -0
  153. package/dist/snapshot-chunks.d.ts +59 -0
  154. package/dist/snapshot-chunks.d.ts.map +1 -0
  155. package/dist/snapshot-chunks.js +202 -0
  156. package/dist/snapshot-chunks.js.map +1 -0
  157. package/dist/stats.d.ts +19 -0
  158. package/dist/stats.d.ts.map +1 -0
  159. package/dist/stats.js +57 -0
  160. package/dist/stats.js.map +1 -0
  161. package/dist/subscriptions/index.d.ts +2 -0
  162. package/dist/subscriptions/index.d.ts.map +1 -0
  163. package/dist/subscriptions/index.js +2 -0
  164. package/dist/subscriptions/index.js.map +1 -0
  165. package/dist/subscriptions/resolve.d.ts +35 -0
  166. package/dist/subscriptions/resolve.d.ts.map +1 -0
  167. package/dist/subscriptions/resolve.js +134 -0
  168. package/dist/subscriptions/resolve.js.map +1 -0
  169. package/package.json +80 -0
  170. package/src/blobs/adapters/database.ts +290 -0
  171. package/src/blobs/adapters/s3.ts +271 -0
  172. package/src/blobs/index.ts +9 -0
  173. package/src/blobs/manager.ts +600 -0
  174. package/src/blobs/migrate.ts +150 -0
  175. package/src/blobs/types.ts +70 -0
  176. package/src/clients.ts +21 -0
  177. package/src/compaction.ts +77 -0
  178. package/src/dialect/index.ts +5 -0
  179. package/src/dialect/types.ts +222 -0
  180. package/src/helpers/conflict.ts +64 -0
  181. package/src/helpers/emitted-change.ts +69 -0
  182. package/src/helpers/index.ts +10 -0
  183. package/src/helpers/paginate.ts +82 -0
  184. package/src/helpers/scope-strings.ts +101 -0
  185. package/src/index.ts +28 -0
  186. package/src/migrate.ts +20 -0
  187. package/src/proxy/handler.ts +152 -0
  188. package/src/proxy/index.ts +18 -0
  189. package/src/proxy/mutation-detector.ts +83 -0
  190. package/src/proxy/oplog.ts +144 -0
  191. package/src/proxy/registry.ts +56 -0
  192. package/src/proxy/types.ts +46 -0
  193. package/src/prune.ts +200 -0
  194. package/src/pull.ts +551 -0
  195. package/src/push.ts +457 -0
  196. package/src/realtime/in-memory.ts +33 -0
  197. package/src/realtime/index.ts +5 -0
  198. package/src/realtime/types.ts +55 -0
  199. package/src/schema.ts +172 -0
  200. package/src/shapes/create-handler.ts +590 -0
  201. package/src/shapes/index.ts +3 -0
  202. package/src/shapes/registry.ts +109 -0
  203. package/src/shapes/types.ts +267 -0
  204. package/src/snapshot-chunks/adapters/s3.ts +68 -0
  205. package/src/snapshot-chunks/db-metadata.ts +238 -0
  206. package/src/snapshot-chunks/index.ts +9 -0
  207. package/src/snapshot-chunks/types.ts +79 -0
  208. package/src/snapshot-chunks.ts +301 -0
  209. package/src/stats.ts +104 -0
  210. package/src/subscriptions/index.ts +1 -0
  211. package/src/subscriptions/resolve.ts +185 -0
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Server-side blob manager.
3
+ *
4
+ * Orchestrates blob uploads and downloads using a pluggable storage adapter.
5
+ * Handles metadata tracking, upload verification, and garbage collection.
6
+ */
7
+ import type { BlobStorageAdapter } from '@syncular/core';
8
+ import { type Kysely } from 'kysely';
9
+ import type { SyncBlobUploadsDb } from './types';
10
+ export interface BlobManagerOptions<DB extends SyncBlobUploadsDb = SyncBlobUploadsDb> {
11
+ /** Database instance for tracking uploads */
12
+ db: Kysely<DB>;
13
+ /** Storage adapter (S3, R2, database, etc.) */
14
+ adapter: BlobStorageAdapter;
15
+ /** Default presigned URL expiration in seconds. Default: 3600 (1 hour) */
16
+ defaultExpiresIn?: number;
17
+ /** How long incomplete uploads are kept before cleanup. Default: 86400 (24 hours) */
18
+ uploadTtlSeconds?: number;
19
+ }
20
+ export interface InitiateUploadOptions {
21
+ hash: string;
22
+ size: number;
23
+ mimeType: string;
24
+ actorId: string;
25
+ }
26
+ export interface GetDownloadUrlOptions {
27
+ hash: string;
28
+ /** Optional: verify actor has access to this blob via a scope check */
29
+ actorId?: string;
30
+ }
31
+ /**
32
+ * Create a blob manager for handling server-side blob operations.
33
+ */
34
+ export declare function createBlobManager<DB extends SyncBlobUploadsDb>(options: BlobManagerOptions<DB>): {
35
+ /**
36
+ * Initiate a blob upload.
37
+ *
38
+ * Checks for deduplication and returns a presigned URL for uploading.
39
+ */
40
+ initiateUpload(opts: InitiateUploadOptions): Promise<{
41
+ exists: boolean;
42
+ uploadId?: string | undefined;
43
+ uploadUrl?: string | undefined;
44
+ uploadMethod?: "POST" | "PUT" | undefined;
45
+ uploadHeaders?: Record<string, string> | undefined;
46
+ chunkSize?: number | undefined;
47
+ }>;
48
+ /**
49
+ * Complete a blob upload.
50
+ *
51
+ * Verifies the blob exists in storage and marks the upload as complete.
52
+ */
53
+ completeUpload(hash: string): Promise<{
54
+ ok: boolean;
55
+ metadata?: {
56
+ hash: string;
57
+ size: number;
58
+ mimeType: string;
59
+ createdAt: string;
60
+ expiresAt?: string | undefined;
61
+ uploadComplete: boolean;
62
+ } | undefined;
63
+ error?: string | undefined;
64
+ }>;
65
+ /**
66
+ * Get a presigned download URL for a blob.
67
+ */
68
+ getDownloadUrl(opts: GetDownloadUrlOptions): Promise<{
69
+ url: string;
70
+ expiresAt: string;
71
+ metadata: {
72
+ hash: string;
73
+ size: number;
74
+ mimeType: string;
75
+ createdAt: string;
76
+ expiresAt?: string | undefined;
77
+ uploadComplete: boolean;
78
+ };
79
+ }>;
80
+ /**
81
+ * Get blob metadata without generating a download URL.
82
+ */
83
+ getMetadata(hash: string): Promise<{
84
+ hash: string;
85
+ size: number;
86
+ mimeType: string;
87
+ createdAt: string;
88
+ expiresAt?: string | undefined;
89
+ uploadComplete: boolean;
90
+ } | null>;
91
+ /**
92
+ * Check if a blob exists and is complete.
93
+ */
94
+ exists(hash: string): Promise<boolean>;
95
+ /**
96
+ * Clean up expired/orphaned uploads.
97
+ *
98
+ * Deletes upload records (and optionally storage) for:
99
+ * - Pending uploads that have expired
100
+ * - Completed uploads with no references (if refCheck provided)
101
+ */
102
+ cleanup(options?: {
103
+ /** Check if a blob hash is referenced by any row */
104
+ isReferenced?: ((hash: string) => Promise<boolean>) | undefined;
105
+ /** Delete from storage too (not just tracking table) */
106
+ deleteFromStorage?: boolean | undefined;
107
+ } | undefined): Promise<{
108
+ deleted: number;
109
+ }>;
110
+ /** The underlying storage adapter */
111
+ adapter: BlobStorageAdapter;
112
+ };
113
+ export type BlobManager = ReturnType<typeof createBlobManager>;
114
+ export interface BlobCleanupSchedulerOptions {
115
+ /** Blob manager instance */
116
+ blobManager: BlobManager;
117
+ /** Interval between cleanup runs in milliseconds. Default: 3600000 (1 hour) */
118
+ intervalMs?: number;
119
+ /** Delete from storage too (not just tracking table). Default: true */
120
+ deleteFromStorage?: boolean;
121
+ /** Optional: Check if a blob hash is referenced by any row */
122
+ isReferenced?: (hash: string) => Promise<boolean>;
123
+ /** Optional: Called after each cleanup run */
124
+ onCleanup?: (result: {
125
+ deleted: number;
126
+ error?: Error;
127
+ }) => void;
128
+ }
129
+ /**
130
+ * Create a garbage collection scheduler for blob storage.
131
+ *
132
+ * Periodically runs cleanup to remove:
133
+ * - Expired pending uploads
134
+ * - Unreferenced blobs (if isReferenced callback provided)
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * const scheduler = createBlobCleanupScheduler({
139
+ * blobManager,
140
+ * intervalMs: 60 * 60 * 1000, // 1 hour
141
+ * deleteFromStorage: true,
142
+ * isReferenced: async (hash) => {
143
+ * const row = await db.selectFrom('my_table')
144
+ * .select('id')
145
+ * .where('blob_hash', '=', hash)
146
+ * .executeTakeFirst();
147
+ * return !!row;
148
+ * },
149
+ * onCleanup: (result) => {
150
+ * console.log(`Cleanup complete: ${result.deleted} blobs removed`);
151
+ * },
152
+ * });
153
+ *
154
+ * // Start the scheduler
155
+ * scheduler.start();
156
+ *
157
+ * // Stop when shutting down
158
+ * scheduler.stop();
159
+ * ```
160
+ */
161
+ export declare function createBlobCleanupScheduler(options: BlobCleanupSchedulerOptions): {
162
+ /**
163
+ * Start the cleanup scheduler.
164
+ * Optionally runs an immediate cleanup before starting the interval.
165
+ */
166
+ start(options?: {
167
+ immediate?: boolean | undefined;
168
+ } | undefined): void;
169
+ /**
170
+ * Stop the cleanup scheduler.
171
+ */
172
+ stop(): void;
173
+ /**
174
+ * Run a single cleanup manually.
175
+ */
176
+ runOnce(): Promise<{
177
+ deleted: number;
178
+ error?: Error | undefined;
179
+ }>;
180
+ /**
181
+ * Check if the scheduler is currently active.
182
+ */
183
+ readonly active: boolean;
184
+ /**
185
+ * Check if a cleanup is currently in progress.
186
+ */
187
+ readonly running: boolean;
188
+ };
189
+ export declare class BlobValidationError extends Error {
190
+ constructor(message: string);
191
+ }
192
+ export declare class BlobNotFoundError extends Error {
193
+ constructor(message: string);
194
+ }
195
+ //# sourceMappingURL=manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/blobs/manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAEV,kBAAkB,EAGnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,KAAK,MAAM,EAAO,MAAM,QAAQ,CAAC;AAC1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAMjD,MAAM,WAAW,kBAAkB,CACjC,EAAE,SAAS,iBAAiB,GAAG,iBAAiB;IAEhD,6CAA6C;IAC7C,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACf,+CAA+C;IAC/C,OAAO,EAAE,kBAAkB,CAAC;IAC5B,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,iBAAiB,EAC5D,OAAO,EAAE,kBAAkB,CAAC,EAAE,CAAC;IAU7B;;;;OAIG;;;;;;;;;IA6GH;;;;OAIG;;;;;;;;;;;;;IA6EH;;OAEG;;;;;;;;;;;;;IAqDH;;OAEG;;;;;;;;;IAkCH;;OAEG;;IAcH;;;;;;OAMG;;QAED,oDAAoD;;QAEpD,wDAAwD;;;;;IAiE1D,qCAAqC;;EAGxC;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAM/D,MAAM,WAAW,2BAA2B;IAC1C,4BAA4B;IAC5B,WAAW,EAAE,WAAW,CAAC;IACzB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,8DAA8D;IAC9D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,8CAA8C;IAC9C,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,KAAK,IAAI,CAAC;CAClE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,2BAA2B;IAsClC;;;OAGG;;;;IAeH;;OAEG;;IAQH;;OAEG;;;;;IAKH;;OAEG;;IAKH;;OAEG;;EAKN;AAMD,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF"}
@@ -0,0 +1,440 @@
1
+ /**
2
+ * Server-side blob manager.
3
+ *
4
+ * Orchestrates blob uploads and downloads using a pluggable storage adapter.
5
+ * Handles metadata tracking, upload verification, and garbage collection.
6
+ */
7
+ import { parseBlobHash } from '@syncular/core';
8
+ import { sql } from 'kysely';
9
+ /**
10
+ * Create a blob manager for handling server-side blob operations.
11
+ */
12
+ export function createBlobManager(options) {
13
+ const { db, adapter, defaultExpiresIn = 3600, uploadTtlSeconds = 86400, } = options;
14
+ return {
15
+ /**
16
+ * Initiate a blob upload.
17
+ *
18
+ * Checks for deduplication and returns a presigned URL for uploading.
19
+ */
20
+ async initiateUpload(opts) {
21
+ const { hash, size, mimeType, actorId } = opts;
22
+ // Validate hash format
23
+ if (!parseBlobHash(hash)) {
24
+ throw new BlobValidationError('Invalid blob hash format');
25
+ }
26
+ // Check if blob already exists (deduplication)
27
+ const exists = await adapter.exists(hash);
28
+ if (exists) {
29
+ // Also check if we have a complete upload record
30
+ const existingResult = await sql `
31
+ select status
32
+ from ${sql.table('sync_blob_uploads')}
33
+ where hash = ${hash} and status = 'complete'
34
+ limit 1
35
+ `.execute(db);
36
+ const existing = existingResult.rows[0];
37
+ if (existing) {
38
+ return { exists: true };
39
+ }
40
+ // Blob exists in storage but we don't have a record - create one
41
+ const existsExpiresAt = new Date(Date.now() + uploadTtlSeconds * 1000).toISOString();
42
+ const existsCompletedAt = new Date().toISOString();
43
+ await sql `
44
+ insert into ${sql.table('sync_blob_uploads')} (
45
+ hash,
46
+ size,
47
+ mime_type,
48
+ status,
49
+ actor_id,
50
+ expires_at,
51
+ completed_at
52
+ )
53
+ values (
54
+ ${hash},
55
+ ${size},
56
+ ${mimeType},
57
+ 'complete',
58
+ ${actorId},
59
+ ${existsExpiresAt},
60
+ ${existsCompletedAt}
61
+ )
62
+ on conflict (hash) do nothing
63
+ `.execute(db);
64
+ return { exists: true };
65
+ }
66
+ // Create pending upload record
67
+ const expiresAt = new Date(Date.now() + uploadTtlSeconds * 1000).toISOString();
68
+ await sql `
69
+ insert into ${sql.table('sync_blob_uploads')} (
70
+ hash,
71
+ size,
72
+ mime_type,
73
+ status,
74
+ actor_id,
75
+ expires_at,
76
+ completed_at
77
+ )
78
+ values (
79
+ ${hash},
80
+ ${size},
81
+ ${mimeType},
82
+ 'pending',
83
+ ${actorId},
84
+ ${expiresAt},
85
+ ${null}
86
+ )
87
+ on conflict (hash)
88
+ do update set
89
+ size = ${size},
90
+ mime_type = ${mimeType},
91
+ status = 'pending',
92
+ actor_id = ${actorId},
93
+ expires_at = ${expiresAt},
94
+ completed_at = ${null}
95
+ `.execute(db);
96
+ // Generate presigned upload URL
97
+ const signed = await adapter.signUpload({
98
+ hash,
99
+ size,
100
+ mimeType,
101
+ expiresIn: defaultExpiresIn,
102
+ });
103
+ return {
104
+ exists: false,
105
+ uploadId: hash, // Use hash as upload ID
106
+ uploadUrl: signed.url,
107
+ uploadMethod: signed.method,
108
+ uploadHeaders: signed.headers,
109
+ };
110
+ },
111
+ /**
112
+ * Complete a blob upload.
113
+ *
114
+ * Verifies the blob exists in storage and marks the upload as complete.
115
+ */
116
+ async completeUpload(hash) {
117
+ // Validate hash format
118
+ if (!parseBlobHash(hash)) {
119
+ return { ok: false, error: 'Invalid blob hash format' };
120
+ }
121
+ // Check upload record exists
122
+ const uploadResult = await sql `
123
+ select hash, size, mime_type, status, created_at
124
+ from ${sql.table('sync_blob_uploads')}
125
+ where hash = ${hash}
126
+ limit 1
127
+ `.execute(db);
128
+ const upload = uploadResult.rows[0];
129
+ if (!upload) {
130
+ return { ok: false, error: 'Upload not found' };
131
+ }
132
+ if (upload.status === 'complete') {
133
+ // Already complete - return metadata
134
+ return {
135
+ ok: true,
136
+ metadata: {
137
+ hash: upload.hash,
138
+ size: upload.size,
139
+ mimeType: upload.mime_type,
140
+ createdAt: upload.created_at,
141
+ uploadComplete: true,
142
+ },
143
+ };
144
+ }
145
+ // Verify blob exists in storage
146
+ const exists = await adapter.exists(hash);
147
+ if (!exists) {
148
+ return { ok: false, error: 'Blob not found in storage' };
149
+ }
150
+ // Optionally verify size matches
151
+ if (adapter.getMetadata) {
152
+ const meta = await adapter.getMetadata(hash);
153
+ if (meta && meta.size !== upload.size) {
154
+ return {
155
+ ok: false,
156
+ error: `Size mismatch: expected ${upload.size}, got ${meta.size}`,
157
+ };
158
+ }
159
+ }
160
+ // Mark upload as complete
161
+ const completedAt = new Date().toISOString();
162
+ await sql `
163
+ update ${sql.table('sync_blob_uploads')}
164
+ set status = 'complete', completed_at = ${completedAt}
165
+ where hash = ${hash}
166
+ `.execute(db);
167
+ return {
168
+ ok: true,
169
+ metadata: {
170
+ hash: upload.hash,
171
+ size: upload.size,
172
+ mimeType: upload.mime_type,
173
+ createdAt: upload.created_at,
174
+ uploadComplete: true,
175
+ },
176
+ };
177
+ },
178
+ /**
179
+ * Get a presigned download URL for a blob.
180
+ */
181
+ async getDownloadUrl(opts) {
182
+ const { hash } = opts;
183
+ // Validate hash format
184
+ if (!parseBlobHash(hash)) {
185
+ throw new BlobNotFoundError('Invalid blob hash format');
186
+ }
187
+ // Get upload record (must be complete)
188
+ const uploadResult = await sql `
189
+ select hash, size, mime_type, status, created_at
190
+ from ${sql.table('sync_blob_uploads')}
191
+ where hash = ${hash} and status = 'complete'
192
+ limit 1
193
+ `.execute(db);
194
+ const upload = uploadResult.rows[0];
195
+ if (!upload) {
196
+ throw new BlobNotFoundError('Blob not found');
197
+ }
198
+ // Generate presigned download URL
199
+ const url = await adapter.signDownload({
200
+ hash,
201
+ expiresIn: defaultExpiresIn,
202
+ });
203
+ const expiresAt = new Date(Date.now() + defaultExpiresIn * 1000).toISOString();
204
+ return {
205
+ url,
206
+ expiresAt,
207
+ metadata: {
208
+ hash: upload.hash,
209
+ size: upload.size,
210
+ mimeType: upload.mime_type,
211
+ createdAt: upload.created_at,
212
+ uploadComplete: true,
213
+ },
214
+ };
215
+ },
216
+ /**
217
+ * Get blob metadata without generating a download URL.
218
+ */
219
+ async getMetadata(hash) {
220
+ // Validate hash format
221
+ if (!parseBlobHash(hash)) {
222
+ return null;
223
+ }
224
+ const uploadResult = await sql `
225
+ select hash, size, mime_type, status, created_at
226
+ from ${sql.table('sync_blob_uploads')}
227
+ where hash = ${hash} and status = 'complete'
228
+ limit 1
229
+ `.execute(db);
230
+ const upload = uploadResult.rows[0];
231
+ if (!upload) {
232
+ return null;
233
+ }
234
+ return {
235
+ hash: upload.hash,
236
+ size: upload.size,
237
+ mimeType: upload.mime_type,
238
+ createdAt: upload.created_at,
239
+ uploadComplete: true,
240
+ };
241
+ },
242
+ /**
243
+ * Check if a blob exists and is complete.
244
+ */
245
+ async exists(hash) {
246
+ if (!parseBlobHash(hash))
247
+ return false;
248
+ const rowResult = await sql `
249
+ select hash
250
+ from ${sql.table('sync_blob_uploads')}
251
+ where hash = ${hash} and status = 'complete'
252
+ limit 1
253
+ `.execute(db);
254
+ return rowResult.rows.length > 0;
255
+ },
256
+ /**
257
+ * Clean up expired/orphaned uploads.
258
+ *
259
+ * Deletes upload records (and optionally storage) for:
260
+ * - Pending uploads that have expired
261
+ * - Completed uploads with no references (if refCheck provided)
262
+ */
263
+ async cleanup(options) {
264
+ const now = new Date().toISOString();
265
+ // Find expired pending uploads
266
+ const expiredResult = await sql `
267
+ select hash
268
+ from ${sql.table('sync_blob_uploads')}
269
+ where status = 'pending' and expires_at < ${now}
270
+ `.execute(db);
271
+ const expired = expiredResult.rows;
272
+ let deleted = 0;
273
+ for (const row of expired) {
274
+ if (options?.deleteFromStorage) {
275
+ try {
276
+ await adapter.delete(row.hash);
277
+ }
278
+ catch {
279
+ // Ignore storage errors during cleanup
280
+ }
281
+ }
282
+ await sql `
283
+ delete from ${sql.table('sync_blob_uploads')}
284
+ where hash = ${row.hash}
285
+ `.execute(db);
286
+ deleted++;
287
+ }
288
+ // If reference check provided, also clean up unreferenced complete uploads
289
+ if (options?.isReferenced) {
290
+ const completeResult = await sql `
291
+ select hash
292
+ from ${sql.table('sync_blob_uploads')}
293
+ where status = 'complete'
294
+ `.execute(db);
295
+ const complete = completeResult.rows;
296
+ for (const row of complete) {
297
+ const referenced = await options.isReferenced(row.hash);
298
+ if (!referenced) {
299
+ if (options?.deleteFromStorage) {
300
+ try {
301
+ await adapter.delete(row.hash);
302
+ }
303
+ catch {
304
+ // Ignore storage errors during cleanup
305
+ }
306
+ }
307
+ await sql `
308
+ delete from ${sql.table('sync_blob_uploads')}
309
+ where hash = ${row.hash}
310
+ `.execute(db);
311
+ deleted++;
312
+ }
313
+ }
314
+ }
315
+ return { deleted };
316
+ },
317
+ /** The underlying storage adapter */
318
+ adapter,
319
+ };
320
+ }
321
+ /**
322
+ * Create a garbage collection scheduler for blob storage.
323
+ *
324
+ * Periodically runs cleanup to remove:
325
+ * - Expired pending uploads
326
+ * - Unreferenced blobs (if isReferenced callback provided)
327
+ *
328
+ * @example
329
+ * ```typescript
330
+ * const scheduler = createBlobCleanupScheduler({
331
+ * blobManager,
332
+ * intervalMs: 60 * 60 * 1000, // 1 hour
333
+ * deleteFromStorage: true,
334
+ * isReferenced: async (hash) => {
335
+ * const row = await db.selectFrom('my_table')
336
+ * .select('id')
337
+ * .where('blob_hash', '=', hash)
338
+ * .executeTakeFirst();
339
+ * return !!row;
340
+ * },
341
+ * onCleanup: (result) => {
342
+ * console.log(`Cleanup complete: ${result.deleted} blobs removed`);
343
+ * },
344
+ * });
345
+ *
346
+ * // Start the scheduler
347
+ * scheduler.start();
348
+ *
349
+ * // Stop when shutting down
350
+ * scheduler.stop();
351
+ * ```
352
+ */
353
+ export function createBlobCleanupScheduler(options) {
354
+ const { blobManager, intervalMs = 3600000, // 1 hour
355
+ deleteFromStorage = true, isReferenced, onCleanup, } = options;
356
+ let intervalId = null;
357
+ let isRunning = false;
358
+ const runCleanup = async () => {
359
+ if (isRunning) {
360
+ return { deleted: 0 };
361
+ }
362
+ isRunning = true;
363
+ try {
364
+ const result = await blobManager.cleanup({
365
+ deleteFromStorage,
366
+ isReferenced,
367
+ });
368
+ onCleanup?.({ deleted: result.deleted });
369
+ return { deleted: result.deleted };
370
+ }
371
+ catch (err) {
372
+ const error = err instanceof Error ? err : new Error(String(err));
373
+ onCleanup?.({ deleted: 0, error });
374
+ return { deleted: 0, error };
375
+ }
376
+ finally {
377
+ isRunning = false;
378
+ }
379
+ };
380
+ return {
381
+ /**
382
+ * Start the cleanup scheduler.
383
+ * Optionally runs an immediate cleanup before starting the interval.
384
+ */
385
+ start(options) {
386
+ if (intervalId) {
387
+ return; // Already running
388
+ }
389
+ if (options?.immediate) {
390
+ void runCleanup();
391
+ }
392
+ intervalId = setInterval(() => {
393
+ void runCleanup();
394
+ }, intervalMs);
395
+ },
396
+ /**
397
+ * Stop the cleanup scheduler.
398
+ */
399
+ stop() {
400
+ if (intervalId) {
401
+ clearInterval(intervalId);
402
+ intervalId = null;
403
+ }
404
+ },
405
+ /**
406
+ * Run a single cleanup manually.
407
+ */
408
+ async runOnce() {
409
+ return runCleanup();
410
+ },
411
+ /**
412
+ * Check if the scheduler is currently active.
413
+ */
414
+ get active() {
415
+ return intervalId !== null;
416
+ },
417
+ /**
418
+ * Check if a cleanup is currently in progress.
419
+ */
420
+ get running() {
421
+ return isRunning;
422
+ },
423
+ };
424
+ }
425
+ // ============================================================================
426
+ // Errors
427
+ // ============================================================================
428
+ export class BlobValidationError extends Error {
429
+ constructor(message) {
430
+ super(message);
431
+ this.name = 'BlobValidationError';
432
+ }
433
+ }
434
+ export class BlobNotFoundError extends Error {
435
+ constructor(message) {
436
+ super(message);
437
+ this.name = 'BlobNotFoundError';
438
+ }
439
+ }
440
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/blobs/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @syncular/server - Blob storage migrations
3
+ *
4
+ * These migrations are separate from core sync migrations because
5
+ * blob storage is optional and may use external storage (S3/R2).
6
+ */
7
+ import type { Kysely } from 'kysely';
8
+ import type { SyncBlobDb } from './types';
9
+ /**
10
+ * Ensures the blob storage schema exists (for database adapter).
11
+ * Only needed if using the database blob storage adapter.
12
+ *
13
+ * For PostgreSQL.
14
+ */
15
+ export declare function ensureBlobStorageSchemaPostgres<DB extends SyncBlobDb>(db: Kysely<DB>): Promise<void>;
16
+ /**
17
+ * Ensures the blob storage schema exists (for database adapter).
18
+ * Only needed if using the database blob storage adapter.
19
+ *
20
+ * For SQLite.
21
+ */
22
+ export declare function ensureBlobStorageSchemaSqlite<DB extends SyncBlobDb>(db: Kysely<DB>): Promise<void>;
23
+ /**
24
+ * Drops the blob schema from the database.
25
+ */
26
+ export declare function dropBlobSchema<DB extends SyncBlobDb>(db: Kysely<DB>): Promise<void>;
27
+ //# sourceMappingURL=migrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/blobs/migrate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,KAAK,EAAE,UAAU,EAAqB,MAAM,SAAS,CAAC;AAgF7D;;;;;GAKG;AACH,wBAAsB,+BAA+B,CAAC,EAAE,SAAS,UAAU,EACzE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,GACb,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;;;;GAKG;AACH,wBAAsB,6BAA6B,CAAC,EAAE,SAAS,UAAU,EACvE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,GACb,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,EAAE,SAAS,UAAU,EACxD,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,GACb,OAAO,CAAC,IAAI,CAAC,CAGf"}