Skip to content

Commit c9bf567

Browse files
committed
Merge branch 'main' into maui
2 parents cfbfe07 + 7576512 commit c9bf567

36 files changed

+1189
-356
lines changed

.github/workflows/dev-packages.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# The version is pulled from the CHANGELOG.md file of the package.
2+
# Add a `-dev.xxx` suffix to the version.
3+
name: Create Dev Release
4+
5+
on: workflow_dispatch
6+
7+
jobs:
8+
dev-release:
9+
name: Publish Dev Packages
10+
runs-on: windows-latest
11+
12+
steps:
13+
- name: Checkout Repository
14+
uses: actions/checkout@v4
15+
16+
- name: Setup .NET SDK
17+
uses: actions/setup-dotnet@v4
18+
with:
19+
dotnet-version: '8.0'
20+
21+
- name: Download PowerSync extension
22+
run: dotnet run --project Tools/Setup
23+
24+
- name: Restore dependencies
25+
run: dotnet restore
26+
27+
- name: Extract Version from CHANGELOG.md
28+
id: extract_version
29+
shell: bash
30+
run: |
31+
VERSION=$(awk '/^## [0-9]+\.[0-9]+\.[0-9]+-dev(\.[0-9]+)?$/ {print $2; exit}' PowerSync/PowerSync.Common/CHANGELOG.md)
32+
echo "Detected Version: $VERSION"
33+
echo "VERSION=$VERSION" >> $GITHUB_ENV
34+
35+
- name: Run Pack
36+
run: dotnet pack -c Release -o ${{ github.workspace }}/output
37+
38+
- name: Run Push
39+
run: dotnet nuget push ${{ github.workspace }}\output\*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }}
40+

.github/workflows/release.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# The version is pulled from the CHANGELOG.md file of the package.
2+
name: Release
3+
4+
on: workflow_dispatch
5+
6+
jobs:
7+
release:
8+
name: Release
9+
runs-on: windows-latest
10+
if: github.ref == 'refs/heads/main'
11+
12+
steps:
13+
- name: Checkout Repository
14+
uses: actions/checkout@v4
15+
16+
- name: Setup .NET SDK
17+
uses: actions/setup-dotnet@v4
18+
with:
19+
dotnet-version: '8.0'
20+
21+
- name: Download PowerSync extension
22+
run: dotnet run --project Tools/Setup
23+
24+
- name: Restore dependencies
25+
run: dotnet restore
26+
27+
- name: Extract Version from CHANGELOG.md
28+
id: extract_version
29+
shell: bash
30+
run: |
31+
VERSION=$(awk '/^## [0-9]+\.[0-9]+\.[0-9]+/ {print $2; exit}' PowerSync/PowerSync.Common/CHANGELOG.md)
32+
echo "Detected Version: $VERSION"
33+
echo "VERSION=$VERSION" >> $GITHUB_ENV
34+
35+
- name: Run Pack
36+
run: dotnet pack -c Release -o ${{ github.workspace }}/output
37+
38+
- name: Run Push
39+
run: dotnet nuget push ${{ github.workspace }}\output\*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }}
40+

.github/workflows/test.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Test Packages
2+
3+
on:
4+
push:
5+
6+
jobs:
7+
build:
8+
name: Test Packages
9+
runs-on: windows-latest
10+
11+
steps:
12+
- name: Checkout Repository
13+
uses: actions/checkout@v4
14+
15+
- name: Setup .NET SDK
16+
uses: actions/setup-dotnet@v4
17+
with:
18+
dotnet-version: '8.0'
19+
20+
- name: Download PowerSync extension
21+
run: dotnet run --project Tools/Setup
22+
23+
- name: Restore dependencies
24+
run: dotnet restore
25+
26+
- name: Run tests
27+
run: dotnet test -v n --framework net8.0

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,4 @@ TestResults/
6060
#Core binaries - add rules after ps extension downloading is supported
6161
*.dylib
6262
*.dll
63-
*.so
63+
*.so
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
## 0.0.2-alpha.1
2+
3+
- Introduce package. Support for Desktop .NET use cases.
4+
5+
### Platform Runtime Support Added
6+
* linux-arm64
7+
* linux-x64
8+
* osx-arm64
9+
* osx-x64
10+
* wind-x64

PowerSync/PowerSync.Common/Client/PowerSyncDatabase.cs

Lines changed: 86 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,26 +28,24 @@ public class BasePowerSyncDatabaseOptions()
2828

2929
}
3030

31-
public abstract class DatabaseSource { }
31+
public interface IDatabaseSource { }
3232

33-
public class DBAdapterSource(IDBAdapter Adapter) : DatabaseSource
33+
public class DBAdapterSource(IDBAdapter Adapter) : IDatabaseSource
3434
{
3535
public IDBAdapter Adapter { get; init; } = Adapter;
3636
}
3737

38-
public class OpenFactorySource(ISQLOpenFactory Factory) : DatabaseSource
38+
public class OpenFactorySource(ISQLOpenFactory Factory) : IDatabaseSource
3939
{
4040
public ISQLOpenFactory Factory { get; init; } = Factory;
4141
}
4242

43-
4443
public class PowerSyncDatabaseOptions() : BasePowerSyncDatabaseOptions()
4544
{
4645
/// <summary>
4746
/// Source for a SQLite database connection.
4847
/// </summary>
49-
public DatabaseSource Database { get; set; } = null!;
50-
48+
public IDatabaseSource Database { get; set; } = null!;
5149
}
5250

5351
public class PowerSyncDBEvent : StreamingSyncImplementationEvent
@@ -64,6 +62,25 @@ public interface IPowerSyncDatabase : IEventStream<PowerSyncDBEvent>
6462
public Task<CrudBatch?> GetCrudBatch(int limit);
6563

6664
public Task<CrudTransaction?> GetNextCrudTransaction();
65+
66+
Task<NonQueryResult> Execute(string query, object[]? parameters = null);
67+
68+
Task<T[]> GetAll<T>(string sql, params object[]? parameters);
69+
70+
Task<T?> GetOptional<T>(string sql, params object[]? parameters);
71+
72+
Task<T> Get<T>(string sql, params object[]? parameters);
73+
74+
Task<T> ReadLock<T>(Func<ILockContext, Task<T>> fn, DBLockOptions? options = null);
75+
76+
Task<T> ReadTransaction<T>(Func<ITransaction, Task<T>> fn, DBLockOptions? options = null);
77+
78+
Task WriteLock(Func<ILockContext, Task> fn, DBLockOptions? options = null);
79+
Task<T> WriteLock<T>(Func<ILockContext, Task<T>> fn, DBLockOptions? options = null);
80+
81+
Task WriteTransaction(Func<ITransaction, Task> fn, DBLockOptions? options = null);
82+
Task<T> WriteTransaction<T>(Func<ITransaction, Task<T>> fn, DBLockOptions? options = null);
83+
6784
}
6885

6986
public class PowerSyncDatabase : EventStream<PowerSyncDBEvent>, IPowerSyncDatabase
@@ -103,9 +120,6 @@ public PowerSyncDatabase(PowerSyncDatabaseOptions options)
103120
}
104121
else if (options.Database is SQLOpenOptions openOptions)
105122
{
106-
// TODO default to MDSQLite factory for now
107-
// Can be broken out, rename this class to Abstract
108-
// `this.openDBAdapter(options)`
109123
Database = new MDSQLiteAdapter(new MDSQLiteAdapterOptions
110124
{
111125
Name = openOptions.DbFilename,
@@ -251,7 +265,7 @@ public async Task UpdateSchema(Schema schema)
251265

252266
try
253267
{
254-
// schema.Validate();
268+
schema.Validate();
255269
}
256270
catch (Exception ex)
257271
{
@@ -342,13 +356,19 @@ public async Task Disconnect()
342356
syncStreamStatusCts?.Cancel();
343357
}
344358

345-
public async Task DisconnectAndClear()
359+
/// <summary>
360+
/// Disconnect and clear the database.
361+
/// Use this when logging out.
362+
/// The database can still be queried after this is called, but the tables
363+
/// would be empty.
364+
///
365+
/// To preserve data in local-only tables, set clearLocal to false.
366+
/// </summary>
367+
public async Task DisconnectAndClear(bool clearLocal = true)
346368
{
347369
await Disconnect();
348370
await WaitForReady();
349371

350-
// TODO CL bool clearLocal = options?.ClearLocal ?? false;
351-
bool clearLocal = true;
352372

353373
await Database.WriteTransaction(async tx =>
354374
{
@@ -360,17 +380,23 @@ await Database.WriteTransaction(async tx =>
360380
Emit(new PowerSyncDBEvent { StatusChanged = CurrentStatus });
361381
}
362382

383+
/// <summary>
384+
/// Close the database, releasing resources.
385+
///
386+
/// Also disconnects any active connection.
387+
///
388+
/// Once close is called, this connection cannot be used again - a new one
389+
/// must be constructed.
390+
/// </summary>
363391
public new async Task Close()
364392
{
365-
base.Close();
366393
await WaitForReady();
367394

368-
// TODO CL
369-
// if (options.Disconnect)
370-
// {
371-
// await Disconnect();
372-
// }
395+
if (Closed) return;
396+
373397

398+
await Disconnect();
399+
base.Close();
374400
syncStreamImplementation?.Close();
375401
BucketStorageAdapter?.Close();
376402

@@ -436,7 +462,7 @@ await Database.WriteTransaction(async tx =>
436462
/// </summary>
437463
public async Task<CrudTransaction?> GetNextCrudTransaction()
438464
{
439-
return await Database.ReadTransaction(async tx =>
465+
return await ReadTransaction(async tx =>
440466
{
441467
var first = await tx.GetOptional<CrudEntryJSON>(
442468
$"SELECT id, tx_id, data FROM {PSInternalTable.CRUD} ORDER BY id ASC LIMIT 1");
@@ -451,6 +477,7 @@ await Database.WriteTransaction(async tx =>
451477

452478
if (txId == null)
453479
{
480+
454481
all = [CrudEntry.FromRow(first)];
455482
}
456483
else
@@ -529,15 +556,51 @@ public async Task<T> Get<T>(string query, object[]? parameters = null)
529556
return await Database.Get<T>(query, parameters);
530557
}
531558

559+
public async Task<T> ReadLock<T>(Func<ILockContext, Task<T>> fn, DBLockOptions? options = null)
560+
{
561+
await WaitForReady();
562+
return await Database.ReadLock(fn, options);
563+
}
564+
565+
public async Task WriteLock(Func<ILockContext, Task> fn, DBLockOptions? options = null)
566+
{
567+
await WaitForReady();
568+
await Database.WriteLock(fn, options);
569+
}
570+
571+
public async Task<T> WriteLock<T>(Func<ILockContext, Task<T>> fn, DBLockOptions? options = null)
572+
{
573+
await WaitForReady();
574+
return await Database.WriteLock(fn, options);
575+
}
576+
577+
public async Task<T> ReadTransaction<T>(Func<ITransaction, Task<T>> fn, DBLockOptions? options = null)
578+
{
579+
await WaitForReady();
580+
return await Database.ReadTransaction(fn, options);
581+
}
582+
583+
public async Task WriteTransaction(Func<ITransaction, Task> fn, DBLockOptions? options = null)
584+
{
585+
await WaitForReady();
586+
await Database.WriteTransaction(fn, options);
587+
}
588+
589+
public async Task<T> WriteTransaction<T>(Func<ITransaction, Task<T>> fn, DBLockOptions? options = null)
590+
{
591+
await WaitForReady();
592+
return await Database.WriteTransaction(fn, options);
593+
}
532594

533595
/// <summary>
534596
/// Executes a read query every time the source tables are modified.
535597
/// <para />
536598
/// Use <see cref="SQLWatchOptions.ThrottleMs"/> to specify the minimum interval between queries.
537599
/// Source tables are automatically detected using <c>EXPLAIN QUERY PLAN</c>.
538600
/// </summary>
539-
public void Watch<T>(string query, object[]? parameters, WatchHandler<T> handler, SQLWatchOptions? options = null)
601+
public Task Watch<T>(string query, object[]? parameters, WatchHandler<T> handler, SQLWatchOptions? options = null)
540602
{
603+
var tcs = new TaskCompletionSource<bool>();
541604
Task.Run(async () =>
542605
{
543606
try
@@ -567,12 +630,14 @@ public void Watch<T>(string query, object[]? parameters, WatchHandler<T> handler
567630
Signal = options?.Signal,
568631
ThrottleMs = options?.ThrottleMs
569632
});
633+
tcs.SetResult(true);
570634
}
571635
catch (Exception ex)
572636
{
573637
handler.OnError?.Invoke(ex);
574638
}
575639
});
640+
return tcs.Task;
576641
}
577642

578643
private record ExplainedResult(string opcode, int p2, int p3);
@@ -592,7 +657,6 @@ public async Task<string[]> ResolveTables(string sql, object[]? parameters = nul
592657
.Select(row => row.p2)
593658
.ToList();
594659

595-
596660
var tables = await GetAll<TableSelectResult>(
597661
"SELECT DISTINCT tbl_name FROM sqlite_master WHERE rootpage IN (SELECT json_each.value FROM json_each(?))",
598662
[JsonConvert.SerializeObject(rootPages)]

PowerSync/PowerSync.Common/Client/SQLOpenFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ namespace PowerSync.Common.Client;
22

33
using PowerSync.Common.DB;
44

5-
public class SQLOpenOptions : DatabaseSource
5+
public class SQLOpenOptions : IDatabaseSource
66
{
77
/// <summary>
88
/// Filename for the database.

PowerSync/PowerSync.Common/Client/Sync/Stream/Remote.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ public Remote(IPowerSyncBackendConnector connector)
5050

5151
credentials = await connector.FetchCredentials();
5252

53-
// TODO CL trailing forward slash check
5453
return credentials;
5554
}
5655

@@ -87,7 +86,6 @@ public async Task<T> Get<T>(string path, Dictionary<string, string>? headers = n
8786
return JsonConvert.DeserializeObject<T>(responseData)!;
8887
}
8988

90-
9189
public async IAsyncEnumerable<StreamingSyncLine?> PostStream(SyncStreamOptions options)
9290
{
9391
using var requestMessage = await BuildRequest(HttpMethod.Post, options.Path, options.Data, options.Headers);
@@ -156,7 +154,6 @@ private async Task<HttpRequestMessage> BuildRequest(HttpMethod method, string pa
156154

157155
if (string.IsNullOrEmpty(credentials.Token))
158156
{
159-
// TODO CL error status code 401
160157
var error = new HttpRequestException("Not signed in");
161158
throw error;
162159
}

0 commit comments

Comments
 (0)