@firtoz/drizzle-utils 0.2.0 → 0.3.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @firtoz/drizzle-utils
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`46059a2`](https://github.com/firtoz/fullstack-toolkit/commit/46059a28bd0135414b9ed022ffe162a2292adae3) Thanks [@firtoz](https://github.com/firtoz)! - Add external sync support and collection truncate utilities:
8
+
9
+ - **`ExternalSyncEvent`** / **`ExternalSyncHandler`** types for receiving sync events from external sources (e.g., proxy server)
10
+ - **`CollectionUtils`** interface with `truncate()` method for clearing all data from a store
11
+ - **`handleTruncate`** added to `SyncBackend` interface
12
+ - **`pushExternalSync`** exposed on `SyncFunctionResult` for pushing external sync events to collections
13
+ - `createSyncFunction` now returns `utils` with truncate functionality
14
+
3
15
  ## 0.2.0
4
16
 
5
17
  ### Minor Changes
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @firtoz/drizzle-utils
2
2
 
3
- Shared utilities and types for Drizzle ORM-based packages. Provides type-safe table builders with automatic timestamp tracking, branded IDs, and common migration types.
3
+ Shared utilities and types for Drizzle ORM-based packages. Provides type-safe table builders with automatic timestamp tracking, branded IDs, common migration types, and collection sync utilities.
4
4
 
5
5
  > **⚠️ Early WIP Notice:** This package is in very early development and is **not production-ready**. It is TypeScript-only and may have breaking changes. While I (the maintainer) have limited time, I'm open to PRs for features, bug fixes, or additional support (like JS builds). Please feel free to try it out and contribute! See [CONTRIBUTING.md](../../CONTRIBUTING.md) for details.
6
6
 
@@ -203,6 +203,64 @@ Comprehensive types for database migrations:
203
203
  - `ViewDefinition` - Database view definition
204
204
  - `EnumDefinition` - Enum type definition
205
205
 
206
+ ### Collection Sync Utilities
207
+
208
+ Support for external sync and collection utilities:
209
+
210
+ #### External Sync Events
211
+
212
+ Push sync events from external sources (e.g., proxy servers) to collections:
213
+
214
+ ```typescript
215
+ import type { ExternalSyncEvent, ExternalSyncHandler } from "@firtoz/drizzle-utils";
216
+
217
+ // Receive sync events from a server
218
+ const handleSync: ExternalSyncHandler<Todo> = (event) => {
219
+ switch (event.type) {
220
+ case "insert":
221
+ // event.items contains new items
222
+ break;
223
+ case "update":
224
+ // event.items contains updated items
225
+ break;
226
+ case "delete":
227
+ // event.items contains deleted items
228
+ break;
229
+ case "truncate":
230
+ // All items should be cleared
231
+ break;
232
+ }
233
+ };
234
+
235
+ // Push events to a collection's reactive store
236
+ syncResult.pushExternalSync({ type: "insert", items: [newTodo] });
237
+ syncResult.pushExternalSync({ type: "truncate" });
238
+ ```
239
+
240
+ #### Collection Utils
241
+
242
+ The `SyncFunctionResult` includes utilities for common operations:
243
+
244
+ ```typescript
245
+ // Truncate (clear all data)
246
+ await collection.utils.truncate();
247
+ ```
248
+
249
+ #### SyncBackend Interface
250
+
251
+ For implementing custom backends, the `SyncBackend` interface includes:
252
+
253
+ ```typescript
254
+ interface SyncBackend<TTable> {
255
+ initialLoad: (write) => Promise<void>;
256
+ loadSubset: (options, write) => Promise<void>;
257
+ handleInsert: (mutations) => Promise<T[]>;
258
+ handleUpdate: (mutations) => Promise<T[]>;
259
+ handleDelete: (mutations) => Promise<void>;
260
+ handleTruncate?: () => Promise<void>; // Optional truncate support
261
+ }
262
+ ```
263
+
206
264
  ## Best Practices
207
265
 
208
266
  ### 1. Use syncableTable for Data Tables
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firtoz/drizzle-utils",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Shared utilities and types for Drizzle-based packages",
5
5
  "main": "./src/index.ts",
6
6
  "module": "./src/index.ts",
@@ -55,15 +55,15 @@
55
55
  "access": "public"
56
56
  },
57
57
  "peerDependencies": {
58
- "@tanstack/db": "^0.5.0",
58
+ "@tanstack/db": "^0.5.10",
59
59
  "drizzle-orm": "^0.44.7",
60
60
  "drizzle-valibot": "^0.4.2",
61
- "valibot": "^1.0.0"
61
+ "valibot": "^1.2.0"
62
62
  },
63
63
  "devDependencies": {
64
- "@tanstack/db": "^0.5.0",
64
+ "@tanstack/db": "^0.5.10",
65
65
  "drizzle-orm": "^0.44.7",
66
66
  "drizzle-valibot": "^0.4.2",
67
- "valibot": "^1.0.0"
67
+ "valibot": "^1.2.0"
68
68
  }
69
69
  }
@@ -160,6 +160,41 @@ export interface SyncBackend<TTable extends Table> {
160
160
  original: InferSchemaOutput<SelectSchema<TTable>>;
161
161
  }>,
162
162
  ) => Promise<void>;
163
+ /**
164
+ * Handle truncate (clear all data from store)
165
+ * Optional - if not provided, truncate util won't be available
166
+ */
167
+ handleTruncate?: () => Promise<void>;
168
+ }
169
+
170
+ /**
171
+ * External sync event for pushing changes from outside (e.g., from a proxy server)
172
+ */
173
+ export type ExternalSyncEvent<T> =
174
+ | { type: "insert"; items: T[] }
175
+ | { type: "update"; items: T[] }
176
+ | { type: "delete"; items: T[] }
177
+ | { type: "truncate" };
178
+
179
+ /**
180
+ * Handler for external sync events
181
+ */
182
+ export type ExternalSyncHandler<T> = (event: ExternalSyncEvent<T>) => void;
183
+
184
+ /**
185
+ * Collection utils that include truncate and external sync functionality
186
+ */
187
+ export interface CollectionUtils<T = unknown> {
188
+ /**
189
+ * Clear all data from the store (truncate).
190
+ * This clears the backend store and updates the local reactive store.
191
+ */
192
+ truncate: () => Promise<void>;
193
+ /**
194
+ * Push external sync events to the collection.
195
+ * Use this when receiving sync messages from a proxy server or other external source.
196
+ */
197
+ pushExternalSync: ExternalSyncHandler<T>;
163
198
  }
164
199
 
165
200
  /**
@@ -185,6 +220,10 @@ export type SyncFunctionResult<TTable extends Table> = {
185
220
  // biome-ignore lint/suspicious/noExplicitAny: Schema type parameter needs to be flexible
186
221
  any
187
222
  >["onDelete"];
223
+ /**
224
+ * Collection utilities including truncate and external sync
225
+ */
226
+ utils: CollectionUtils<InferSchemaOutput<SelectSchema<TTable>>>;
188
227
  };
189
228
 
190
229
  /**
@@ -194,8 +233,9 @@ export function createSyncFunction<TTable extends Table>(
194
233
  config: BaseSyncConfig<TTable>,
195
234
  backend: SyncBackend<TTable>,
196
235
  ): SyncFunctionResult<TTable> {
236
+ type ItemType = InferSchemaOutput<SelectSchema<TTable>>;
197
237
  type CollectionType = CollectionConfig<
198
- InferSchemaOutput<SelectSchema<TTable>>,
238
+ ItemType,
199
239
  string,
200
240
  // biome-ignore lint/suspicious/noExplicitAny: Schema type parameter needs to be flexible
201
241
  any
@@ -205,11 +245,25 @@ export function createSyncFunction<TTable extends Table>(
205
245
  let updateListener: CollectionType["onUpdate"];
206
246
  let deleteListener: CollectionType["onDelete"];
207
247
 
248
+ // Captured sync functions for external sync
249
+ let syncBegin: (() => void) | null = null;
250
+ let syncWrite:
251
+ | ((op: { type: "insert" | "update" | "delete"; value: ItemType }) => void)
252
+ | null = null;
253
+ let syncCommit: (() => void) | null = null;
254
+ let syncTruncate: (() => void) | null = null;
255
+
208
256
  const syncFn: SyncConfig<
209
257
  InferSchemaOutput<SelectSchema<TTable>>,
210
258
  string
211
259
  >["sync"] = (params) => {
212
- const { begin, write, commit, markReady } = params;
260
+ const { begin, write, commit, markReady, truncate } = params;
261
+
262
+ // Capture sync functions for external use
263
+ syncBegin = begin;
264
+ syncWrite = write;
265
+ syncCommit = commit;
266
+ syncTruncate = truncate;
213
267
 
214
268
  const initialSync = async () => {
215
269
  await config.readyPromise;
@@ -315,6 +369,69 @@ export function createSyncFunction<TTable extends Table>(
315
369
  } satisfies SyncConfigRes;
316
370
  };
317
371
 
372
+ // External sync handler - allows pushing sync events from outside (e.g., proxy server)
373
+ const pushExternalSync: ExternalSyncHandler<ItemType> = (event) => {
374
+ if (!syncBegin || !syncWrite || !syncCommit || !syncTruncate) {
375
+ if (config.debug) {
376
+ console.warn(
377
+ "[pushExternalSync] Sync functions not initialized yet - event will be dropped",
378
+ event,
379
+ );
380
+ }
381
+ return;
382
+ }
383
+
384
+ switch (event.type) {
385
+ case "insert":
386
+ syncBegin();
387
+ for (const item of event.items) {
388
+ syncWrite({ type: "insert", value: item });
389
+ }
390
+ syncCommit();
391
+ break;
392
+ case "update":
393
+ syncBegin();
394
+ for (const item of event.items) {
395
+ syncWrite({ type: "update", value: item });
396
+ }
397
+ syncCommit();
398
+ break;
399
+ case "delete":
400
+ syncBegin();
401
+ for (const item of event.items) {
402
+ syncWrite({ type: "delete", value: item });
403
+ }
404
+ syncCommit();
405
+ break;
406
+ case "truncate":
407
+ syncBegin();
408
+ syncTruncate();
409
+ syncCommit();
410
+ break;
411
+ }
412
+ };
413
+
414
+ // Create utils with truncate and external sync
415
+ const utils: CollectionUtils<ItemType> = {
416
+ truncate: async () => {
417
+ if (!backend.handleTruncate) {
418
+ throw new Error("Truncate not supported by this backend");
419
+ }
420
+ if (!syncBegin || !syncTruncate || !syncCommit) {
421
+ throw new Error(
422
+ "Sync functions not initialized - sync function may not have been called yet",
423
+ );
424
+ }
425
+ // Clear the backend store
426
+ await backend.handleTruncate();
427
+ // Update local reactive store (same pattern as insert/update/delete listeners)
428
+ syncBegin();
429
+ syncTruncate();
430
+ syncCommit();
431
+ },
432
+ pushExternalSync,
433
+ };
434
+
318
435
  return {
319
436
  sync: syncFn,
320
437
  onInsert: async (params) => {
@@ -341,6 +458,7 @@ export function createSyncFunction<TTable extends Table>(
341
458
  }
342
459
  return deleteListener(params);
343
460
  },
461
+ utils,
344
462
  };
345
463
  }
346
464
 
@@ -478,13 +596,17 @@ export function createCollectionConfig<
478
596
  any
479
597
  >["onDelete"];
480
598
  syncMode?: SyncMode;
481
- }): CollectionConfig<
482
- InferSchemaOutput<SelectSchema<TTable>>,
483
- string,
484
- // biome-ignore lint/suspicious/noExplicitAny: Schema type parameter needs to be flexible
485
- any
599
+ }): Omit<
600
+ CollectionConfig<
601
+ InferSchemaOutput<SelectSchema<TTable>>,
602
+ string,
603
+ // biome-ignore lint/suspicious/noExplicitAny: Schema type parameter needs to be flexible
604
+ any
605
+ >,
606
+ "utils"
486
607
  > & {
487
608
  schema: TSchema;
609
+ utils: CollectionUtils<InferSchemaOutput<SelectSchema<TTable>>>;
488
610
  } {
489
611
  return {
490
612
  schema: config.schema,
@@ -497,12 +619,7 @@ export function createCollectionConfig<
497
619
  onUpdate: config.onUpdate ?? config.syncResult.onUpdate,
498
620
  onDelete: config.onDelete ?? config.syncResult.onDelete,
499
621
  syncMode: config.syncMode,
500
- } as CollectionConfig<
501
- InferSchemaOutput<SelectSchema<TTable>>,
502
- string,
503
- // biome-ignore lint/suspicious/noExplicitAny: Schema type parameter needs to be flexible
504
- any
505
- > & {
506
- schema: TSchema;
622
+ // Include utils with truncate and pushExternalSync
623
+ utils: config.syncResult.utils,
507
624
  };
508
625
  }
package/src/index.ts CHANGED
@@ -26,6 +26,10 @@ export type {
26
26
  InferCollectionFromTable,
27
27
  BaseSyncConfig,
28
28
  SyncBackend,
29
+ SyncFunctionResult,
30
+ ExternalSyncEvent,
31
+ ExternalSyncHandler,
32
+ CollectionUtils,
29
33
  } from "./collection-utils";
30
34
 
31
35
  export {