Skip to content

Commit 2e3091e

Browse files
authored
Merge pull request #90 from powersync-ja/remove-lodash
Remove lodash (mostly)
2 parents c447e26 + 77b3078 commit 2e3091e

File tree

11 files changed

+78
-72
lines changed

11 files changed

+78
-72
lines changed

.changeset/mean-numbers-remember.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@journeyapps/powersync-sdk-common": patch
3+
"@journeyapps/powersync-sdk-web": patch
4+
---
5+
6+
Reduce JS bundle size

packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1-
import _ from 'lodash';
21
import { Mutex } from 'async-mutex';
2+
import { EventIterator } from 'event-iterator';
33
import Logger, { ILogger } from 'js-logger';
4+
import throttle from 'lodash/throttle';
45
import { DBAdapter, QueryResult, Transaction, isBatchedUpdateNotification } from '../db/DBAdapter';
5-
import { Schema } from '../db/schema/Schema';
66
import { SyncStatus } from '../db/crud/SyncStatus';
77
import { UploadQueueStats } from '../db/crud/UploadQueueStatus';
8+
import { Schema } from '../db/schema/Schema';
9+
import { BaseObserver } from '../utils/BaseObserver';
10+
import { mutexRunExclusive } from '../utils/mutex';
11+
import { quoteIdentifier } from '../utils/strings';
812
import { PowerSyncBackendConnector } from './connection/PowerSyncBackendConnector';
13+
import { BucketStorageAdapter, PSInternalTable } from './sync/bucket/BucketStorageAdapter';
14+
import { CrudBatch } from './sync/bucket/CrudBatch';
15+
import { CrudEntry, CrudEntryJSON } from './sync/bucket/CrudEntry';
16+
import { CrudTransaction } from './sync/bucket/CrudTransaction';
917
import {
1018
AbstractStreamingSyncImplementation,
1119
DEFAULT_CRUD_UPLOAD_THROTTLE_MS,
1220
StreamingSyncImplementationListener,
1321
StreamingSyncImplementation
1422
} from './sync/stream/AbstractStreamingSyncImplementation';
15-
import { CrudBatch } from './sync/bucket/CrudBatch';
16-
import { CrudTransaction } from './sync/bucket/CrudTransaction';
17-
import { BucketStorageAdapter, PSInternalTable } from './sync/bucket/BucketStorageAdapter';
18-
import { CrudEntry, CrudEntryJSON } from './sync/bucket/CrudEntry';
19-
import { mutexRunExclusive } from '../utils/mutex';
20-
import { BaseObserver } from '../utils/BaseObserver';
21-
import { EventIterator } from 'event-iterator';
22-
import { quoteIdentifier } from '../utils/strings';
2323

2424
export interface DisconnectAndClearOptions {
2525
/** When set to false, data in local-only tables is preserved. */
@@ -548,25 +548,26 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
548548
* Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
549549
*/
550550
async *watch(sql: string, parameters?: any[], options?: SQLWatchOptions): AsyncIterable<QueryResult> {
551-
//Fetch initial data
551+
// Fetch initial data
552552
yield await this.executeReadOnly(sql, parameters);
553553

554-
const resolvedTables = options?.tables ?? [];
554+
const resolvedTables = options?.tables ? [...options.tables] : [];
555555
if (!options?.tables) {
556556
const explained = await this.getAll<{ opcode: string; p3: number; p2: number }>(`EXPLAIN ${sql}`, parameters);
557-
const rootPages = _.chain(explained)
558-
.filter((row) => row['opcode'] == 'OpenRead' && row['p3'] == 0 && _.isNumber(row['p2']))
559-
.map((row) => row['p2'])
560-
.value();
557+
const rootPages = explained
558+
.filter((row) => row.opcode == 'OpenRead' && row.p3 == 0 && typeof row.p2 == 'number')
559+
.map((row) => row.p2);
561560
const tables = await this.getAll<{ tbl_name: string }>(
562-
`SELECT tbl_name FROM sqlite_master WHERE rootpage IN (SELECT json_each.value FROM json_each(?))`,
561+
`SELECT DISTINCT tbl_name FROM sqlite_master WHERE rootpage IN (SELECT json_each.value FROM json_each(?))`,
563562
[JSON.stringify(rootPages)]
564563
);
565-
tables.forEach((t) => resolvedTables.push(t.tbl_name.replace(POWERSYNC_TABLE_MATCH, '')));
564+
for (let table of tables) {
565+
resolvedTables.push(table.tbl_name.replace(POWERSYNC_TABLE_MATCH, ''));
566+
}
566567
}
567568
for await (const event of this.onChange({
568569
...(options ?? {}),
569-
tables: _.uniq(resolvedTables)
570+
tables: resolvedTables
570571
})) {
571572
yield await this.executeReadOnly(sql, parameters);
572573
}
@@ -582,21 +583,20 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
582583
*/
583584
onChange(options?: SQLWatchOptions): AsyncIterable<WatchOnChangeEvent> {
584585
const resolvedOptions = options ?? {};
585-
const watchedTables = resolvedOptions.tables ?? [];
586+
const watchedTables = new Set(resolvedOptions.tables ?? []);
586587

587-
let throttledTableUpdates: string[] = [];
588+
let changedTables = new Set<string>();
588589
const throttleMs = resolvedOptions.throttleMs ?? DEFAULT_WATCH_THROTTLE_MS;
589590

590591
return new EventIterator<WatchOnChangeEvent>((eventOptions) => {
591-
const flushTableUpdates = _.throttle(
592+
const flushTableUpdates = throttle(
592593
() => {
593-
const intersection = _.intersection(watchedTables, throttledTableUpdates);
594-
if (intersection.length) {
594+
if (changedTables.size > 0) {
595595
eventOptions.push({
596-
changedTables: intersection
596+
changedTables: [...changedTables]
597597
});
598598
}
599-
throttledTableUpdates = [];
599+
changedTables.clear();
600600
},
601601
throttleMs,
602602
{ leading: false, trailing: true }
@@ -618,7 +618,9 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
618618
? filteredTables
619619
: filteredTables.map((t) => t.replace(POWERSYNC_TABLE_MATCH, ''));
620620

621-
throttledTableUpdates.push(...mappedTableNames);
621+
for (let table of mappedTableNames) {
622+
changedTables.add(table);
623+
}
622624

623625
flushTableUpdates();
624626
}

packages/powersync-sdk-common/src/client/sync/bucket/CrudEntry.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import _ from 'lodash';
2-
31
/**
42
* 64-bit unsigned integer stored as a string in base-10.
53
*
@@ -109,7 +107,7 @@ export class CrudEntry {
109107
}
110108

111109
equals(entry: CrudEntry) {
112-
return _.isEqual(this.toComparisonArray(), entry.toComparisonArray());
110+
return JSON.stringify(this.toComparisonArray()) == JSON.stringify(entry.toComparisonArray());
113111
}
114112

115113
/**

packages/powersync-sdk-common/src/client/sync/stream/AbstractStreamingSyncImplementation.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import _ from 'lodash';
1+
import throttle from 'lodash/throttle';
2+
23
import Logger, { ILogger } from 'js-logger';
34

45
import {
@@ -102,7 +103,7 @@ export abstract class AbstractStreamingSyncImplementation
102103
});
103104
this.abortController = null;
104105

105-
this.triggerCrudUpload = _.throttle(
106+
this.triggerCrudUpload = throttle(
106107
() => {
107108
if (!this.syncStatus.connected || this.syncStatus.dataFlowStatus.uploading) {
108109
return;
@@ -296,6 +297,7 @@ export abstract class AbstractStreamingSyncImplementation
296297
after: after
297298
}));
298299

300+
// These are compared by reference
299301
let targetCheckpoint: Checkpoint | null = null;
300302
let validatedCheckpoint: Checkpoint | null = null;
301303
let appliedCheckpoint: Checkpoint | null = null;
@@ -313,7 +315,7 @@ export abstract class AbstractStreamingSyncImplementation
313315
// A connection is active and messages are being received
314316
if (!this.syncStatus.connected) {
315317
// There is a connection now
316-
_.defer(() => this.triggerCrudUpload());
318+
Promise.resolve().then(() => this.triggerCrudUpload());
317319
this.updateSyncStatus({
318320
connected: true
319321
});
@@ -346,7 +348,7 @@ export abstract class AbstractStreamingSyncImplementation
346348
// Continue waiting.
347349
// landing here the whole time
348350
} else {
349-
appliedCheckpoint = _.clone(targetCheckpoint);
351+
appliedCheckpoint = targetCheckpoint;
350352
this.logger.debug('validated checkpoint', appliedCheckpoint);
351353
this.updateSyncStatus({
352354
connected: true,
@@ -357,7 +359,7 @@ export abstract class AbstractStreamingSyncImplementation
357359
});
358360
}
359361

360-
validatedCheckpoint = _.clone(targetCheckpoint);
362+
validatedCheckpoint = targetCheckpoint;
361363
} else if (isStreamingSyncCheckpointDiff(line)) {
362364
// TODO: It may be faster to just keep track of the diff, instead of the entire checkpoint
363365
if (targetCheckpoint == null) {
@@ -409,12 +411,12 @@ export abstract class AbstractStreamingSyncImplementation
409411
} else {
410412
this.logger.debug('Sync complete');
411413

412-
if (_.isEqual(targetCheckpoint, appliedCheckpoint)) {
414+
if (targetCheckpoint === appliedCheckpoint) {
413415
this.updateSyncStatus({
414416
connected: true,
415417
lastSyncedAt: new Date()
416418
});
417-
} else if (_.isEqual(validatedCheckpoint, targetCheckpoint)) {
419+
} else if (validatedCheckpoint === targetCheckpoint) {
418420
const result = await this.options.adapter.syncLocalDatabase(targetCheckpoint!);
419421
if (!result.checkpointValid) {
420422
// This means checksums failed. Start again with a new checkpoint.
@@ -425,7 +427,7 @@ export abstract class AbstractStreamingSyncImplementation
425427
// Checksums valid, but need more data for a consistent checkpoint.
426428
// Continue waiting.
427429
} else {
428-
appliedCheckpoint = _.clone(targetCheckpoint);
430+
appliedCheckpoint = targetCheckpoint;
429431
this.updateSyncStatus({
430432
connected: true,
431433
lastSyncedAt: new Date(),
@@ -471,7 +473,10 @@ export abstract class AbstractStreamingSyncImplementation
471473
const updatedStatus = new SyncStatus({
472474
connected: options.connected ?? this.syncStatus.connected,
473475
lastSyncedAt: options.lastSyncedAt ?? this.syncStatus.lastSyncedAt,
474-
dataFlow: _.merge(_.clone(this.syncStatus.dataFlowStatus), options.dataFlow ?? {})
476+
dataFlow: {
477+
...this.syncStatus.dataFlowStatus,
478+
...options.dataFlow
479+
}
475480
});
476481

477482
if (!this.syncStatus.isEqual(updatedStatus)) {

packages/powersync-sdk-common/src/db/crud/SyncStatus.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import _ from 'lodash';
2-
31
export type SyncDataFlowStatus = Partial<{
42
downloading: boolean;
53
uploading: boolean;
@@ -49,7 +47,7 @@ export class SyncStatus {
4947
}
5048

5149
isEqual(status: SyncStatus) {
52-
return _.isEqual(this.options, status.options);
50+
return JSON.stringify(this.options) == JSON.stringify(status.options);
5351
}
5452

5553
getMessage() {

packages/powersync-sdk-common/src/db/schema/Table.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import _, { indexOf } from 'lodash';
21
import { Column } from '../Column';
32
import type { Index } from './Index';
43
import { TableV2 } from './TableV2';
@@ -86,10 +85,13 @@ export class Table {
8685
}
8786

8887
get validName() {
89-
return _.chain([this.name, this.viewNameOverride])
90-
.compact()
91-
.every((name) => !InvalidSQLCharacters.test(name))
92-
.value();
88+
if (InvalidSQLCharacters.test(this.name)) {
89+
return false;
90+
}
91+
if (this.viewNameOverride != null && InvalidSQLCharacters.test(this.viewNameOverride)) {
92+
return false;
93+
}
94+
return true;
9395
}
9496

9597
validate() {

packages/powersync-sdk-web/src/db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import _ from 'lodash';
21
import {
32
AbstractPowerSyncDatabase,
43
AbstractPowerSyncDatabaseOpenFactory,
@@ -73,10 +72,14 @@ export abstract class AbstractWebPowerSyncDatabaseOpenFactory extends AbstractPo
7372
}
7473

7574
protected resolveDBFlags(): WebPowerSyncFlags {
76-
return _.merge(_.clone(DEFAULT_POWERSYNC_FLAGS), {
77-
ssrMode: this.isServerSide(),
78-
enableMultiTabs: this.options.flags?.enableMultiTabs
79-
});
75+
let flags = {
76+
...DEFAULT_POWERSYNC_FLAGS,
77+
ssrMode: this.isServerSide()
78+
};
79+
if (typeof this.options.flags?.enableMultiTabs != 'undefined') {
80+
flags.enableMultiTabs = this.options.flags.enableMultiTabs;
81+
}
82+
return flags;
8083
}
8184

8285
generateInstance(options: PowerSyncDatabaseOptions): AbstractPowerSyncDatabase {

packages/powersync-sdk-web/src/db/sync/SSRWebStreamingSyncImplementation.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import _ from 'lodash';
21
import {
32
AbstractStreamingSyncImplementation,
43
AbstractStreamingSyncImplementationOptions,

packages/powersync-sdk-web/src/db/sync/WebStreamingSyncImplementation.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import _ from 'lodash';
21
import {
32
AbstractStreamingSyncImplementation,
43
AbstractStreamingSyncImplementationOptions,

packages/powersync-sdk-web/src/worker/db/open-db.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as SQLite from '@journeyapps/wa-sqlite';
22
import '@journeyapps/wa-sqlite';
3-
import _ from 'lodash';
43
import * as Comlink from 'comlink';
54
import { v4 as uuid } from 'uuid';
65
import { QueryResult } from '@journeyapps/powersync-sdk-common';
@@ -93,21 +92,16 @@ export async function _openDB(dbFileName: string): Promise<DBWorkerInterface> {
9392
}
9493
}
9594

96-
const rows = _.chain(results)
97-
.filter(({ rows }) => !!rows.length)
98-
.flatMap(({ columns, rows }) =>
99-
_.map(rows, (row) =>
100-
_.reduce(
101-
columns,
102-
(out: Record<string, any>, key: string, index) => {
103-
out[key] = row[index];
104-
return out;
105-
},
106-
{}
107-
)
108-
)
109-
)
110-
.value();
95+
let rows: Record<string, any>[] = [];
96+
for (let resultset of results) {
97+
for (let row of resultset.rows) {
98+
let outRow: Record<string, any> = {};
99+
resultset.columns.forEach((key, index) => {
100+
outRow[key] = row[index];
101+
});
102+
rows.push(outRow);
103+
}
104+
}
111105

112106
const result = {
113107
insertId: sqlite3.last_insert_id(db),

packages/powersync-sdk-web/tests/crud.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ describe('CRUD Tests', () => {
163163
const tx = (await powersync.getNextCrudTransaction())!;
164164
expect(tx.transactionId).equals(1);
165165
const expectedCrudEntry = new CrudEntry(1, UpdateType.PUT, 'logs', testId, 1, {
166-
level: 'INFO',
167-
content: 'test log'
166+
content: 'test log',
167+
level: 'INFO'
168168
});
169169
expect(tx.crud[0].equals(expectedCrudEntry)).true;
170170
});

0 commit comments

Comments
 (0)