Skip to content

Remove lodash (mostly) #90

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/mean-numbers-remember.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@journeyapps/powersync-sdk-common": patch
"@journeyapps/powersync-sdk-web": patch
---

Reduce JS bundle size
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import _ from 'lodash';
import { Mutex } from 'async-mutex';
import { EventIterator } from 'event-iterator';
import Logger, { ILogger } from 'js-logger';
import throttle from 'lodash/throttle';
import { DBAdapter, QueryResult, Transaction, isBatchedUpdateNotification } from '../db/DBAdapter';
import { Schema } from '../db/schema/Schema';
import { SyncStatus } from '../db/crud/SyncStatus';
import { UploadQueueStats } from '../db/crud/UploadQueueStatus';
import { Schema } from '../db/schema/Schema';
import { BaseObserver } from '../utils/BaseObserver';
import { mutexRunExclusive } from '../utils/mutex';
import { quoteIdentifier } from '../utils/strings';
import { PowerSyncBackendConnector } from './connection/PowerSyncBackendConnector';
import { BucketStorageAdapter, PSInternalTable } from './sync/bucket/BucketStorageAdapter';
import { CrudBatch } from './sync/bucket/CrudBatch';
import { CrudEntry, CrudEntryJSON } from './sync/bucket/CrudEntry';
import { CrudTransaction } from './sync/bucket/CrudTransaction';
import {
AbstractStreamingSyncImplementation,
DEFAULT_CRUD_UPLOAD_THROTTLE_MS,
StreamingSyncImplementationListener,
StreamingSyncImplementation
} from './sync/stream/AbstractStreamingSyncImplementation';
import { CrudBatch } from './sync/bucket/CrudBatch';
import { CrudTransaction } from './sync/bucket/CrudTransaction';
import { BucketStorageAdapter, PSInternalTable } from './sync/bucket/BucketStorageAdapter';
import { CrudEntry, CrudEntryJSON } from './sync/bucket/CrudEntry';
import { mutexRunExclusive } from '../utils/mutex';
import { BaseObserver } from '../utils/BaseObserver';
import { EventIterator } from 'event-iterator';
import { quoteIdentifier } from '../utils/strings';

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

const resolvedTables = options?.tables ?? [];
const resolvedTables = options?.tables ? [...options.tables] : [];
if (!options?.tables) {
const explained = await this.getAll<{ opcode: string; p3: number; p2: number }>(`EXPLAIN ${sql}`, parameters);
const rootPages = _.chain(explained)
.filter((row) => row['opcode'] == 'OpenRead' && row['p3'] == 0 && _.isNumber(row['p2']))
.map((row) => row['p2'])
.value();
const rootPages = explained
.filter((row) => row.opcode == 'OpenRead' && row.p3 == 0 && typeof row.p2 == 'number')
.map((row) => row.p2);
const tables = await this.getAll<{ tbl_name: string }>(
`SELECT tbl_name FROM sqlite_master WHERE rootpage IN (SELECT json_each.value FROM json_each(?))`,
`SELECT DISTINCT tbl_name FROM sqlite_master WHERE rootpage IN (SELECT json_each.value FROM json_each(?))`,
[JSON.stringify(rootPages)]
);
tables.forEach((t) => resolvedTables.push(t.tbl_name.replace(POWERSYNC_TABLE_MATCH, '')));
for (let table of tables) {
resolvedTables.push(table.tbl_name.replace(POWERSYNC_TABLE_MATCH, ''));
}
}
for await (const event of this.onChange({
...(options ?? {}),
tables: _.uniq(resolvedTables)
tables: resolvedTables
})) {
yield await this.executeReadOnly(sql, parameters);
}
Expand All @@ -582,21 +583,20 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
*/
onChange(options?: SQLWatchOptions): AsyncIterable<WatchOnChangeEvent> {
const resolvedOptions = options ?? {};
const watchedTables = resolvedOptions.tables ?? [];
const watchedTables = new Set(resolvedOptions.tables ?? []);

let throttledTableUpdates: string[] = [];
let changedTables = new Set<string>();
const throttleMs = resolvedOptions.throttleMs ?? DEFAULT_WATCH_THROTTLE_MS;

return new EventIterator<WatchOnChangeEvent>((eventOptions) => {
const flushTableUpdates = _.throttle(
const flushTableUpdates = throttle(
() => {
const intersection = _.intersection(watchedTables, throttledTableUpdates);
if (intersection.length) {
if (changedTables.size > 0) {
eventOptions.push({
changedTables: intersection
changedTables: [...changedTables]
});
}
throttledTableUpdates = [];
changedTables.clear();
},
throttleMs,
{ leading: false, trailing: true }
Expand All @@ -618,7 +618,9 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
? filteredTables
: filteredTables.map((t) => t.replace(POWERSYNC_TABLE_MATCH, ''));

throttledTableUpdates.push(...mappedTableNames);
for (let table of mappedTableNames) {
changedTables.add(table);
}

flushTableUpdates();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import _ from 'lodash';

/**
* 64-bit unsigned integer stored as a string in base-10.
*
Expand Down Expand Up @@ -109,7 +107,7 @@ export class CrudEntry {
}

equals(entry: CrudEntry) {
return _.isEqual(this.toComparisonArray(), entry.toComparisonArray());
return JSON.stringify(this.toComparisonArray()) == JSON.stringify(entry.toComparisonArray());
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import _ from 'lodash';
import throttle from 'lodash/throttle';

import Logger, { ILogger } from 'js-logger';

import {
Expand Down Expand Up @@ -102,7 +103,7 @@ export abstract class AbstractStreamingSyncImplementation
});
this.abortController = null;

this.triggerCrudUpload = _.throttle(
this.triggerCrudUpload = throttle(
() => {
if (!this.syncStatus.connected || this.syncStatus.dataFlowStatus.uploading) {
return;
Expand Down Expand Up @@ -296,6 +297,7 @@ export abstract class AbstractStreamingSyncImplementation
after: after
}));

// These are compared by reference
let targetCheckpoint: Checkpoint | null = null;
let validatedCheckpoint: Checkpoint | null = null;
let appliedCheckpoint: Checkpoint | null = null;
Expand All @@ -313,7 +315,7 @@ export abstract class AbstractStreamingSyncImplementation
// A connection is active and messages are being received
if (!this.syncStatus.connected) {
// There is a connection now
_.defer(() => this.triggerCrudUpload());
Promise.resolve().then(() => this.triggerCrudUpload());
this.updateSyncStatus({
connected: true
});
Expand Down Expand Up @@ -346,7 +348,7 @@ export abstract class AbstractStreamingSyncImplementation
// Continue waiting.
// landing here the whole time
} else {
appliedCheckpoint = _.clone(targetCheckpoint);
appliedCheckpoint = targetCheckpoint;
this.logger.debug('validated checkpoint', appliedCheckpoint);
this.updateSyncStatus({
connected: true,
Expand All @@ -357,7 +359,7 @@ export abstract class AbstractStreamingSyncImplementation
});
}

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

if (_.isEqual(targetCheckpoint, appliedCheckpoint)) {
if (targetCheckpoint === appliedCheckpoint) {
this.updateSyncStatus({
connected: true,
lastSyncedAt: new Date()
});
} else if (_.isEqual(validatedCheckpoint, targetCheckpoint)) {
} else if (validatedCheckpoint === targetCheckpoint) {
const result = await this.options.adapter.syncLocalDatabase(targetCheckpoint!);
if (!result.checkpointValid) {
// This means checksums failed. Start again with a new checkpoint.
Expand All @@ -425,7 +427,7 @@ export abstract class AbstractStreamingSyncImplementation
// Checksums valid, but need more data for a consistent checkpoint.
// Continue waiting.
} else {
appliedCheckpoint = _.clone(targetCheckpoint);
appliedCheckpoint = targetCheckpoint;
this.updateSyncStatus({
connected: true,
lastSyncedAt: new Date(),
Expand Down Expand Up @@ -471,7 +473,10 @@ export abstract class AbstractStreamingSyncImplementation
const updatedStatus = new SyncStatus({
connected: options.connected ?? this.syncStatus.connected,
lastSyncedAt: options.lastSyncedAt ?? this.syncStatus.lastSyncedAt,
dataFlow: _.merge(_.clone(this.syncStatus.dataFlowStatus), options.dataFlow ?? {})
dataFlow: {
...this.syncStatus.dataFlowStatus,
...options.dataFlow
}
});

if (!this.syncStatus.isEqual(updatedStatus)) {
Expand Down
4 changes: 1 addition & 3 deletions packages/powersync-sdk-common/src/db/crud/SyncStatus.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import _ from 'lodash';

export type SyncDataFlowStatus = Partial<{
downloading: boolean;
uploading: boolean;
Expand Down Expand Up @@ -49,7 +47,7 @@ export class SyncStatus {
}

isEqual(status: SyncStatus) {
return _.isEqual(this.options, status.options);
return JSON.stringify(this.options) == JSON.stringify(status.options);
}

getMessage() {
Expand Down
12 changes: 7 additions & 5 deletions packages/powersync-sdk-common/src/db/schema/Table.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import _, { indexOf } from 'lodash';
import { Column } from '../Column';
import type { Index } from './Index';
import { TableV2 } from './TableV2';
Expand Down Expand Up @@ -86,10 +85,13 @@ export class Table {
}

get validName() {
return _.chain([this.name, this.viewNameOverride])
.compact()
.every((name) => !InvalidSQLCharacters.test(name))
.value();
if (InvalidSQLCharacters.test(this.name)) {
return false;
}
if (this.viewNameOverride != null && InvalidSQLCharacters.test(this.viewNameOverride)) {
return false;
}
return true;
}

validate() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import _ from 'lodash';
import {
AbstractPowerSyncDatabase,
AbstractPowerSyncDatabaseOpenFactory,
Expand Down Expand Up @@ -73,10 +72,14 @@ export abstract class AbstractWebPowerSyncDatabaseOpenFactory extends AbstractPo
}

protected resolveDBFlags(): WebPowerSyncFlags {
return _.merge(_.clone(DEFAULT_POWERSYNC_FLAGS), {
ssrMode: this.isServerSide(),
enableMultiTabs: this.options.flags?.enableMultiTabs
});
let flags = {
...DEFAULT_POWERSYNC_FLAGS,
ssrMode: this.isServerSide()
};
if (typeof this.options.flags?.enableMultiTabs != 'undefined') {
flags.enableMultiTabs = this.options.flags.enableMultiTabs;
}
return flags;
}

generateInstance(options: PowerSyncDatabaseOptions): AbstractPowerSyncDatabase {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import _ from 'lodash';
import {
AbstractStreamingSyncImplementation,
AbstractStreamingSyncImplementationOptions,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import _ from 'lodash';
import {
AbstractStreamingSyncImplementation,
AbstractStreamingSyncImplementationOptions,
Expand Down
26 changes: 10 additions & 16 deletions packages/powersync-sdk-web/src/worker/db/open-db.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as SQLite from '@journeyapps/wa-sqlite';
import '@journeyapps/wa-sqlite';
import _ from 'lodash';
import * as Comlink from 'comlink';
import { v4 as uuid } from 'uuid';
import { QueryResult } from '@journeyapps/powersync-sdk-common';
Expand Down Expand Up @@ -93,21 +92,16 @@ export async function _openDB(dbFileName: string): Promise<DBWorkerInterface> {
}
}

const rows = _.chain(results)
.filter(({ rows }) => !!rows.length)
.flatMap(({ columns, rows }) =>
_.map(rows, (row) =>
_.reduce(
columns,
(out: Record<string, any>, key: string, index) => {
out[key] = row[index];
return out;
},
{}
)
)
)
.value();
let rows: Record<string, any>[] = [];
for (let resultset of results) {
for (let row of resultset.rows) {
let outRow: Record<string, any> = {};
resultset.columns.forEach((key, index) => {
outRow[key] = row[index];
});
rows.push(outRow);
}
}

const result = {
insertId: sqlite3.last_insert_id(db),
Expand Down
4 changes: 2 additions & 2 deletions packages/powersync-sdk-web/tests/crud.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ describe('CRUD Tests', () => {
const tx = (await powersync.getNextCrudTransaction())!;
expect(tx.transactionId).equals(1);
const expectedCrudEntry = new CrudEntry(1, UpdateType.PUT, 'logs', testId, 1, {
level: 'INFO',
content: 'test log'
content: 'test log',
level: 'INFO'
});
expect(tx.crud[0].equals(expectedCrudEntry)).true;
});
Expand Down