@anfenn/dync 1.0.28 → 1.0.30

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.
@@ -1,4 +1,4 @@
1
- import { a as SyncApi, k as SyncState } from '../types-9I2fmDbU.cjs';
1
+ import { a as SyncApi, k as SyncState } from '../types-DW42y281.cjs';
2
2
  import '../dexie-T9m1mP1h.cjs';
3
3
  import 'dexie';
4
4
  import '../types-CSbIAfu2.cjs';
@@ -1,4 +1,4 @@
1
- import { a as SyncApi, k as SyncState } from '../types-DLFva7gq.js';
1
+ import { a as SyncApi, k as SyncState } from '../types-n8Zge2zF.js';
2
2
  import '../dexie-BFPA0JU2.js';
3
3
  import 'dexie';
4
4
  import '../types-CSbIAfu2.js';
@@ -97,7 +97,7 @@ interface SyncOptions {
97
97
  interface DyncOptions<TStoreMap extends Record<string, any> = Record<string, any>> {
98
98
  databaseName: string;
99
99
  storageAdapter: StorageAdapter;
100
- sync: Partial<Record<keyof TStoreMap, ApiFunctions>> | BatchSync;
100
+ sync?: Partial<Record<keyof TStoreMap, ApiFunctions>> | BatchSync;
101
101
  options?: SyncOptions;
102
102
  }
103
103
  interface FirstLoadProgress {
@@ -97,7 +97,7 @@ interface SyncOptions {
97
97
  interface DyncOptions<TStoreMap extends Record<string, any> = Record<string, any>> {
98
98
  databaseName: string;
99
99
  storageAdapter: StorageAdapter;
100
- sync: Partial<Record<keyof TStoreMap, ApiFunctions>> | BatchSync;
100
+ sync?: Partial<Record<keyof TStoreMap, ApiFunctions>> | BatchSync;
101
101
  options?: SyncOptions;
102
102
  }
103
103
  interface FirstLoadProgress {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anfenn/dync",
3
- "version": "1.0.28",
3
+ "version": "1.0.30",
4
4
  "private": false,
5
5
  "description": "Write once, run IndexedDB & SQLite with sync anywhere - React, React Native, Expo, Capacitor, Electron & Node.js",
6
6
  "keywords": [
@@ -132,8 +132,8 @@
132
132
  "LICENSE"
133
133
  ],
134
134
  "scripts": {
135
- "build": "pnpm run format && pnpm run lint && pnpm exec tsc --noEmit && tsup",
136
- "build:all": "pnpm run build && pnpm --filter react-capacitor build",
135
+ "build": "pnpm format && pnpm lint && pnpm exec tsc --noEmit && tsup",
136
+ "build:all": "pnpm build && pnpm --filter react-capacitor build",
137
137
  "dev": "tsup --watch",
138
138
  "lint": "eslint . --fix \"src/**/*.{js,jsx,ts,tsx,json}\"",
139
139
  "format": "prettier --write \"**/*.{ts,tsx,md}\"",
@@ -142,7 +142,9 @@
142
142
  "test:all": "pnpm --filter tests test:all",
143
143
  "test:ci": "pnpm --filter tests test:ci",
144
144
  "test:browser:full": "pnpm --filter tests test:browser:full",
145
- "reinstall": "find . -name node_modules -type d -prune -exec rm -rf {} + && pnpm install"
145
+ "reinstall": "find . -name node_modules -type d -prune -exec rm -rf {} + && pnpm install",
146
+ "push": "bash -c '[ -n \"$1\" ] && git add . && git commit -m \"$1\" && git push' --",
147
+ "release": "bash -c '[ -n \"$1\" ] || { echo \"Usage: pnpm release <commit-message>\"; exit 1; } && pnpm build:all && pnpm test:all && pnpm test:browser:full && git add . && git commit -m \"$1\" && npm version patch && git push && git push --tags && npm publish' --"
146
148
  },
147
149
  "devDependencies": {
148
150
  "@capacitor-community/sqlite": "7.0.2",
@@ -6,7 +6,7 @@ import type { WithTransaction } from './types';
6
6
  export type EmitMutation = (event: MutationEvent) => void;
7
7
 
8
8
  /**
9
- * Wraps a table with mutation emission for reactive updates.
9
+ * Wraps a table with mutation emission for reactive updates and auto-generates _localId.
10
10
  * This allows useLiveQuery to work with any table.
11
11
  */
12
12
  export function wrapWithMutationEmitter<T>(table: StorageTable<T>, tableName: string, emitMutation: EmitMutation): void {
@@ -21,13 +21,15 @@ export function wrapWithMutationEmitter<T>(table: StorageTable<T>, tableName: st
21
21
  const rawClear = table.raw.clear;
22
22
 
23
23
  table.add = async (item: AddItem<T>) => {
24
- const result = await rawAdd(item as T);
24
+ const itemWithLocalId = { ...item, _localId: (item as any)._localId || createLocalId() } as T;
25
+ const result = await rawAdd(itemWithLocalId);
25
26
  emitMutation({ type: 'add', tableName, keys: [result] });
26
27
  return result;
27
28
  };
28
29
 
29
30
  table.put = async (item: T) => {
30
- const result = await rawPut(item);
31
+ const itemWithLocalId = { ...item, _localId: (item as any)._localId || createLocalId() } as T;
32
+ const result = await rawPut(itemWithLocalId);
31
33
  emitMutation({ type: 'update', tableName, keys: [result] });
32
34
  return result;
33
35
  };
@@ -46,7 +48,11 @@ export function wrapWithMutationEmitter<T>(table: StorageTable<T>, tableName: st
46
48
  };
47
49
 
48
50
  table.bulkAdd = async (items: AddItem<T>[]) => {
49
- const result = await rawBulkAdd(items as T[]);
51
+ const itemsWithLocalIds = items.map((item) => ({
52
+ ...item,
53
+ _localId: (item as any)._localId || createLocalId(),
54
+ })) as T[];
55
+ const result = await rawBulkAdd(itemsWithLocalIds);
50
56
  if (items.length > 0) {
51
57
  emitMutation({ type: 'add', tableName });
52
58
  }
@@ -54,7 +60,11 @@ export function wrapWithMutationEmitter<T>(table: StorageTable<T>, tableName: st
54
60
  };
55
61
 
56
62
  table.bulkPut = async (items: T[]) => {
57
- const result = await rawBulkPut(items);
63
+ const itemsWithLocalIds = items.map((item) => ({
64
+ ...item,
65
+ _localId: (item as any)._localId || createLocalId(),
66
+ })) as T[];
67
+ const result = await rawBulkPut(itemsWithLocalIds);
58
68
  if (items.length > 0) {
59
69
  emitMutation({ type: 'update', tableName });
60
70
  }
@@ -85,15 +85,17 @@ class DyncBase<_TStoreMap extends Record<string, any> = Record<string, any>> {
85
85
  constructor(config: DyncOptions<_TStoreMap>) {
86
86
  const { databaseName, storageAdapter, sync: syncConfig, options } = config;
87
87
 
88
- // Detect mode based on whether sync config has batch sync shape
89
- const isBatchMode = typeof (syncConfig as BatchSync).push === 'function' && typeof (syncConfig as BatchSync).pull === 'function';
90
-
91
- if (isBatchMode) {
92
- this.batchSync = syncConfig as BatchSync;
93
- this.syncedTables = new Set(this.batchSync.syncTables);
94
- } else {
95
- this.syncApis = syncConfig as Record<string, ApiFunctions>;
96
- this.syncedTables = new Set(Object.keys(this.syncApis));
88
+ if (syncConfig) {
89
+ // Detect mode based on whether sync config has batch sync shape
90
+ const isBatchMode = typeof syncConfig.push === 'function' && typeof syncConfig.pull === 'function';
91
+
92
+ if (isBatchMode) {
93
+ this.batchSync = syncConfig as BatchSync;
94
+ this.syncedTables = new Set(this.batchSync.syncTables);
95
+ } else {
96
+ this.syncApis = syncConfig as Record<string, ApiFunctions>;
97
+ this.syncedTables = new Set(Object.keys(this.syncApis));
98
+ }
97
99
  }
98
100
 
99
101
  this.adapter = storageAdapter;
@@ -158,6 +160,9 @@ class DyncBase<_TStoreMap extends Record<string, any> = Record<string, any>> {
158
160
  // Auto-inject sync fields for sync tables
159
161
  // Note: updated_at is indexed to support user queries like orderBy('updated_at')
160
162
  fullSchema[tableName] = `${LOCAL_PK}, &${SERVER_PK}, ${tableSchema}, ${UPDATED_AT}`;
163
+ } else {
164
+ // Auto-inject _localId as primary key for non-sync tables
165
+ fullSchema[tableName] = `${LOCAL_PK}, ${tableSchema}`;
161
166
  }
162
167
 
163
168
  self.logger.debug(
@@ -167,6 +172,9 @@ class DyncBase<_TStoreMap extends Record<string, any> = Record<string, any>> {
167
172
  if (isSyncTable) {
168
173
  // Auto-inject sync columns for structured schemas
169
174
  fullSchema[tableName] = self.injectSyncColumns(tableSchema);
175
+ } else {
176
+ // Auto-inject _localId column for non-sync structured schemas
177
+ fullSchema[tableName] = self.injectLocalIdColumn(tableSchema);
170
178
  }
171
179
 
172
180
  const schemaColumns = Object.keys((fullSchema[tableName] as SQLiteTableDefinition).columns ?? {}).join(', ');
@@ -350,6 +358,26 @@ class DyncBase<_TStoreMap extends Record<string, any> = Record<string, any>> {
350
358
  };
351
359
  }
352
360
 
361
+ private injectLocalIdColumn(schema: SQLiteTableDefinition): SQLiteTableDefinition {
362
+ const columns = schema.columns ?? {};
363
+
364
+ // Validate user hasn't defined reserved _localId column
365
+ if (columns[LOCAL_PK]) {
366
+ throw new Error(`Column '${LOCAL_PK}' is auto-injected and cannot be defined manually.`);
367
+ }
368
+
369
+ // Inject _localId column as primary key
370
+ const injectedColumns: Record<string, any> = {
371
+ ...columns,
372
+ [LOCAL_PK]: { type: 'TEXT' },
373
+ };
374
+
375
+ return {
376
+ ...schema,
377
+ columns: injectedColumns,
378
+ };
379
+ }
380
+
353
381
  private enhanceSyncTable<T>(table: StorageTable<T & SyncedRecord>, tableName: string): void {
354
382
  enhanceSyncTable({
355
383
  table,
package/src/types.ts CHANGED
@@ -125,7 +125,7 @@ export interface SyncOptions {
125
125
  export interface DyncOptions<TStoreMap extends Record<string, any> = Record<string, any>> {
126
126
  databaseName: string;
127
127
  storageAdapter: StorageAdapter;
128
- sync: Partial<Record<keyof TStoreMap, ApiFunctions>> | BatchSync;
128
+ sync?: Partial<Record<keyof TStoreMap, ApiFunctions>> | BatchSync;
129
129
  options?: SyncOptions;
130
130
  }
131
131