create-syncular-app 0.1.3 → 0.2.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.
Files changed (64) hide show
  1. package/README.md +70 -27
  2. package/dist/cli.d.ts +11 -0
  3. package/dist/cli.js +131 -179
  4. package/dist/constants.d.ts +40 -0
  5. package/dist/constants.js +46 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.js +3 -0
  8. package/dist/scaffold.d.ts +44 -0
  9. package/dist/scaffold.js +106 -0
  10. package/package.json +22 -15
  11. package/src/cli.ts +174 -0
  12. package/src/constants.ts +53 -0
  13. package/src/index.ts +3 -0
  14. package/src/scaffold.ts +161 -0
  15. package/template/minimal/README.md +45 -0
  16. package/template/minimal/gitignore +5 -0
  17. package/template/minimal/migrations/0001_initial/up.sql +9 -0
  18. package/template/minimal/package.json +22 -0
  19. package/template/minimal/src/clients.ts +54 -0
  20. package/template/minimal/src/make-client.ts +26 -0
  21. package/template/minimal/src/server.ts +39 -0
  22. package/template/minimal/src/smoke.test.ts +70 -0
  23. package/template/minimal/src/syncular.generated.ts +66 -0
  24. package/template/minimal/syncular.ir.json +66 -0
  25. package/template/minimal/syncular.json +17 -0
  26. package/template/minimal/tsconfig.json +16 -0
  27. package/template/web/README.md +63 -0
  28. package/template/web/gitignore +5 -0
  29. package/template/web/migrations/0001_initial/up.sql +11 -0
  30. package/template/web/package.json +22 -0
  31. package/template/web/src/frontend/index.html +37 -0
  32. package/template/web/src/frontend/main.ts +191 -0
  33. package/template/web/src/frontend/worker.ts +9 -0
  34. package/template/web/src/server.ts +183 -0
  35. package/template/web/src/smoke.test.ts +91 -0
  36. package/template/web/src/syncular.generated.ts +74 -0
  37. package/template/web/syncular.ir.json +76 -0
  38. package/template/web/syncular.json +17 -0
  39. package/template/web/tsconfig.json +16 -0
  40. package/template/README.md +0 -122
  41. package/template/_gitignore +0 -5
  42. package/template/generated/kotlin/SyncularApp.kt +0 -1005
  43. package/template/generated/rust/diesel_tables.rs +0 -142
  44. package/template/generated/rust/migrations.rs +0 -32
  45. package/template/generated/rust/schema.rs +0 -15
  46. package/template/generated/rust/syncular.rs +0 -926
  47. package/template/generated/swift/SyncularApp.swift +0 -1191
  48. package/template/generated/syncular.codegen.json +0 -19
  49. package/template/index.html +0 -13
  50. package/template/migrations/0001_initial/down.sql +0 -1
  51. package/template/migrations/0001_initial/up.sql +0 -8
  52. package/template/package.json +0 -33
  53. package/template/scripts/dev.ts +0 -42
  54. package/template/src/app.tsx +0 -231
  55. package/template/src/client/syncular.ts +0 -61
  56. package/template/src/generated/syncular.generated.ts +0 -769
  57. package/template/src/generated/syncular.server.generated.ts +0 -512
  58. package/template/src/main.tsx +0 -5
  59. package/template/src/server/sync-server.ts +0 -129
  60. package/template/src/styles.css +0 -233
  61. package/template/syncular.app.ts +0 -23
  62. package/template/syncular.schema.json +0 -145
  63. package/template/tsconfig.json +0 -18
  64. package/template/vite.config.ts +0 -9
@@ -1,1191 +0,0 @@
1
- // @generated by `cargo run --manifest-path rust/Cargo.toml -p syncular-codegen --`
2
- // Source: migrations/*.sql and generated Syncular codegen handoff
3
-
4
- import Foundation
5
-
6
- public let syncularNativeExpectedFfiAbiVersion = 2
7
- public let syncularNativeExpectedCrateVersion = "0.1.0"
8
- public let syncularNativeGeneratedSchemaVersion = 1
9
-
10
- public let syncularNativeGeneratedAppSchemaJson = "{\"clientSchemaSupport\":{\"current\":1,\"minSupported\":1,\"supported\":[1]},\"localBaseSchema\":{\"tableSetupSql\":[\"CREATE TABLE IF NOT EXISTS \\\"tasks\\\" (\\n \\\"id\\\" TEXT PRIMARY KEY,\\n \\\"title\\\" TEXT NOT NULL,\\n \\\"completed\\\" INTEGER NOT NULL DEFAULT 0,\\n \\\"user_id\\\" TEXT NOT NULL,\\n \\\"created_at\\\" INTEGER NOT NULL DEFAULT 0,\\n \\\"server_version\\\" INTEGER NOT NULL DEFAULT 0\\n) WITHOUT ROWID\"]},\"migrations\":[{\"name\":\"initial\",\"schemaVersion\":1,\"upSql\":\"CREATE TABLE IF NOT EXISTS tasks (\\n id TEXT PRIMARY KEY,\\n title TEXT NOT NULL,\\n completed INTEGER NOT NULL DEFAULT 0,\\n user_id TEXT NOT NULL,\\n created_at BIGINT NOT NULL DEFAULT 0,\\n server_version BIGINT NOT NULL DEFAULT 0\\n) WITHOUT ROWID;\",\"version\":\"0001\"}],\"schemaVersion\":1,\"tables\":[{\"blobColumns\":[],\"columns\":[{\"name\":\"id\",\"notnullRequired\":false,\"primaryKey\":true,\"typeFamily\":\"text\"},{\"name\":\"title\",\"notnullRequired\":true,\"primaryKey\":false,\"typeFamily\":\"text\"},{\"name\":\"completed\",\"notnullRequired\":true,\"primaryKey\":false,\"typeFamily\":\"integer\"},{\"name\":\"user_id\",\"notnullRequired\":true,\"primaryKey\":false,\"typeFamily\":\"text\"},{\"name\":\"created_at\",\"notnullRequired\":true,\"primaryKey\":false,\"typeFamily\":\"integer\"},{\"name\":\"server_version\",\"notnullRequired\":true,\"primaryKey\":false,\"typeFamily\":\"integer\"}],\"crdtYjsFields\":[],\"encryptedFields\":[],\"name\":\"tasks\",\"primaryKeyColumn\":\"id\",\"scopes\":[{\"column\":\"user_id\",\"name\":\"user_id\",\"required\":true,\"source\":\"actorId\"}],\"serverVersionColumn\":\"server_version\",\"softDeleteColumn\":null,\"subscriptionId\":\"sub-tasks\"}]}"
11
-
12
- public enum SyncularNativeGeneratedError: Error, Equatable {
13
- case runtimeManifestMismatch(String)
14
- }
15
-
16
- public struct SyncularNativeRuntimeManifest: Decodable, Equatable {
17
- public let ffiAbiVersion: Int
18
- public let crateName: String
19
- public let crateVersion: String
20
- public let schemaVersion: Int
21
- public let storageBackend: String
22
- public let transportBackends: [String]
23
- public let capabilities: [String]
24
-
25
- private enum CodingKeys: String, CodingKey {
26
- case ffiAbiVersion = "ffi_abi_version"
27
- case crateName = "crate_name"
28
- case crateVersion = "crate_version"
29
- case schemaVersion = "schema_version"
30
- case storageBackend = "storage_backend"
31
- case transportBackends = "transport_backends"
32
- case capabilities
33
- }
34
- }
35
-
36
- public func assertSyncularNativeRuntimeManifestJson(_ manifestJson: String) throws {
37
- let data = Data(manifestJson.utf8)
38
- let manifest = try JSONDecoder().decode(SyncularNativeRuntimeManifest.self, from: data)
39
- try assertSyncularNativeRuntimeManifest(manifest)
40
- }
41
-
42
- public func assertSyncularNativeRuntimeManifest(_ manifest: SyncularNativeRuntimeManifest) throws {
43
- guard manifest.ffiAbiVersion == syncularNativeExpectedFfiAbiVersion else {
44
- throw SyncularNativeGeneratedError.runtimeManifestMismatch("FFI ABI version \(manifest.ffiAbiVersion) does not match generated expectation \(syncularNativeExpectedFfiAbiVersion)")
45
- }
46
- guard manifest.crateVersion == syncularNativeExpectedCrateVersion else {
47
- throw SyncularNativeGeneratedError.runtimeManifestMismatch("Rust crate version \(manifest.crateVersion) does not match generated expectation \(syncularNativeExpectedCrateVersion)")
48
- }
49
- guard manifest.storageBackend == "diesel-sqlite" else {
50
- throw SyncularNativeGeneratedError.runtimeManifestMismatch("Rust storage backend \(manifest.storageBackend) is not diesel-sqlite")
51
- }
52
- guard manifest.capabilities.contains("generated-json-local-operations") else {
53
- throw SyncularNativeGeneratedError.runtimeManifestMismatch("Rust native runtime is missing generated-json-local-operations")
54
- }
55
- guard manifest.capabilities.contains("generated-json-mutations") else {
56
- throw SyncularNativeGeneratedError.runtimeManifestMismatch("Rust native runtime is missing generated-json-mutations")
57
- }
58
- guard manifest.capabilities.contains("generated-json-leased-mutations") else {
59
- throw SyncularNativeGeneratedError.runtimeManifestMismatch("Rust native runtime is missing generated-json-leased-mutations")
60
- }
61
- guard manifest.capabilities.contains("queued-json-leased-mutations") else {
62
- throw SyncularNativeGeneratedError.runtimeManifestMismatch("Rust native runtime is missing queued-json-leased-mutations")
63
- }
64
- guard manifest.capabilities.contains("auth-lease-issue") else {
65
- throw SyncularNativeGeneratedError.runtimeManifestMismatch("Rust native runtime is missing auth-lease-issue")
66
- }
67
- guard manifest.capabilities.contains("read-only-query-json") else {
68
- throw SyncularNativeGeneratedError.runtimeManifestMismatch("Rust native runtime is missing read-only-query-json")
69
- }
70
- guard manifest.capabilities.contains("query-observer-events") else {
71
- throw SyncularNativeGeneratedError.runtimeManifestMismatch("Rust native runtime is missing query-observer-events")
72
- }
73
- }
74
-
75
- public enum SyncularGeneratedOperationKind: String, Codable, Equatable {
76
- case upsert
77
- case delete
78
- }
79
-
80
- public indirect enum SyncularJsonValue: Codable, Equatable {
81
- case string(String)
82
- case int(Int64)
83
- case double(Double)
84
- case bool(Bool)
85
- case object([String: SyncularJsonValue])
86
- case array([SyncularJsonValue])
87
- case null
88
-
89
- public init(from decoder: Decoder) throws {
90
- let container = try decoder.singleValueContainer()
91
- if container.decodeNil() { self = .null }
92
- else if let value = try? container.decode(Bool.self) { self = .bool(value) }
93
- else if let value = try? container.decode(Int64.self) { self = .int(value) }
94
- else if let value = try? container.decode(Double.self) { self = .double(value) }
95
- else if let value = try? container.decode([String: SyncularJsonValue].self) { self = .object(value) }
96
- else if let value = try? container.decode([SyncularJsonValue].self) { self = .array(value) }
97
- else { self = .string(try container.decode(String.self)) }
98
- }
99
-
100
- public func encode(to encoder: Encoder) throws {
101
- var container = encoder.singleValueContainer()
102
- switch self {
103
- case .string(let value): try container.encode(value)
104
- case .int(let value): try container.encode(value)
105
- case .double(let value): try container.encode(value)
106
- case .bool(let value): try container.encode(value)
107
- case .object(let value): try container.encode(value)
108
- case .array(let value): try container.encode(value)
109
- case .null: try container.encodeNil()
110
- }
111
- }
112
- }
113
-
114
- public struct SyncularBlobRef: Codable, Equatable {
115
- public let hash: String
116
- public let size: Int64
117
- public let mimeType: String
118
- public let encrypted: Bool?
119
- public let keyId: String?
120
-
121
- public init(hash: String, size: Int64, mimeType: String, encrypted: Bool? = nil, keyId: String? = nil) {
122
- self.hash = hash
123
- self.size = size
124
- self.mimeType = mimeType
125
- self.encrypted = encrypted
126
- self.keyId = keyId
127
- }
128
-
129
- private enum CodingKeys: String, CodingKey {
130
- case hash
131
- case size
132
- case mimeType
133
- case encrypted
134
- case keyId
135
- }
136
-
137
- public init(from decoder: Decoder) throws {
138
- let single = try decoder.singleValueContainer()
139
- if let encoded = try? single.decode(String.self) {
140
- self = try JSONDecoder().decode(SyncularBlobRef.self, from: Data(encoded.utf8))
141
- return
142
- }
143
- let container = try decoder.container(keyedBy: CodingKeys.self)
144
- hash = try container.decode(String.self, forKey: .hash)
145
- size = try container.decode(Int64.self, forKey: .size)
146
- mimeType = try container.decode(String.self, forKey: .mimeType)
147
- encrypted = try container.decodeIfPresent(Bool.self, forKey: .encrypted)
148
- keyId = try container.decodeIfPresent(String.self, forKey: .keyId)
149
- }
150
-
151
- public func encode(to encoder: Encoder) throws {
152
- var container = encoder.container(keyedBy: CodingKeys.self)
153
- try container.encode(hash, forKey: .hash)
154
- try container.encode(size, forKey: .size)
155
- try container.encode(mimeType, forKey: .mimeType)
156
- try container.encodeIfPresent(encrypted, forKey: .encrypted)
157
- try container.encodeIfPresent(keyId, forKey: .keyId)
158
- }
159
-
160
- public var syncularPayloadValue: SyncularJsonValue {
161
- var value: [String: SyncularJsonValue] = [
162
- "hash": .string(hash),
163
- "size": .int(size),
164
- "mimeType": .string(mimeType),
165
- ]
166
- if let encrypted { value["encrypted"] = .bool(encrypted) }
167
- if let keyId { value["keyId"] = .string(keyId) }
168
- return .object(value)
169
- }
170
-
171
- public func jsonString() throws -> String {
172
- let encoder = JSONEncoder()
173
- encoder.outputFormatting = [.sortedKeys]
174
- return String(data: try encoder.encode(self), encoding: .utf8)!
175
- }
176
- }
177
-
178
- public struct SyncularSubscriptionArgs: Equatable {
179
- public let actorId: String
180
- public let projectId: String?
181
-
182
- public let bootstrapPhases: [String: Int64]
183
-
184
- public init(actorId: String, projectId: String? = nil, bootstrapPhases: [String: Int64] = [:]) {
185
- self.actorId = actorId
186
- self.projectId = projectId
187
- self.bootstrapPhases = bootstrapPhases
188
- }
189
- }
190
-
191
- private func syncularBootstrapPhase(args: SyncularSubscriptionArgs, table: String, subscriptionId: String) -> Int64 {
192
- args.bootstrapPhases[subscriptionId] ?? args.bootstrapPhases[table] ?? 0
193
- }
194
-
195
- public struct SyncularSubscriptionSpec: Codable, Equatable {
196
- public let id: String
197
- public let table: String
198
- public let scopes: [String: SyncularJsonValue]
199
- public let params: [String: SyncularJsonValue]
200
- public let bootstrapPhase: Int64
201
-
202
- public init(id: String, table: String, scopes: [String: SyncularJsonValue], params: [String: SyncularJsonValue] = [:], bootstrapPhase: Int64 = 0) {
203
- self.id = id
204
- self.table = table
205
- self.scopes = scopes
206
- self.params = params
207
- self.bootstrapPhase = bootstrapPhase
208
- }
209
-
210
- public func jsonString() throws -> String {
211
- let encoder = JSONEncoder()
212
- encoder.outputFormatting = [.sortedKeys]
213
- return String(data: try encoder.encode(self), encoding: .utf8)!
214
- }
215
- }
216
-
217
- public struct SyncularAuthLeaseScope: Codable, Equatable {
218
- public let subscriptionId: String
219
- public let table: String
220
- public let values: [String: SyncularJsonValue]
221
- public let operations: [String]
222
-
223
- public init(subscriptionId: String, table: String, values: [String: SyncularJsonValue], operations: [String]) {
224
- self.subscriptionId = subscriptionId
225
- self.table = table
226
- self.values = values
227
- self.operations = operations
228
- }
229
- }
230
-
231
- public struct SyncularAuthLeaseIssueRequest: Codable, Equatable {
232
- public let schemaVersion: Int
233
- public let ttlMs: Int64?
234
- public let scopes: [SyncularAuthLeaseScope]
235
-
236
- public init(schemaVersion: Int = syncularNativeGeneratedSchemaVersion, ttlMs: Int64? = nil, scopes: [SyncularAuthLeaseScope]) {
237
- self.schemaVersion = schemaVersion
238
- self.ttlMs = ttlMs
239
- self.scopes = scopes
240
- }
241
-
242
- public func jsonString() throws -> String {
243
- let encoder = JSONEncoder()
244
- encoder.outputFormatting = [.sortedKeys]
245
- return String(data: try encoder.encode(self), encoding: .utf8)!
246
- }
247
- }
248
-
249
- public struct SyncularAuthLeaseRecord: Codable, Equatable {
250
- public let leaseId: String
251
- public let kid: String
252
- public let actorId: String
253
- public let issuedAtMs: Int64
254
- public let notBeforeMs: Int64
255
- public let expiresAtMs: Int64
256
- public let schemaVersion: Int
257
- public let payloadJson: String
258
- public let token: String
259
- public let status: String
260
- public let lastValidationError: String?
261
- public let createdAtMs: Int64
262
- public let updatedAtMs: Int64
263
- }
264
-
265
- public func syncularSubscriptionsJson(_ subscriptions: [SyncularSubscriptionSpec]) throws -> String {
266
- let encoder = JSONEncoder()
267
- encoder.outputFormatting = [.sortedKeys]
268
- return String(data: try encoder.encode(subscriptions), encoding: .utf8)!
269
- }
270
-
271
- public func syncularDefaultSubscriptionsJson(actorId: String, projectId: String? = nil, bootstrapPhases: [String: Int64] = [:]) throws -> String {
272
- try syncularSubscriptionsJson(syncularDefaultSubscriptions(args: SyncularSubscriptionArgs(actorId: actorId, projectId: projectId, bootstrapPhases: bootstrapPhases)))
273
- }
274
-
275
- public func syncularDefaultSubscriptions(args: SyncularSubscriptionArgs) -> [SyncularSubscriptionSpec] {
276
- [
277
- taskSubscription(args: args),
278
- ]
279
- }
280
-
281
- public func taskSubscription(args: SyncularSubscriptionArgs) -> SyncularSubscriptionSpec {
282
- var scopes: [String: SyncularJsonValue] = [:]
283
- scopes["user_id"] = .string(args.actorId)
284
- return SyncularSubscriptionSpec(id: "sub-tasks", table: "tasks", scopes: scopes, params: [:], bootstrapPhase: syncularBootstrapPhase(args: args, table: "tasks", subscriptionId: "sub-tasks"))
285
- }
286
-
287
- public struct SyncularReadonlyQuery: Codable, Equatable {
288
- public let sql: String
289
- public let params: [SyncularJsonValue]
290
- public let tables: [String]
291
-
292
- public init(sql: String, params: [SyncularJsonValue] = [], tables: [String]) {
293
- self.sql = sql
294
- self.params = params
295
- self.tables = tables
296
- }
297
-
298
- public func jsonString() throws -> String {
299
- let encoder = JSONEncoder()
300
- encoder.outputFormatting = [.sortedKeys]
301
- return String(data: try encoder.encode(self), encoding: .utf8)!
302
- }
303
- }
304
-
305
- public struct SyncularLiveQueryRegistration: Codable, Equatable {
306
- public let id: String
307
- public let tables: [String]
308
- public let label: String?
309
-
310
- public init(id: String, tables: [String], label: String? = nil) {
311
- self.id = id
312
- self.tables = tables
313
- self.label = label
314
- }
315
-
316
- public func jsonString() throws -> String {
317
- let encoder = JSONEncoder()
318
- encoder.outputFormatting = [.sortedKeys]
319
- return String(data: try encoder.encode(self), encoding: .utf8)!
320
- }
321
- }
322
-
323
- public struct SyncularChangedCrdtField: Decodable, Equatable {
324
- public let field: String
325
- public let stateColumn: String
326
- public let containerKey: String
327
- public let rowIdField: String
328
- public let kind: String
329
- public let syncMode: String
330
-
331
- public init(field: String, stateColumn: String, containerKey: String, rowIdField: String, kind: String, syncMode: String) {
332
- self.field = field
333
- self.stateColumn = stateColumn
334
- self.containerKey = containerKey
335
- self.rowIdField = rowIdField
336
- self.kind = kind
337
- self.syncMode = syncMode
338
- }
339
- }
340
-
341
- public struct SyncularChangedRow: Decodable, Equatable {
342
- public let table: String
343
- public let rowId: String?
344
- public let operation: String
345
- public let changedFields: [String]
346
- public let crdtFields: [String]
347
- public let crdtFieldChanges: [SyncularChangedCrdtField]
348
- public let commitId: String?
349
- public let commitSeq: Int64?
350
- public let subscriptionId: String?
351
- public let serverVersion: Int64?
352
-
353
- public init(table: String, rowId: String? = nil, operation: String, changedFields: [String] = [], crdtFields: [String] = [], crdtFieldChanges: [SyncularChangedCrdtField] = [], commitId: String? = nil, commitSeq: Int64? = nil, subscriptionId: String? = nil, serverVersion: Int64? = nil) {
354
- self.table = table
355
- self.rowId = rowId
356
- self.operation = operation
357
- self.changedFields = changedFields
358
- self.crdtFields = crdtFields
359
- self.crdtFieldChanges = crdtFieldChanges
360
- self.commitId = commitId
361
- self.commitSeq = commitSeq
362
- self.subscriptionId = subscriptionId
363
- self.serverVersion = serverVersion
364
- }
365
-
366
- public init(from decoder: Decoder) throws {
367
- let container = try decoder.container(keyedBy: CodingKeys.self)
368
- table = try container.decode(String.self, forKey: .table)
369
- rowId = try container.decodeIfPresent(String.self, forKey: .rowId)
370
- operation = try container.decode(String.self, forKey: .operation)
371
- changedFields = try container.decodeIfPresent([String].self, forKey: .changedFields) ?? []
372
- crdtFields = try container.decodeIfPresent([String].self, forKey: .crdtFields) ?? []
373
- crdtFieldChanges = try container.decodeIfPresent([SyncularChangedCrdtField].self, forKey: .crdtFieldChanges) ?? []
374
- commitId = try container.decodeIfPresent(String.self, forKey: .commitId)
375
- commitSeq = try container.decodeIfPresent(Int64.self, forKey: .commitSeq)
376
- subscriptionId = try container.decodeIfPresent(String.self, forKey: .subscriptionId)
377
- serverVersion = try container.decodeIfPresent(Int64.self, forKey: .serverVersion)
378
- }
379
-
380
- private enum CodingKeys: String, CodingKey {
381
- case table
382
- case rowId
383
- case operation
384
- case changedFields
385
- case crdtFields
386
- case crdtFieldChanges
387
- case commitId
388
- case commitSeq
389
- case subscriptionId
390
- case serverVersion
391
- }
392
- }
393
-
394
- public struct SyncularBootstrapState: Decodable, Equatable {
395
- public let asOfCommitSeq: Int64
396
- public let tables: [String]
397
- public let tableIndex: Int64
398
- public let rowCursor: String?
399
- }
400
-
401
- public struct SyncularBootstrapSubscriptionStatus: Decodable, Equatable {
402
- public let id: String
403
- public let table: String
404
- public let expected: Bool
405
- public let ready: Bool
406
- public let status: String?
407
- public let phase: String
408
- public let progressPercent: Int64
409
- public let cursor: Int64?
410
- public let bootstrapState: SyncularBootstrapState?
411
- public let bootstrapPhase: Int64
412
- }
413
-
414
- public struct SyncularBootstrapPhaseStatus: Decodable, Equatable {
415
- public let phase: Int64
416
- public let expectedSubscriptionIds: [String]
417
- public let readySubscriptionIds: [String]
418
- public let pendingSubscriptionIds: [String]
419
- public let isReady: Bool
420
- public let progressPercent: Int64
421
- }
422
-
423
- public struct SyncularBootstrapStatus: Decodable, Equatable {
424
- public let channelPhase: String
425
- public let progressPercent: Int64
426
- public let isBootstrapping: Bool
427
- public let criticalReady: Bool
428
- public let interactiveReady: Bool
429
- public let complete: Bool
430
- public let activePhase: Int64?
431
- public let expectedSubscriptionIds: [String]
432
- public let readySubscriptionIds: [String]
433
- public let pendingSubscriptionIds: [String]
434
- public let subscriptions: [SyncularBootstrapSubscriptionStatus]
435
- public let phases: [SyncularBootstrapPhaseStatus]
436
- }
437
-
438
- public struct SyncularNativeErrorInfo: Decodable, Equatable {
439
- public let kind: String
440
- public let code: String
441
- public let category: String
442
- public let retryable: Bool
443
- public let recommendedAction: String
444
- public let message: String
445
- public let debug: String?
446
- }
447
-
448
- public struct SyncularNativeLifecycleBootstrap: Decodable, Equatable {
449
- public let complete: Bool
450
- public let criticalReady: Bool
451
- public let interactiveReady: Bool
452
- public let isBootstrapping: Bool
453
- public let progressPercent: Int64
454
- }
455
-
456
- public struct SyncularNativeLifecycleOutbox: Decodable, Equatable {
457
- public let pending: UInt64
458
- }
459
-
460
- public struct SyncularNativeLifecycleConflicts: Decodable, Equatable {
461
- public let unresolved: UInt64
462
- }
463
-
464
- public struct SyncularNativeLifecycleBlobUploads: Decodable, Equatable {
465
- public let pending: Int64
466
- public let uploading: Int64
467
- public let failed: Int64
468
- }
469
-
470
- public struct SyncularNativeLifecycleState: Decodable, Equatable {
471
- public let phase: String
472
- public let online: Bool
473
- public let requiresAction: Bool
474
- public let pendingRequests: UInt64
475
- public let bootstrap: SyncularNativeLifecycleBootstrap?
476
- public let outbox: SyncularNativeLifecycleOutbox?
477
- public let conflicts: SyncularNativeLifecycleConflicts?
478
- public let blobUploads: SyncularNativeLifecycleBlobUploads?
479
- }
480
-
481
- public struct SyncularNativeEvent: Decodable, Equatable {
482
- public let eventSeq: UInt64
483
- public let kind: String
484
- public let error: SyncularNativeErrorInfo?
485
- public let tables: [String]
486
- public let queries: [String]
487
- public let changedRows: [SyncularChangedRow]
488
- public let commandId: String?
489
- public let clientCommitId: String?
490
- public let durationMs: UInt64?
491
- public let droppedCount: UInt64?
492
- public let bootstrap: SyncularBootstrapStatus?
493
- public let lifecycle: SyncularNativeLifecycleState?
494
- public let resyncRequired: Bool
495
-
496
- public init(eventSeq: UInt64 = 0, kind: String, error: SyncularNativeErrorInfo? = nil, tables: [String] = [], queries: [String] = [], changedRows: [SyncularChangedRow] = [], commandId: String? = nil, clientCommitId: String? = nil, durationMs: UInt64? = nil, droppedCount: UInt64? = nil, bootstrap: SyncularBootstrapStatus? = nil, lifecycle: SyncularNativeLifecycleState? = nil, resyncRequired: Bool = false) {
497
- self.eventSeq = eventSeq
498
- self.kind = kind
499
- self.error = error
500
- self.tables = tables
501
- self.queries = queries
502
- self.changedRows = changedRows
503
- self.commandId = commandId
504
- self.clientCommitId = clientCommitId
505
- self.durationMs = durationMs
506
- self.droppedCount = droppedCount
507
- self.bootstrap = bootstrap
508
- self.lifecycle = lifecycle
509
- self.resyncRequired = resyncRequired
510
- }
511
-
512
- private enum CodingKeys: String, CodingKey {
513
- case eventSeq = "event_seq"
514
- case kind
515
- case error
516
- case tables
517
- case queries
518
- case changedRows
519
- case commandId = "command_id"
520
- case clientCommitId = "client_commit_id"
521
- case durationMs = "duration_ms"
522
- case droppedCount
523
- case bootstrap
524
- case lifecycle
525
- case resyncRequired
526
- }
527
-
528
- public init(from decoder: Decoder) throws {
529
- let container = try decoder.container(keyedBy: CodingKeys.self)
530
- eventSeq = try container.decodeIfPresent(UInt64.self, forKey: .eventSeq) ?? 0
531
- kind = try container.decode(String.self, forKey: .kind)
532
- error = try container.decodeIfPresent(SyncularNativeErrorInfo.self, forKey: .error)
533
- tables = try container.decodeIfPresent([String].self, forKey: .tables) ?? []
534
- queries = try container.decodeIfPresent([String].self, forKey: .queries) ?? []
535
- changedRows = try container.decodeIfPresent([SyncularChangedRow].self, forKey: .changedRows) ?? []
536
- commandId = try container.decodeIfPresent(String.self, forKey: .commandId)
537
- clientCommitId = try container.decodeIfPresent(String.self, forKey: .clientCommitId)
538
- durationMs = try container.decodeIfPresent(UInt64.self, forKey: .durationMs)
539
- droppedCount = try container.decodeIfPresent(UInt64.self, forKey: .droppedCount)
540
- bootstrap = try container.decodeIfPresent(SyncularBootstrapStatus.self, forKey: .bootstrap)
541
- lifecycle = try container.decodeIfPresent(SyncularNativeLifecycleState.self, forKey: .lifecycle)
542
- resyncRequired = (try container.decodeIfPresent(Bool.self, forKey: .resyncRequired)) ?? (kind == "EventsOverflowed")
543
- }
544
-
545
- public var eventStreamLost: Bool {
546
- kind == "EventsOverflowed" || resyncRequired
547
- }
548
- }
549
-
550
- public func syncularDecodeNativeEvent(_ eventJson: String) throws -> SyncularNativeEvent {
551
- try JSONDecoder().decode(SyncularNativeEvent.self, from: Data(eventJson.utf8))
552
- }
553
-
554
- public func syncularNativeEventRequiresFullRefresh(_ event: SyncularNativeEvent) -> Bool {
555
- event.eventStreamLost
556
- }
557
-
558
- public protocol SyncularNativeEventJsonSource {
559
- func eventJsonStream(capacity: UInt64) -> AsyncThrowingStream<String, Error>
560
- }
561
-
562
- public func syncularNativeEventStream(from source: SyncularNativeEventJsonSource, capacity: UInt64 = 256) -> AsyncThrowingStream<SyncularNativeEvent, Error> {
563
- AsyncThrowingStream { continuation in
564
- let task = Task {
565
- do {
566
- for try await eventJson in source.eventJsonStream(capacity: capacity) {
567
- continuation.yield(try syncularDecodeNativeEvent(eventJson))
568
- }
569
- continuation.finish()
570
- } catch {
571
- continuation.finish(throwing: error)
572
- }
573
- }
574
- continuation.onTermination = { _ in task.cancel() }
575
- }
576
- }
577
-
578
- public struct TaskChangedFields: Equatable {
579
- public let raw: Set<String>
580
- public let id: Bool
581
- public let title: Bool
582
- public let completed: Bool
583
- public let userId: Bool
584
- public let createdAt: Bool
585
- public let serverVersion: Bool
586
-
587
- public init(_ fields: [String]) {
588
- let raw = Set(fields)
589
- self.raw = raw
590
- self.id = raw.contains("id")
591
- self.title = raw.contains("title")
592
- self.completed = raw.contains("completed")
593
- self.userId = raw.contains("user_id")
594
- self.createdAt = raw.contains("created_at")
595
- self.serverVersion = raw.contains("server_version")
596
- }
597
-
598
- public func contains(_ column: String) -> Bool {
599
- switch column {
600
- case "id": id
601
- case "title": title
602
- case "completed": completed
603
- case "user_id": userId
604
- case "created_at": createdAt
605
- case "server_version": serverVersion
606
- default: false
607
- }
608
- }
609
- }
610
-
611
- public struct TaskChangedRow: Equatable {
612
- public let raw: SyncularChangedRow
613
- public let changed: TaskChangedFields
614
- public let crdt: TaskChangedFields
615
-
616
- public var rowId: String? { raw.rowId }
617
- public var operation: String { raw.operation }
618
- public var isInsert: Bool { raw.operation == "insert" }
619
- public var isUpdate: Bool { raw.operation == "update" }
620
- public var isDelete: Bool { raw.operation == "delete" }
621
-
622
- public init?(_ row: SyncularChangedRow) {
623
- guard row.table == "tasks" else { return nil }
624
- self.raw = row
625
- self.changed = TaskChangedFields(row.changedFields)
626
- self.crdt = TaskChangedFields(row.crdtFields)
627
- }
628
- }
629
-
630
- public func taskChangedRows(_ rows: [SyncularChangedRow]) -> [TaskChangedRow] {
631
- rows.compactMap(TaskChangedRow.init)
632
- }
633
-
634
- public func taskChangedRows(in event: SyncularNativeEvent) -> [TaskChangedRow] {
635
- taskChangedRows(event.changedRows)
636
- }
637
-
638
- public struct SyncularGeneratedOperation: Codable, Equatable {
639
- public let table: String
640
- public let rowId: String
641
- public let op: SyncularGeneratedOperationKind
642
- public let payload: [String: SyncularJsonValue]?
643
- public let baseVersion: Int64?
644
-
645
- public init(table: String, rowId: String, op: SyncularGeneratedOperationKind, payload: [String: SyncularJsonValue]?, baseVersion: Int64?) {
646
- self.table = table
647
- self.rowId = rowId
648
- self.op = op
649
- self.payload = payload
650
- self.baseVersion = baseVersion
651
- }
652
-
653
- private enum CodingKeys: String, CodingKey {
654
- case table
655
- case rowId = "row_id"
656
- case op
657
- case payload
658
- case baseVersion = "base_version"
659
- }
660
-
661
- public func jsonString() throws -> String {
662
- let encoder = JSONEncoder()
663
- encoder.outputFormatting = [.sortedKeys]
664
- return String(data: try encoder.encode(self), encoding: .utf8)!
665
- }
666
-
667
- public func encode(to encoder: Encoder) throws {
668
- var container = encoder.container(keyedBy: CodingKeys.self)
669
- try container.encode(table, forKey: .table)
670
- try container.encode(rowId, forKey: .rowId)
671
- try container.encode(op, forKey: .op)
672
- if let payload {
673
- try container.encode(payload, forKey: .payload)
674
- } else {
675
- try container.encodeNil(forKey: .payload)
676
- }
677
- if let baseVersion {
678
- try container.encode(baseVersion, forKey: .baseVersion)
679
- } else {
680
- try container.encodeNil(forKey: .baseVersion)
681
- }
682
- }
683
- }
684
-
685
- public struct SyncularFieldEncryptionRule: Codable, Equatable {
686
- public let scope: String
687
- public let table: String?
688
- public let fields: [String]
689
- public let rowIdField: String?
690
-
691
- public init(scope: String, table: String?, fields: [String], rowIdField: String?) {
692
- self.scope = scope
693
- self.table = table
694
- self.fields = fields
695
- self.rowIdField = rowIdField
696
- }
697
- }
698
-
699
- public struct SyncularFieldEncryptionConfig: Codable, Equatable {
700
- public let rules: [SyncularFieldEncryptionRule]
701
- public let keys: [String: String]
702
- public let encryptionKid: String?
703
- public let decryptionErrorMode: String?
704
- public let envelopePrefix: String?
705
-
706
- public init(keys: [String: String], rules: [SyncularFieldEncryptionRule] = syncularGeneratedFieldEncryptionRules, encryptionKid: String? = nil, decryptionErrorMode: String? = nil, envelopePrefix: String? = nil) {
707
- self.rules = rules
708
- self.keys = keys
709
- self.encryptionKid = encryptionKid
710
- self.decryptionErrorMode = decryptionErrorMode
711
- self.envelopePrefix = envelopePrefix
712
- }
713
-
714
- public func jsonString() throws -> String {
715
- let encoder = JSONEncoder()
716
- encoder.outputFormatting = [.sortedKeys]
717
- return String(data: try encoder.encode(self), encoding: .utf8)!
718
- }
719
- }
720
-
721
- public let syncularGeneratedFieldEncryptionRules: [SyncularFieldEncryptionRule] = [
722
- ]
723
-
724
- public func syncularGeneratedFieldEncryptionConfigJson(keys: [String: String], encryptionKid: String? = nil, decryptionErrorMode: String? = nil, envelopePrefix: String? = nil) throws -> String {
725
- try SyncularFieldEncryptionConfig(keys: keys, rules: syncularGeneratedFieldEncryptionRules, encryptionKid: encryptionKid, decryptionErrorMode: decryptionErrorMode, envelopePrefix: envelopePrefix).jsonString()
726
- }
727
-
728
- public protocol SyncularNativeJsonClient {
729
- func applyMutationJson(mutationJson: String, localRowJson: String?) throws -> String
730
- func applyLeasedMutationJson(mutationJson: String, localRowJson: String?) throws -> String
731
- func enqueueMutationJson(mutationJson: String, localRowJson: String?) throws -> String
732
- func enqueueLeasedMutationJson(mutationJson: String, localRowJson: String?) throws -> String
733
- func issueAuthLeaseJson(requestJson: String) throws -> String
734
- func queryJson(requestJson: String) throws -> String
735
- func registerQueryJson(queryJson: String) throws -> String
736
- func unregisterQuery(id: String) throws -> Bool
737
- func diagnosticSnapshotJson() throws -> String
738
- }
739
-
740
- public extension SyncularNativeJsonClient {
741
- func apply(_ operation: SyncularGeneratedOperation, localRowJson: String? = nil) throws -> String {
742
- try applyMutationJson(mutationJson: operation.jsonString(), localRowJson: localRowJson)
743
- }
744
-
745
- func applyLeased(_ operation: SyncularGeneratedOperation, localRowJson: String? = nil) throws -> String {
746
- try applyLeasedMutationJson(mutationJson: operation.jsonString(), localRowJson: localRowJson)
747
- }
748
-
749
- func enqueue(_ operation: SyncularGeneratedOperation, localRowJson: String? = nil) throws -> String {
750
- try enqueueMutationJson(mutationJson: operation.jsonString(), localRowJson: localRowJson)
751
- }
752
-
753
- func enqueueLeased(_ operation: SyncularGeneratedOperation, localRowJson: String? = nil) throws -> String {
754
- try enqueueLeasedMutationJson(mutationJson: operation.jsonString(), localRowJson: localRowJson)
755
- }
756
-
757
- func issueAuthLease(_ request: SyncularAuthLeaseIssueRequest) throws -> SyncularAuthLeaseRecord {
758
- try syncularDecodeJson(issueAuthLeaseJson(requestJson: request.jsonString()), as: SyncularAuthLeaseRecord.self)
759
- }
760
-
761
- func diagnosticSnapshot() throws -> SyncularJsonValue {
762
- try syncularDecodeJson(diagnosticSnapshotJson(), as: SyncularJsonValue.self)
763
- }
764
-
765
- func query<Row: Decodable>(_ query: SyncularReadonlyQuery, as type: Row.Type) throws -> [Row] {
766
- try syncularDecodeQueryRows(queryJson(requestJson: query.jsonString()), as: Row.self)
767
- }
768
-
769
- func registerLiveQuery(_ registration: SyncularLiveQueryRegistration) throws -> String {
770
- try registerQueryJson(queryJson: registration.jsonString())
771
- }
772
- }
773
-
774
- public final class SyncularNativeLiveQuery<Row: Decodable> {
775
- public let id: String
776
- public let query: SyncularReadonlyQuery
777
- public let label: String?
778
- public private(set) var rows: [Row] = []
779
- private let rowType: Row.Type
780
-
781
- public init(id: String, query: SyncularReadonlyQuery, as rowType: Row.Type, label: String? = nil) {
782
- self.id = id
783
- self.query = query
784
- self.label = label
785
- self.rowType = rowType
786
- }
787
-
788
- @discardableResult
789
- public func start(on client: SyncularNativeJsonClient) throws -> [Row] {
790
- _ = try client.registerLiveQuery(SyncularLiveQueryRegistration(id: id, tables: query.tables, label: label))
791
- return try refresh(on: client)
792
- }
793
-
794
- @discardableResult
795
- public func refresh(on client: SyncularNativeJsonClient) throws -> [Row] {
796
- rows = try client.query(query, as: rowType)
797
- return rows
798
- }
799
-
800
- @discardableResult
801
- public func stop(on client: SyncularNativeJsonClient) throws -> Bool {
802
- try client.unregisterQuery(id: id)
803
- }
804
-
805
- public func matches(queryIds: [String]) -> Bool {
806
- queryIds.contains(id)
807
- }
808
-
809
- @discardableResult
810
- public func refreshIfChanged(event: SyncularNativeEvent, on client: SyncularNativeJsonClient) throws -> [Row]? {
811
- if syncularNativeEventRequiresFullRefresh(event) {
812
- return try refresh(on: client)
813
- }
814
- guard event.kind == "QueriesChanged", matches(queryIds: event.queries) else {
815
- return nil
816
- }
817
- return try refresh(on: client)
818
- }
819
-
820
- @discardableResult
821
- public func refreshIfChanged(eventJson: String, on client: SyncularNativeJsonClient) throws -> [Row]? {
822
- try refreshIfChanged(event: syncularDecodeNativeEvent(eventJson), on: client)
823
- }
824
- }
825
-
826
- private func syncularDecodeJson<T: Decodable>(_ json: String, as type: T.Type) throws -> T {
827
- try JSONDecoder().decode(T.self, from: Data(json.utf8))
828
- }
829
-
830
- private struct SyncularQueryResult<Row: Decodable>: Decodable {
831
- let rows: [Row]
832
- }
833
-
834
- private func syncularDecodeQueryRows<Row: Decodable>(_ json: String, as type: Row.Type) throws -> [Row] {
835
- try JSONDecoder().decode(SyncularQueryResult<Row>.self, from: Data(json.utf8)).rows
836
- }
837
-
838
- public protocol SyncularQueryValue {
839
- var syncularJsonValue: SyncularJsonValue { get }
840
- }
841
-
842
- extension String: SyncularQueryValue { public var syncularJsonValue: SyncularJsonValue { .string(self) } }
843
- extension Int: SyncularQueryValue { public var syncularJsonValue: SyncularJsonValue { .int(Int64(self)) } }
844
- extension Int64: SyncularQueryValue { public var syncularJsonValue: SyncularJsonValue { .int(self) } }
845
- extension Double: SyncularQueryValue { public var syncularJsonValue: SyncularJsonValue { .double(self) } }
846
- extension Bool: SyncularQueryValue { public var syncularJsonValue: SyncularJsonValue { .bool(self) } }
847
-
848
- extension SyncularBlobRef: SyncularQueryValue { public var syncularJsonValue: SyncularJsonValue { .string((try? jsonString()) ?? "{}") } }
849
-
850
- public struct SyncularQueryPredicate: Equatable {
851
- public let sql: String
852
- public let params: [SyncularJsonValue]
853
-
854
- public init(sql: String, params: [SyncularJsonValue] = []) {
855
- self.sql = sql
856
- self.params = params
857
- }
858
-
859
- public func and(_ other: SyncularQueryPredicate) -> SyncularQueryPredicate {
860
- SyncularQueryPredicate(sql: "((\(sql)) and (\(other.sql)))", params: params + other.params)
861
- }
862
-
863
- public func or(_ other: SyncularQueryPredicate) -> SyncularQueryPredicate {
864
- SyncularQueryPredicate(sql: "((\(sql)) or (\(other.sql)))", params: params + other.params)
865
- }
866
- }
867
-
868
- public struct SyncularQueryOrder: Equatable {
869
- public let sql: String
870
-
871
- public init(sql: String) {
872
- self.sql = sql
873
- }
874
- }
875
-
876
- public struct SyncularQueryColumn<Value>: Equatable {
877
- public let table: String
878
- public let name: String
879
-
880
- public init(table: String, name: String) {
881
- self.table = table
882
- self.name = name
883
- }
884
-
885
- public func eq(_ value: Value) -> SyncularQueryPredicate where Value: SyncularQueryValue {
886
- SyncularQueryPredicate(sql: "\(syncularQuoteIdentifier(name)) = ?", params: [value.syncularJsonValue])
887
- }
888
-
889
- public func notEq(_ value: Value) -> SyncularQueryPredicate where Value: SyncularQueryValue {
890
- SyncularQueryPredicate(sql: "\(syncularQuoteIdentifier(name)) != ?", params: [value.syncularJsonValue])
891
- }
892
-
893
- public func gt(_ value: Value) -> SyncularQueryPredicate where Value: SyncularQueryValue {
894
- SyncularQueryPredicate(sql: "\(syncularQuoteIdentifier(name)) > ?", params: [value.syncularJsonValue])
895
- }
896
-
897
- public func gte(_ value: Value) -> SyncularQueryPredicate where Value: SyncularQueryValue {
898
- SyncularQueryPredicate(sql: "\(syncularQuoteIdentifier(name)) >= ?", params: [value.syncularJsonValue])
899
- }
900
-
901
- public func lt(_ value: Value) -> SyncularQueryPredicate where Value: SyncularQueryValue {
902
- SyncularQueryPredicate(sql: "\(syncularQuoteIdentifier(name)) < ?", params: [value.syncularJsonValue])
903
- }
904
-
905
- public func lte(_ value: Value) -> SyncularQueryPredicate where Value: SyncularQueryValue {
906
- SyncularQueryPredicate(sql: "\(syncularQuoteIdentifier(name)) <= ?", params: [value.syncularJsonValue])
907
- }
908
-
909
- public func isNull() -> SyncularQueryPredicate {
910
- SyncularQueryPredicate(sql: "\(syncularQuoteIdentifier(name)) is null", params: [])
911
- }
912
-
913
- public func isNotNull() -> SyncularQueryPredicate {
914
- SyncularQueryPredicate(sql: "\(syncularQuoteIdentifier(name)) is not null", params: [])
915
- }
916
-
917
- public func isIn(_ values: [Value]) -> SyncularQueryPredicate where Value: SyncularQueryValue {
918
- guard !values.isEmpty else { return SyncularQueryPredicate(sql: "0 = 1") }
919
- let placeholders = Array(repeating: "?", count: values.count).joined(separator: ", ")
920
- return SyncularQueryPredicate(sql: "\(syncularQuoteIdentifier(name)) in (\(placeholders))", params: values.map(\.syncularJsonValue))
921
- }
922
-
923
- public func notIn(_ values: [Value]) -> SyncularQueryPredicate where Value: SyncularQueryValue {
924
- guard !values.isEmpty else { return SyncularQueryPredicate(sql: "1 = 1") }
925
- let placeholders = Array(repeating: "?", count: values.count).joined(separator: ", ")
926
- return SyncularQueryPredicate(sql: "\(syncularQuoteIdentifier(name)) not in (\(placeholders))", params: values.map(\.syncularJsonValue))
927
- }
928
-
929
- public func asc() -> SyncularQueryOrder {
930
- SyncularQueryOrder(sql: "\(syncularQuoteIdentifier(name)) asc")
931
- }
932
-
933
- public func desc() -> SyncularQueryOrder {
934
- SyncularQueryOrder(sql: "\(syncularQuoteIdentifier(name)) desc")
935
- }
936
- }
937
-
938
- public struct SyncularQueryTable<Row: Decodable>: Equatable {
939
- public let name: String
940
- public let columns: [String]
941
-
942
- public init(name: String, columns: [String]) {
943
- self.name = name
944
- self.columns = columns
945
- }
946
-
947
- public func select() -> SyncularSelectQuery<Row> {
948
- SyncularSelectQuery(table: self)
949
- }
950
- }
951
-
952
- public struct SyncularSelectQuery<Row: Decodable>: Equatable {
953
- public let table: SyncularQueryTable<Row>
954
- public let predicates: [SyncularQueryPredicate]
955
- public let orders: [SyncularQueryOrder]
956
- public let limitValue: Int?
957
-
958
- public init(table: SyncularQueryTable<Row>, predicates: [SyncularQueryPredicate] = [], orders: [SyncularQueryOrder] = [], limitValue: Int? = nil) {
959
- self.table = table
960
- self.predicates = predicates
961
- self.orders = orders
962
- self.limitValue = limitValue
963
- }
964
-
965
- public func filter(_ predicate: SyncularQueryPredicate) -> Self {
966
- Self(table: table, predicates: predicates + [predicate], orders: orders, limitValue: limitValue)
967
- }
968
-
969
- public func orderBy(_ order: SyncularQueryOrder) -> Self {
970
- Self(table: table, predicates: predicates, orders: orders + [order], limitValue: limitValue)
971
- }
972
-
973
- public func limit(_ value: Int) -> Self {
974
- Self(table: table, predicates: predicates, orders: orders, limitValue: value)
975
- }
976
-
977
- public func readonlyQuery() -> SyncularReadonlyQuery {
978
- let columnSql = table.columns.map(syncularQuoteIdentifier).joined(separator: ", ")
979
- var sql = "select \(columnSql) from \(syncularQuoteIdentifier(table.name))"
980
- var params: [SyncularJsonValue] = []
981
- if !predicates.isEmpty {
982
- sql += " where " + predicates.map(\.sql).joined(separator: " and ")
983
- params = predicates.flatMap(\.params)
984
- }
985
- if !orders.isEmpty {
986
- sql += " order by " + orders.map(\.sql).joined(separator: ", ")
987
- }
988
- if let limitValue {
989
- sql += " limit \(limitValue)"
990
- }
991
- return SyncularReadonlyQuery(sql: sql, params: params, tables: [table.name])
992
- }
993
-
994
- public func fetch(on client: SyncularNativeJsonClient) throws -> [Row] {
995
- try client.query(readonlyQuery(), as: Row.self)
996
- }
997
-
998
- public func liveQuery(id: String, label: String? = nil) -> SyncularNativeLiveQuery<Row> {
999
- SyncularNativeLiveQuery(id: id, query: readonlyQuery(), as: Row.self, label: label)
1000
- }
1001
- }
1002
-
1003
- private func syncularQuoteIdentifier(_ identifier: String) -> String {
1004
- "\"" + identifier.replacingOccurrences(of: "\"", with: "\"\"") + "\""
1005
- }
1006
-
1007
- public struct TaskRow: Codable, Equatable {
1008
- public let id: String
1009
- public let title: String
1010
- public let completed: Int64
1011
- public let userId: String
1012
- public let createdAt: Int64
1013
- public let serverVersion: Int64
1014
-
1015
- private enum CodingKeys: String, CodingKey {
1016
- case id
1017
- case title
1018
- case completed
1019
- case userId = "user_id"
1020
- case createdAt = "created_at"
1021
- case serverVersion = "server_version"
1022
- }
1023
- }
1024
-
1025
- public struct NewTask: Codable, Equatable {
1026
- public let id: String
1027
- public let title: String
1028
- public let completed: Int64?
1029
- public let userId: String
1030
- public let createdAt: Int64?
1031
-
1032
- public init(id: String, title: String, completed: Int64? = nil, userId: String, createdAt: Int64? = nil) {
1033
- self.id = id
1034
- self.title = title
1035
- self.completed = completed
1036
- self.userId = userId
1037
- self.createdAt = createdAt
1038
- }
1039
-
1040
- private enum CodingKeys: String, CodingKey {
1041
- case id
1042
- case title
1043
- case completed
1044
- case userId = "user_id"
1045
- case createdAt = "created_at"
1046
- }
1047
- }
1048
-
1049
- public struct TaskPatch: Codable, Equatable {
1050
- public let title: String?
1051
- public let completed: Int64?
1052
- public let userId: String?
1053
- public let createdAt: Int64?
1054
-
1055
- public init(title: String? = nil, completed: Int64? = nil, userId: String? = nil, createdAt: Int64? = nil) {
1056
- self.title = title
1057
- self.completed = completed
1058
- self.userId = userId
1059
- self.createdAt = createdAt
1060
- }
1061
-
1062
- private enum CodingKeys: String, CodingKey {
1063
- case title
1064
- case completed
1065
- case userId = "user_id"
1066
- case createdAt = "created_at"
1067
- }
1068
- }
1069
-
1070
- public enum TaskQuery {
1071
- public static let table = SyncularQueryTable<TaskRow>(name: "tasks", columns: ["id", "title", "completed", "user_id", "created_at", "server_version"])
1072
- public static let id = SyncularQueryColumn<String>(table: "tasks", name: "id")
1073
- public static let title = SyncularQueryColumn<String>(table: "tasks", name: "title")
1074
- public static let completed = SyncularQueryColumn<Int64>(table: "tasks", name: "completed")
1075
- public static let userId = SyncularQueryColumn<String>(table: "tasks", name: "user_id")
1076
- public static let createdAt = SyncularQueryColumn<Int64>(table: "tasks", name: "created_at")
1077
- public static let serverVersion = SyncularQueryColumn<Int64>(table: "tasks", name: "server_version")
1078
- public static func select() -> SyncularSelectQuery<TaskRow> { table.select() }
1079
- }
1080
-
1081
- public enum SyncularAppOperations {
1082
- public static func newTask(_ input: NewTask, baseVersion: Int64? = 0) -> SyncularGeneratedOperation {
1083
- var payload: [String: SyncularJsonValue] = [:]
1084
- payload["title"] = .string(input.title)
1085
- payload["completed"] = .int(input.completed ?? 0)
1086
- payload["user_id"] = .string(input.userId)
1087
- payload["created_at"] = .int(input.createdAt ?? 0)
1088
- return SyncularGeneratedOperation(
1089
- table: "tasks",
1090
- rowId: input.id,
1091
- op: .upsert,
1092
- payload: payload,
1093
- baseVersion: baseVersion
1094
- )
1095
- }
1096
-
1097
- public static func patchTask(rowId: String, patch: TaskPatch, baseVersion: Int64? = nil) -> SyncularGeneratedOperation {
1098
- var payload: [String: SyncularJsonValue] = [:]
1099
- if let value = patch.title {
1100
- payload["title"] = .string(value)
1101
- }
1102
- if let value = patch.completed {
1103
- payload["completed"] = .int(value)
1104
- }
1105
- if let value = patch.userId {
1106
- payload["user_id"] = .string(value)
1107
- }
1108
- if let value = patch.createdAt {
1109
- payload["created_at"] = .int(value)
1110
- }
1111
- return SyncularGeneratedOperation(
1112
- table: "tasks",
1113
- rowId: rowId,
1114
- op: .upsert,
1115
- payload: payload,
1116
- baseVersion: baseVersion
1117
- )
1118
- }
1119
-
1120
- public static func deleteTask(rowId: String, baseVersion: Int64? = nil) -> SyncularGeneratedOperation {
1121
- SyncularGeneratedOperation(
1122
- table: "tasks",
1123
- rowId: rowId,
1124
- op: .delete,
1125
- payload: nil,
1126
- baseVersion: baseVersion
1127
- )
1128
- }
1129
-
1130
- }
1131
-
1132
- public struct SyncularAppMutations {
1133
- private let client: SyncularNativeJsonClient
1134
- private let queued: Bool
1135
- private let leased: Bool
1136
-
1137
- init(client: SyncularNativeJsonClient, queued: Bool = false, leased: Bool = false) {
1138
- self.client = client
1139
- self.queued = queued
1140
- self.leased = leased
1141
- }
1142
-
1143
- public var tasks: TaskMutations { TaskMutations(client: client, queued: queued, leased: leased) }
1144
- }
1145
-
1146
- public extension SyncularNativeJsonClient {
1147
- var mutations: SyncularAppMutations { SyncularAppMutations(client: self) }
1148
- var queuedMutations: SyncularAppMutations { SyncularAppMutations(client: self, queued: true) }
1149
- var leasedMutations: SyncularAppMutations { SyncularAppMutations(client: self, leased: true) }
1150
- var queuedLeasedMutations: SyncularAppMutations { SyncularAppMutations(client: self, queued: true, leased: true) }
1151
- }
1152
-
1153
- public struct TaskMutations {
1154
- private let client: SyncularNativeJsonClient
1155
- private let queued: Bool
1156
- private let leased: Bool
1157
-
1158
- init(client: SyncularNativeJsonClient, queued: Bool, leased: Bool) {
1159
- self.client = client
1160
- self.queued = queued
1161
- self.leased = leased
1162
- }
1163
-
1164
- public func insert(_ input: NewTask, baseVersion: Int64? = 0, localRowJson: String? = nil) throws -> String {
1165
- let operation = SyncularAppOperations.newTask(input, baseVersion: baseVersion)
1166
- if queued {
1167
- return leased ? try client.enqueueLeased(operation, localRowJson: localRowJson) : try client.enqueue(operation, localRowJson: localRowJson)
1168
- }
1169
- return leased ? try client.applyLeased(operation, localRowJson: localRowJson) : try client.apply(operation, localRowJson: localRowJson)
1170
- }
1171
-
1172
- public func update(rowId: String, patch: TaskPatch, baseVersion: Int64? = nil, localRowJson: String? = nil) throws -> String {
1173
- let operation = SyncularAppOperations.patchTask(rowId: rowId, patch: patch, baseVersion: baseVersion)
1174
- if queued {
1175
- return leased ? try client.enqueueLeased(operation, localRowJson: localRowJson) : try client.enqueue(operation, localRowJson: localRowJson)
1176
- }
1177
- return leased ? try client.applyLeased(operation, localRowJson: localRowJson) : try client.apply(operation, localRowJson: localRowJson)
1178
- }
1179
-
1180
- public func delete(rowId: String, baseVersion: Int64? = nil) throws -> String {
1181
- let operation = SyncularAppOperations.deleteTask(rowId: rowId, baseVersion: baseVersion)
1182
- if queued {
1183
- return leased ? try client.enqueueLeased(operation) : try client.enqueue(operation)
1184
- }
1185
- return leased ? try client.applyLeased(operation) : try client.apply(operation)
1186
- }
1187
-
1188
- }
1189
-
1190
- public extension SyncularNativeJsonClient {
1191
- }