create-syncular-app 0.15.13 → 0.15.15

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-syncular-app",
3
- "version": "0.15.13",
3
+ "version": "0.15.15",
4
4
  "description": "Scaffold a new Syncular app: `create-syncular-app`",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Benjamin Kniffler",
@@ -49,6 +49,6 @@
49
49
  "!dist/**/*.test.d.ts"
50
50
  ],
51
51
  "devDependencies": {
52
- "@syncular/typegen": "0.15.13"
52
+ "@syncular/typegen": "0.15.15"
53
53
  }
54
54
  }
@@ -26,6 +26,21 @@ export function syqlPresent<T>(value: T): SyqlPresent<T> {
26
26
  return { present: true, value };
27
27
  }
28
28
 
29
+ /** A generated named-query row did not match its analyzed result type. */
30
+ export class QueryResultDecodeError extends TypeError {
31
+ readonly name = 'QueryResultDecodeError';
32
+ constructor(readonly query: string, readonly column: string) {
33
+ super(query + ': result column ' + column + ' is not a SQLite boolean');
34
+ }
35
+ }
36
+
37
+ /** Lift a SQLite boolean: preserve booleans, map 0 to false and finite non-zero numbers to true. */
38
+ function decodeQueryBoolean(value: unknown, query: string, column: string): boolean {
39
+ if (typeof value === 'boolean') return value;
40
+ if (typeof value === 'number' && Number.isFinite(value)) return value !== 0;
41
+ throw new QueryResultDecodeError(query, column);
42
+ }
43
+
29
44
  /** A bindable SQL param/row value (the wrapper's SqlValue subset). */
30
45
  export type QueryValue =
31
46
  | string
@@ -54,6 +69,7 @@ export interface NamedQuery<Row, Params = undefined> {
54
69
  readonly id: string;
55
70
  readonly hasParams: boolean;
56
71
  readonly sql: string;
72
+ readonly mapRow: (row: Readonly<Record<string, unknown>>) => Row;
57
73
  readonly tables: readonly string[];
58
74
  readonly bind: (params: Params) => readonly QueryValue[];
59
75
  readonly sqlFor?: (params: Params) => string;
@@ -89,6 +105,14 @@ export interface ListTodosRow {
89
105
  updatedAtMs: number;
90
106
  }
91
107
 
108
+ /** Decode one storage-shaped result row for 'listTodos'. */
109
+ export function listTodosMapRow(row: Readonly<Record<string, unknown>>): ListTodosRow {
110
+ return {
111
+ ...row,
112
+ done: decodeQueryBoolean(row['done'], 'listTodos', 'done'),
113
+ } as unknown as ListTodosRow;
114
+ }
115
+
92
116
  /** Public revision-1 SYQL inputs for 'listTodos'. */
93
117
  export interface ListTodosParams {
94
118
  listId: string;
@@ -123,7 +147,7 @@ function listTodosSelect(raw: ListTodosParams): { sql: string; bind: QueryValue[
123
147
  export async function listTodos(client: QueryClient, params: ListTodosParams): Promise<ListTodosRow[]> {
124
148
  const selected = listTodosSelect(params);
125
149
  const rows = await client.query(selected.sql, selected.bind);
126
- return rows as unknown as ListTodosRow[];
150
+ return rows.map((row) => listTodosMapRow(row as Readonly<Record<string, unknown>>));
127
151
  }
128
152
 
129
153
  /** Revisioned reactive descriptor for `useQuery(listTodosQuery, params)`. */
@@ -131,6 +155,7 @@ export const listTodosQuery: NamedQuery<ListTodosRow, ListTodosParams> = {
131
155
  id: 'sha256:7c07edc88f5e6f4b6da196da47e08abe4c7cb304d257a429da6950024f02fa85/listTodos',
132
156
  hasParams: true,
133
157
  sql: 'select id, list_id AS listId, title, done, position, updated_at_ms AS updatedAtMs from todos where todos.list_id = ? order by position, id',
158
+ mapRow: listTodosMapRow,
134
159
  sqlFor: (params: ListTodosParams) => listTodosSelect(params).sql,
135
160
  tables: listTodosTables,
136
161
  dependencies: (params) => [
@@ -21,7 +21,7 @@ tauri = { version = "2", features = [] }
21
21
  # The syncular plugin from crates.io. `native-transport` gives the core its
22
22
  # real HTTP + WebSocket transport (ureq + tungstenite) inside the host
23
23
  # process — the webview never talks to the sync server directly.
24
- tauri-plugin-syncular = { version = "0.15.13", features = ["native-transport"] }
24
+ tauri-plugin-syncular = { version = "0.15.15", features = ["native-transport"] }
25
25
 
26
26
  [features]
27
27
  default = []