tinybase 9.5.0 → 9.6.0-beta.1

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 (148) hide show
  1. package/@types/omni/index.d.ts +4 -0
  2. package/@types/omni/with-schemas/index.d.ts +4 -0
  3. package/@types/persisters/index.d.ts +8 -1
  4. package/@types/persisters/persister-better-sqlite3/index.d.ts +178 -0
  5. package/@types/persisters/persister-better-sqlite3/with-schemas/index.d.ts +202 -0
  6. package/@types/persisters/persister-capacitor-sqlite/index.d.ts +181 -0
  7. package/@types/persisters/persister-capacitor-sqlite/with-schemas/index.d.ts +205 -0
  8. package/@types/persisters/persister-indexed-db/index.d.ts +17 -9
  9. package/@types/persisters/persister-indexed-db/with-schemas/index.d.ts +18 -10
  10. package/@types/persisters/persister-libsql/index.d.ts +27 -0
  11. package/@types/persisters/persister-libsql/with-schemas/index.d.ts +27 -0
  12. package/@types/persisters/persister-pg/index.d.ts +197 -0
  13. package/@types/persisters/persister-pg/with-schemas/index.d.ts +222 -0
  14. package/@types/persisters/persister-supabase/index.d.ts +201 -0
  15. package/@types/persisters/persister-supabase/with-schemas/index.d.ts +217 -0
  16. package/@types/persisters/with-schemas/index.d.ts +8 -1
  17. package/@types/ui-react-dom/index.d.ts +10 -10
  18. package/@types/ui-react-dom/with-schemas/index.d.ts +10 -10
  19. package/@types/ui-react-dom-charts/index.d.ts +7 -7
  20. package/@types/ui-react-dom-charts/with-schemas/index.d.ts +7 -7
  21. package/@types/ui-react-inspector/index.d.ts +1 -1
  22. package/@types/ui-react-inspector/with-schemas/index.d.ts +1 -1
  23. package/@types/ui-solid-dom/index.d.ts +10 -10
  24. package/@types/ui-solid-dom/with-schemas/index.d.ts +10 -10
  25. package/@types/ui-solid-inspector/index.d.ts +1 -1
  26. package/@types/ui-solid-inspector/with-schemas/index.d.ts +1 -1
  27. package/@types/ui-svelte-dom/index.d.ts +10 -10
  28. package/@types/ui-svelte-dom/with-schemas/index.d.ts +10 -10
  29. package/@types/ui-svelte-inspector/index.d.ts +1 -1
  30. package/@types/ui-svelte-inspector/with-schemas/index.d.ts +1 -1
  31. package/agents.md +55 -2
  32. package/min/omni/index.js +1 -1
  33. package/min/omni/index.js.gz +0 -0
  34. package/min/omni/with-schemas/index.js +1 -1
  35. package/min/omni/with-schemas/index.js.gz +0 -0
  36. package/min/persisters/index.js +1 -1
  37. package/min/persisters/index.js.gz +0 -0
  38. package/min/persisters/persister-better-sqlite3/index.js +1 -0
  39. package/min/persisters/persister-better-sqlite3/index.js.gz +0 -0
  40. package/min/persisters/persister-better-sqlite3/with-schemas/index.js +1 -0
  41. package/min/persisters/persister-better-sqlite3/with-schemas/index.js.gz +0 -0
  42. package/min/persisters/persister-capacitor-sqlite/index.js +1 -0
  43. package/min/persisters/persister-capacitor-sqlite/index.js.gz +0 -0
  44. package/min/persisters/persister-capacitor-sqlite/with-schemas/index.js +1 -0
  45. package/min/persisters/persister-capacitor-sqlite/with-schemas/index.js.gz +0 -0
  46. package/min/persisters/persister-cr-sqlite-wasm/index.js +1 -1
  47. package/min/persisters/persister-cr-sqlite-wasm/index.js.gz +0 -0
  48. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +1 -1
  49. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js.gz +0 -0
  50. package/min/persisters/persister-durable-object-sql-storage/index.js +1 -1
  51. package/min/persisters/persister-durable-object-sql-storage/index.js.gz +0 -0
  52. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js +1 -1
  53. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js.gz +0 -0
  54. package/min/persisters/persister-electric-sql/index.js +1 -1
  55. package/min/persisters/persister-electric-sql/index.js.gz +0 -0
  56. package/min/persisters/persister-electric-sql/with-schemas/index.js +1 -1
  57. package/min/persisters/persister-electric-sql/with-schemas/index.js.gz +0 -0
  58. package/min/persisters/persister-expo-sqlite/index.js +1 -1
  59. package/min/persisters/persister-expo-sqlite/index.js.gz +0 -0
  60. package/min/persisters/persister-expo-sqlite/with-schemas/index.js +1 -1
  61. package/min/persisters/persister-expo-sqlite/with-schemas/index.js.gz +0 -0
  62. package/min/persisters/persister-indexed-db/index.js +1 -1
  63. package/min/persisters/persister-indexed-db/index.js.gz +0 -0
  64. package/min/persisters/persister-indexed-db/with-schemas/index.js +1 -1
  65. package/min/persisters/persister-indexed-db/with-schemas/index.js.gz +0 -0
  66. package/min/persisters/persister-libsql/index.js +1 -1
  67. package/min/persisters/persister-libsql/index.js.gz +0 -0
  68. package/min/persisters/persister-libsql/with-schemas/index.js +1 -1
  69. package/min/persisters/persister-libsql/with-schemas/index.js.gz +0 -0
  70. package/min/persisters/persister-pg/index.js +1 -0
  71. package/min/persisters/persister-pg/index.js.gz +0 -0
  72. package/min/persisters/persister-pg/with-schemas/index.js +1 -0
  73. package/min/persisters/persister-pg/with-schemas/index.js.gz +0 -0
  74. package/min/persisters/persister-pglite/index.js +1 -1
  75. package/min/persisters/persister-pglite/index.js.gz +0 -0
  76. package/min/persisters/persister-pglite/with-schemas/index.js +1 -1
  77. package/min/persisters/persister-pglite/with-schemas/index.js.gz +0 -0
  78. package/min/persisters/persister-postgres/index.js +1 -1
  79. package/min/persisters/persister-postgres/index.js.gz +0 -0
  80. package/min/persisters/persister-postgres/with-schemas/index.js +1 -1
  81. package/min/persisters/persister-postgres/with-schemas/index.js.gz +0 -0
  82. package/min/persisters/persister-powersync/index.js +1 -1
  83. package/min/persisters/persister-powersync/index.js.gz +0 -0
  84. package/min/persisters/persister-powersync/with-schemas/index.js +1 -1
  85. package/min/persisters/persister-powersync/with-schemas/index.js.gz +0 -0
  86. package/min/persisters/persister-react-native-sqlite/index.js +1 -1
  87. package/min/persisters/persister-react-native-sqlite/index.js.gz +0 -0
  88. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js +1 -1
  89. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js.gz +0 -0
  90. package/min/persisters/persister-sqlite-bun/index.js +1 -1
  91. package/min/persisters/persister-sqlite-bun/index.js.gz +0 -0
  92. package/min/persisters/persister-sqlite-bun/with-schemas/index.js +1 -1
  93. package/min/persisters/persister-sqlite-bun/with-schemas/index.js.gz +0 -0
  94. package/min/persisters/persister-sqlite-wasm/index.js +1 -1
  95. package/min/persisters/persister-sqlite-wasm/index.js.gz +0 -0
  96. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js +1 -1
  97. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js.gz +0 -0
  98. package/min/persisters/persister-sqlite3/index.js +1 -1
  99. package/min/persisters/persister-sqlite3/index.js.gz +0 -0
  100. package/min/persisters/persister-sqlite3/with-schemas/index.js +1 -1
  101. package/min/persisters/persister-sqlite3/with-schemas/index.js.gz +0 -0
  102. package/min/persisters/persister-supabase/index.js +1 -0
  103. package/min/persisters/persister-supabase/index.js.gz +0 -0
  104. package/min/persisters/persister-supabase/with-schemas/index.js +1 -0
  105. package/min/persisters/persister-supabase/with-schemas/index.js.gz +0 -0
  106. package/min/persisters/with-schemas/index.js +1 -1
  107. package/min/persisters/with-schemas/index.js.gz +0 -0
  108. package/omni/index.js +269 -34
  109. package/omni/with-schemas/index.js +269 -34
  110. package/package.json +164 -4
  111. package/persisters/index.js +23 -7
  112. package/persisters/persister-better-sqlite3/index.js +1767 -0
  113. package/persisters/persister-better-sqlite3/with-schemas/index.js +1767 -0
  114. package/persisters/persister-capacitor-sqlite/index.js +1769 -0
  115. package/persisters/persister-capacitor-sqlite/with-schemas/index.js +1769 -0
  116. package/persisters/persister-cr-sqlite-wasm/index.js +20 -6
  117. package/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +20 -6
  118. package/persisters/persister-durable-object-sql-storage/index.js +20 -7
  119. package/persisters/persister-durable-object-sql-storage/with-schemas/index.js +20 -7
  120. package/persisters/persister-electric-sql/index.js +20 -6
  121. package/persisters/persister-electric-sql/with-schemas/index.js +20 -6
  122. package/persisters/persister-expo-sqlite/index.js +20 -6
  123. package/persisters/persister-expo-sqlite/with-schemas/index.js +20 -6
  124. package/persisters/persister-indexed-db/index.js +48 -20
  125. package/persisters/persister-indexed-db/with-schemas/index.js +48 -20
  126. package/persisters/persister-libsql/index.js +20 -6
  127. package/persisters/persister-libsql/with-schemas/index.js +20 -6
  128. package/persisters/persister-pg/index.js +1905 -0
  129. package/persisters/persister-pg/with-schemas/index.js +1905 -0
  130. package/persisters/persister-pglite/index.js +20 -6
  131. package/persisters/persister-pglite/with-schemas/index.js +20 -6
  132. package/persisters/persister-postgres/index.js +20 -6
  133. package/persisters/persister-postgres/with-schemas/index.js +20 -6
  134. package/persisters/persister-powersync/index.js +27 -10
  135. package/persisters/persister-powersync/with-schemas/index.js +27 -10
  136. package/persisters/persister-react-native-sqlite/index.js +20 -6
  137. package/persisters/persister-react-native-sqlite/with-schemas/index.js +20 -6
  138. package/persisters/persister-sqlite-bun/index.js +20 -6
  139. package/persisters/persister-sqlite-bun/with-schemas/index.js +20 -6
  140. package/persisters/persister-sqlite-wasm/index.js +20 -6
  141. package/persisters/persister-sqlite-wasm/with-schemas/index.js +20 -6
  142. package/persisters/persister-sqlite3/index.js +20 -6
  143. package/persisters/persister-sqlite3/with-schemas/index.js +20 -6
  144. package/persisters/persister-supabase/index.js +883 -0
  145. package/persisters/persister-supabase/with-schemas/index.js +883 -0
  146. package/persisters/with-schemas/index.js +23 -7
  147. package/readme.md +14 -14
  148. package/releases.md +163 -66
@@ -0,0 +1,222 @@
1
+ /**
2
+ * The persister-pg module of the TinyBase project lets you save and load Store
3
+ * data to and from a PostgreSQL database, via the `pg` module (in an
4
+ * appropriate environment).
5
+ *
6
+ * Since the `pg` module is the de facto standard PostgreSQL driver for Node.js,
7
+ * this module also works with the many hosted PostgreSQL services that provide
8
+ * a `pg`-compatible driver, such as Neon.
9
+ * @see Database Persistence guide
10
+ * @packageDocumentation
11
+ * @module persister-pg
12
+ * @since 9.6.0
13
+ */
14
+ import type {Client, Pool} from 'pg';
15
+ import type {MergeableStore} from '../../../mergeable-store/with-schemas/index.d.ts';
16
+ import type {
17
+ OptionalSchemas,
18
+ Store,
19
+ } from '../../../store/with-schemas/index.d.ts';
20
+ import type {
21
+ DatabasePersisterConfig,
22
+ DpcJson,
23
+ Persister,
24
+ Persists,
25
+ } from '../../with-schemas/index.d.ts';
26
+
27
+ /**
28
+ * The PgPersister interface represents a Persister that lets you save and load
29
+ * Store data to and from a PostgreSQL database, via the `pg` module.
30
+ *
31
+ * You should use the createPgPersister function to create a PgPersister object.
32
+ *
33
+ * It is a minor extension to the Persister interface and simply provides an
34
+ * extra getPg method for accessing a reference to the database connection the
35
+ * Store is being persisted to.
36
+ * @category Persister
37
+ * @since 9.6.0
38
+ */
39
+ export interface PgPersister<Schemas extends OptionalSchemas> extends Persister<
40
+ Schemas,
41
+ Persists.StoreOrMergeableStore
42
+ > {
43
+ /**
44
+ * The getPg method returns a reference to the database connection the Store
45
+ * is being persisted to.
46
+ * @returns A reference to the `pg` Pool or Client.
47
+ * @example
48
+ * This example creates a Persister object against a newly-created Store and
49
+ * then gets the database connection back out again.
50
+ *
51
+ * ```js
52
+ * import {Pool} from 'pg';
53
+ * import {createStore} from 'tinybase';
54
+ * import {createPgPersister} from 'tinybase/persisters/persister-pg';
55
+ *
56
+ * const pool = new Pool({
57
+ * connectionString: 'postgres://localhost:5432/tinybase',
58
+ * });
59
+ * const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
60
+ * const persister = await createPgPersister(store, pool, 'my_tinybase');
61
+ *
62
+ * console.log(persister.getPg() == pool);
63
+ * // -> true
64
+ *
65
+ * await persister.destroy();
66
+ * await pool.end();
67
+ * ```
68
+ * @category Getter
69
+ * @since 9.6.0
70
+ */
71
+ getPg(): Pool | Client;
72
+ }
73
+
74
+ /**
75
+ * The createPgPersister function creates a PgPersister object that can persist
76
+ * the Store to a PostgreSQL database via the `pg` module.
77
+ *
78
+ * This has schema-based typing. The following is a simplified representation:
79
+ *
80
+ * ```ts override
81
+ * createPgPersister<StoreType extends Store>(
82
+ * store: StoreType,
83
+ * pg: Pool | Client,
84
+ * configOrStoreTableName?:
85
+ * | (NoInfer<StoreType> extends MergeableStore
86
+ * ? DpcJson
87
+ * : DatabasePersisterConfig)
88
+ * | string,
89
+ * onSqlCommand?: (sql: string, params?: any[]) => void,
90
+ * onIgnoredError?: (error: any) => void,
91
+ * ): Promise<PgPersister>;
92
+ * ```
93
+ *
94
+ * A PgPersister supports regular Store objects, and can also be used to persist
95
+ * the metadata of a MergeableStore when using the JSON serialization mode, as
96
+ * described below.
97
+ *
98
+ * As well as providing a reference to the Store to persist, you must provide a
99
+ * `pg` parameter which is either a Pool or a Client. If you provide a Pool, one
100
+ * connection is checked out for the lifetime of the Persister (and a second
101
+ * while automatic loading is enabled), since the Persister needs to issue
102
+ * transactions on a stable connection. Make sure your pool is large enough to
103
+ * accommodate that.
104
+ *
105
+ * A database Persister uses one of two modes: either a JSON serialization of
106
+ * the whole Store stored in a single row of a table (the default), or a tabular
107
+ * mapping of Table Ids to database table names and vice-versa).
108
+ *
109
+ * The third argument is a DatabasePersisterConfig object that configures which
110
+ * of those modes to use, and settings for each. If the third argument is simply
111
+ * a string, it is used as the `storeTableName` property of the JSON
112
+ * serialization.
113
+ *
114
+ * See the documentation for the DpcJson and DpcTabular types for more
115
+ * information on how both of those modes can be configured.
116
+ *
117
+ * Note: When using tabular mode, SQL NULL values are loaded as TinyBase null
118
+ * values, making tables dense (every Row has every Cell). See the Database
119
+ * Persistence guide for details.
120
+ *
121
+ * This method is asynchronous because it will await the creation of a dedicated
122
+ * new connection to the database. You will need to `await` a call to this
123
+ * function or handle the return type natively as a Promise.
124
+ * @param store The Store or MergeableStore to persist.
125
+ * @param pg The `pg` Pool or Client that identifies the database connection.
126
+ * @param configOrStoreTableName A DatabasePersisterConfig to configure the
127
+ * persistence mode (or a string to set the `storeTableName` property of the
128
+ * JSON serialization).
129
+ * @param onSqlCommand An optional handler called every time the Persister
130
+ * executes a SQL command or query. This is suitable for logging persistence
131
+ * behavior in a development environment.
132
+ * @param onIgnoredError An optional handler for the errors that the Persister
133
+ * would otherwise ignore when trying to save or load data. This is suitable for
134
+ * debugging persistence issues in a development environment.
135
+ * @returns A reference to the new PgPersister object.
136
+ * @example
137
+ * This example creates a PgPersister object and persists the Store to a local
138
+ * PostgreSQL database as a JSON serialization into the `my_tinybase` table. It
139
+ * makes a change to the database directly and then reloads it back into the
140
+ * Store.
141
+ *
142
+ * ```js
143
+ * import {Pool} from 'pg';
144
+ * import {createStore} from 'tinybase';
145
+ * import {createPgPersister} from 'tinybase/persisters/persister-pg';
146
+ *
147
+ * const pool = new Pool({
148
+ * connectionString: 'postgres://localhost:5432/tinybase',
149
+ * });
150
+ * const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
151
+ * const persister = await createPgPersister(store, pool, 'my_tinybase');
152
+ *
153
+ * await persister.save();
154
+ * // Store will be saved to the database.
155
+ *
156
+ * console.log((await pool.query('SELECT * FROM my_tinybase;')).rows);
157
+ * // -> [{_id: '_', store: '[{"pets":{"fido":{"species":"dog"}}},{}]'}]
158
+ *
159
+ * const json = '[{"pets":{"felix":{"species":"cat"}}},{}]';
160
+ * await pool.query('UPDATE my_tinybase SET store = $1 WHERE _id = $2;', [
161
+ * json,
162
+ * '_',
163
+ * ]);
164
+ *
165
+ * await persister.load();
166
+ * console.log(store.getTables());
167
+ * // -> {pets: {felix: {species: 'cat'}}}
168
+ *
169
+ * await persister.destroy();
170
+ * await pool.end();
171
+ * ```
172
+ * @example
173
+ * This example creates a PgPersister object and persists the Store to a local
174
+ * PostgreSQL database with tabular mapping.
175
+ *
176
+ * ```js
177
+ * import {Pool} from 'pg';
178
+ * import {createStore} from 'tinybase';
179
+ * import {createPgPersister} from 'tinybase/persisters/persister-pg';
180
+ *
181
+ * const pool = new Pool({
182
+ * connectionString: 'postgres://localhost:5432/tinybase',
183
+ * });
184
+ * const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
185
+ * const persister = await createPgPersister(store, pool, {
186
+ * mode: 'tabular',
187
+ * tables: {load: {pets: 'pets'}, save: {pets: 'pets'}},
188
+ * });
189
+ *
190
+ * await persister.save();
191
+ * console.log((await pool.query('SELECT * FROM pets;')).rows);
192
+ * // -> [{_id: 'fido', species: '"dog"'}]
193
+ * // Note that Cells and Values are JSON-encoded in PostgreSQL databases.
194
+ *
195
+ * await pool.query(
196
+ * `INSERT INTO pets (_id, species) VALUES ('felix', '"cat"')`,
197
+ * );
198
+ * await persister.load();
199
+ * console.log(store.getTables());
200
+ * // -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}
201
+ *
202
+ * await persister.destroy();
203
+ * await pool.query('DROP TABLE IF EXISTS pets');
204
+ * await pool.end();
205
+ * ```
206
+ * @category Creation
207
+ * @since 9.6.0
208
+ */
209
+ export function createPgPersister<Schemas extends OptionalSchemas>(
210
+ store: MergeableStore<Schemas>,
211
+ pg: Pool | Client,
212
+ configOrStoreTableName?: DpcJson | string,
213
+ onSqlCommand?: (sql: string, params?: any[]) => void,
214
+ onIgnoredError?: (error: any) => void,
215
+ ): Promise<PgPersister<Schemas>>;
216
+ export function createPgPersister<Schemas extends OptionalSchemas>(
217
+ store: Store<Schemas> & {getMergeableContent?: never},
218
+ pg: Pool | Client,
219
+ configOrStoreTableName?: DatabasePersisterConfig<Schemas> | string,
220
+ onSqlCommand?: (sql: string, params?: any[]) => void,
221
+ onIgnoredError?: (error: any) => void,
222
+ ): Promise<PgPersister<Schemas>>;
@@ -0,0 +1,201 @@
1
+ /**
2
+ * The persister-supabase module of the TinyBase project lets you save and load
3
+ * Store data to and from a Supabase project, via the `@supabase/supabase-js`
4
+ * module.
5
+ *
6
+ * Unlike the other PostgreSQL Persisters, this one talks to Supabase's REST API
7
+ * rather than to the database directly. That means it works in a browser or
8
+ * edge runtime, that row-level security policies apply to the data it reads and
9
+ * writes, and that changes can arrive over Supabase Realtime instead of
10
+ * polling.
11
+ *
12
+ * It also means that only the JSON serialization mode is available, since the
13
+ * REST API cannot execute the arbitrary SQL that tabular mapping needs.
14
+ *
15
+ * Most importantly, it means this Persister issues no DDL whatsoever, which is
16
+ * a real departure from how the other database Persisters behave. Those create
17
+ * their table when it is missing, add columns as new Cells appear, drop columns
18
+ * and tables that have become empty, and, in PostgreSQL, install the functions
19
+ * and event triggers they need to listen for changes and drop them again when
20
+ * they are destroyed. This one does none of that: it reads and writes a single
21
+ * row of a table you have already set up, and if that table is missing or
22
+ * shaped wrong, it reports the error rather than correcting the schema. Your
23
+ * database structure is yours, and stays exactly as your migrations left it.
24
+ *
25
+ * If you want a Persister that manages its own schema, or you need tabular
26
+ * mode, connect to the database directly with the persister-pg module instead.
27
+ * @see Database Persistence guide
28
+ * @packageDocumentation
29
+ * @module persister-supabase
30
+ * @since 9.6.0
31
+ */
32
+ import type {SupabaseClient} from '@supabase/supabase-js';
33
+ import type {MergeableStore} from '../../mergeable-store/index.d.ts';
34
+ import type {Store} from '../../store/index.d.ts';
35
+ import type {DpcJson, Persister, Persists} from '../index.d.ts';
36
+
37
+ /**
38
+ * The SupabasePersister interface represents a Persister that lets you save and
39
+ * load Store data to and from a Supabase project.
40
+ *
41
+ * You should use the createSupabasePersister function to create a
42
+ * SupabasePersister object.
43
+ *
44
+ * It is a minor extension to the Persister interface and simply provides an
45
+ * extra getSupabase method for accessing a reference to the Supabase client the
46
+ * Store is being persisted with.
47
+ * @category Persister
48
+ * @since 9.6.0
49
+ */
50
+ export interface SupabasePersister extends Persister<Persists.StoreOrMergeableStore> {
51
+ /**
52
+ * The getSupabase method returns a reference to the Supabase client the Store
53
+ * is being persisted with.
54
+ * @returns A reference to the Supabase client.
55
+ * @example
56
+ * This example creates a Persister object against a newly-created Store and
57
+ * then gets the Supabase client back out again.
58
+ *
59
+ * ```js ignore
60
+ * import {createClient} from '@supabase/supabase-js';
61
+ * import {createStore} from 'tinybase';
62
+ * import {createSupabasePersister} from 'tinybase/persisters/persister-supabase';
63
+ *
64
+ * const supabase = createClient(
65
+ * 'https://my-project.supabase.co',
66
+ * 'anon-key',
67
+ * );
68
+ * const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
69
+ * const persister = createSupabasePersister(store, supabase, 'my_tinybase');
70
+ *
71
+ * console.log(persister.getSupabase() == supabase);
72
+ * // -> true
73
+ *
74
+ * await persister.destroy();
75
+ * ```
76
+ * @category Getter
77
+ * @since 9.6.0
78
+ */
79
+ getSupabase(): SupabaseClient;
80
+ }
81
+
82
+ /**
83
+ * The createSupabasePersister function creates a SupabasePersister object that
84
+ * can persist the Store to a Supabase project.
85
+ *
86
+ * A SupabasePersister supports regular Store objects, and can also be used to
87
+ * persist the metadata of a MergeableStore.
88
+ *
89
+ * As well as providing a reference to the Store to persist, you must provide a
90
+ * `supabase` parameter which is the client returned from Supabase's
91
+ * `createClient` function.
92
+ *
93
+ * The Persister stores a JSON serialization of the whole Store in a single row
94
+ * of a table. The third argument is a DpcJson object that configures the table
95
+ * and column names to use, or, if it is simply a string, it is used as the
96
+ * `storeTableName` property.
97
+ *
98
+ * Unlike the other database Persisters, this one will not create or alter any
99
+ * of that structure for you, so you need to set it up yourself with a migration
100
+ * or the Supabase SQL editor before the Persister will work at all. By default
101
+ * it expects a table called `tinybase` with a text primary key called `_id` and
102
+ * a text column called `store`:
103
+ *
104
+ * ```sql ignore
105
+ * CREATE TABLE tinybase (_id text PRIMARY KEY, store text);
106
+ * ```
107
+ *
108
+ * Row-level security applies to everything the Persister does, so it will read
109
+ * nothing and save nothing until a policy grants access to it. What that policy
110
+ * should be depends on who is allowed to see the Store: the following lets any
111
+ * signed-in user read and write it, which suits a Store that a whole team
112
+ * shares, but a per-user Store wants a policy that compares `auth.uid()` to a
113
+ * column of its own instead.
114
+ *
115
+ * ```sql ignore
116
+ * ALTER TABLE tinybase ENABLE ROW LEVEL SECURITY;
117
+ * CREATE POLICY tinybase_access ON tinybase
118
+ * FOR ALL TO authenticated USING (true) WITH CHECK (true);
119
+ * ```
120
+ *
121
+ * For the startAutoLoad method to pick up changes made elsewhere, the table
122
+ * must also be published to Supabase Realtime:
123
+ *
124
+ * ```sql ignore
125
+ * ALTER PUBLICATION supabase_realtime ADD TABLE tinybase;
126
+ * ```
127
+ *
128
+ * Note that Realtime checks a client's `SELECT` policy before it will send that
129
+ * client a change, so a Store that loads and saves correctly but never
130
+ * auto-loads is usually a policy problem rather than a publication one.
131
+ *
132
+ * If you cannot enable Realtime, set the `autoLoadIntervalSeconds` property of
133
+ * the DpcJson object, and the Persister will additionally poll the table at
134
+ * that interval. It does not poll unless you provide that property.
135
+ * @param store The Store or MergeableStore to persist.
136
+ * @param supabase The Supabase client that was returned from `createClient`.
137
+ * @param configOrStoreTableName A DpcJson object to configure the table and
138
+ * column names (or a string to set the `storeTableName` property).
139
+ * @param onIgnoredError An optional handler for the errors that the Persister
140
+ * would otherwise ignore when trying to save or load data. This is suitable for
141
+ * debugging persistence issues in a development environment.
142
+ * @returns A reference to the new SupabasePersister object.
143
+ * @example
144
+ * This example creates a SupabasePersister object and persists the Store to a
145
+ * Supabase project as a JSON serialization into the `my_tinybase` table.
146
+ *
147
+ * ```js ignore
148
+ * import {createClient} from '@supabase/supabase-js';
149
+ * import {createStore} from 'tinybase';
150
+ * import {createSupabasePersister} from 'tinybase/persisters/persister-supabase';
151
+ *
152
+ * const supabase = createClient(
153
+ * 'https://my-project.supabase.co',
154
+ * 'anon-key',
155
+ * );
156
+ * const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
157
+ * const persister = createSupabasePersister(store, supabase, 'my_tinybase');
158
+ *
159
+ * await persister.save();
160
+ * // Store will be saved to the Supabase table.
161
+ *
162
+ * console.log((await supabase.from('my_tinybase').select('store')).data);
163
+ * // -> [{store: '[{"pets":{"fido":{"species":"dog"}}},{}]'}]
164
+ *
165
+ * await persister.destroy();
166
+ * ```
167
+ * @example
168
+ * This example creates a SupabasePersister object that automatically loads
169
+ * changes made to the table by other clients, over Supabase Realtime.
170
+ *
171
+ * ```js ignore
172
+ * import {createClient} from '@supabase/supabase-js';
173
+ * import {createStore} from 'tinybase';
174
+ * import {createSupabasePersister} from 'tinybase/persisters/persister-supabase';
175
+ *
176
+ * const supabase = createClient(
177
+ * 'https://my-project.supabase.co',
178
+ * 'anon-key',
179
+ * );
180
+ * const store = createStore();
181
+ * const persister = createSupabasePersister(store, supabase, {
182
+ * mode: 'json',
183
+ * storeTableName: 'my_tinybase',
184
+ * });
185
+ *
186
+ * await persister.startAutoLoad();
187
+ * await persister.startAutoSave();
188
+ *
189
+ * // Changes made by other clients now arrive over Realtime.
190
+ *
191
+ * await persister.destroy();
192
+ * ```
193
+ * @category Creation
194
+ * @since 9.6.0
195
+ */
196
+ export function createSupabasePersister(
197
+ store: Store | MergeableStore,
198
+ supabase: SupabaseClient,
199
+ configOrStoreTableName?: DpcJson | string,
200
+ onIgnoredError?: (error: any) => void,
201
+ ): SupabasePersister;
@@ -0,0 +1,217 @@
1
+ /**
2
+ * The persister-supabase module of the TinyBase project lets you save and load
3
+ * Store data to and from a Supabase project, via the `@supabase/supabase-js`
4
+ * module.
5
+ *
6
+ * Unlike the other PostgreSQL Persisters, this one talks to Supabase's REST API
7
+ * rather than to the database directly. That means it works in a browser or
8
+ * edge runtime, that row-level security policies apply to the data it reads and
9
+ * writes, and that changes can arrive over Supabase Realtime instead of
10
+ * polling.
11
+ *
12
+ * It also means that only the JSON serialization mode is available, since the
13
+ * REST API cannot execute the arbitrary SQL that tabular mapping needs.
14
+ *
15
+ * Most importantly, it means this Persister issues no DDL whatsoever, which is
16
+ * a real departure from how the other database Persisters behave. Those create
17
+ * their table when it is missing, add columns as new Cells appear, drop columns
18
+ * and tables that have become empty, and, in PostgreSQL, install the functions
19
+ * and event triggers they need to listen for changes and drop them again when
20
+ * they are destroyed. This one does none of that: it reads and writes a single
21
+ * row of a table you have already set up, and if that table is missing or
22
+ * shaped wrong, it reports the error rather than correcting the schema. Your
23
+ * database structure is yours, and stays exactly as your migrations left it.
24
+ *
25
+ * If you want a Persister that manages its own schema, or you need tabular
26
+ * mode, connect to the database directly with the persister-pg module instead.
27
+ * @see Database Persistence guide
28
+ * @packageDocumentation
29
+ * @module persister-supabase
30
+ * @since 9.6.0
31
+ */
32
+ import type {SupabaseClient} from '@supabase/supabase-js';
33
+ import type {MergeableStore} from '../../../mergeable-store/with-schemas/index.d.ts';
34
+ import type {
35
+ OptionalSchemas,
36
+ Store,
37
+ } from '../../../store/with-schemas/index.d.ts';
38
+ import type {DpcJson, Persister, Persists} from '../../with-schemas/index.d.ts';
39
+
40
+ /**
41
+ * The SupabasePersister interface represents a Persister that lets you save and
42
+ * load Store data to and from a Supabase project.
43
+ *
44
+ * You should use the createSupabasePersister function to create a
45
+ * SupabasePersister object.
46
+ *
47
+ * It is a minor extension to the Persister interface and simply provides an
48
+ * extra getSupabase method for accessing a reference to the Supabase client the
49
+ * Store is being persisted with.
50
+ * @category Persister
51
+ * @since 9.6.0
52
+ */
53
+ export interface SupabasePersister<
54
+ Schemas extends OptionalSchemas,
55
+ > extends Persister<Schemas, Persists.StoreOrMergeableStore> {
56
+ /**
57
+ * The getSupabase method returns a reference to the Supabase client the Store
58
+ * is being persisted with.
59
+ * @returns A reference to the Supabase client.
60
+ * @example
61
+ * This example creates a Persister object against a newly-created Store and
62
+ * then gets the Supabase client back out again.
63
+ *
64
+ * ```js ignore
65
+ * import {createClient} from '@supabase/supabase-js';
66
+ * import {createStore} from 'tinybase';
67
+ * import {createSupabasePersister} from 'tinybase/persisters/persister-supabase';
68
+ *
69
+ * const supabase = createClient(
70
+ * 'https://my-project.supabase.co',
71
+ * 'anon-key',
72
+ * );
73
+ * const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
74
+ * const persister = createSupabasePersister(store, supabase, 'my_tinybase');
75
+ *
76
+ * console.log(persister.getSupabase() == supabase);
77
+ * // -> true
78
+ *
79
+ * await persister.destroy();
80
+ * ```
81
+ * @category Getter
82
+ * @since 9.6.0
83
+ */
84
+ getSupabase(): SupabaseClient;
85
+ }
86
+
87
+ /**
88
+ * The createSupabasePersister function creates a SupabasePersister object that
89
+ * can persist the Store to a Supabase project.
90
+ *
91
+ * This has schema-based typing. The following is a simplified representation:
92
+ *
93
+ * ```ts override
94
+ * createSupabasePersister(
95
+ * store: Store | MergeableStore,
96
+ * supabase: SupabaseClient,
97
+ * configOrStoreTableName?: DpcJson | string,
98
+ * onIgnoredError?: (error: any) => void,
99
+ * ): SupabasePersister;
100
+ * ```
101
+ *
102
+ * A SupabasePersister supports regular Store objects, and can also be used to
103
+ * persist the metadata of a MergeableStore.
104
+ *
105
+ * As well as providing a reference to the Store to persist, you must provide a
106
+ * `supabase` parameter which is the client returned from Supabase's
107
+ * `createClient` function.
108
+ *
109
+ * The Persister stores a JSON serialization of the whole Store in a single row
110
+ * of a table. The third argument is a DpcJson object that configures the table
111
+ * and column names to use, or, if it is simply a string, it is used as the
112
+ * `storeTableName` property.
113
+ *
114
+ * Unlike the other database Persisters, this one will not create or alter any
115
+ * of that structure for you, so you need to set it up yourself with a migration
116
+ * or the Supabase SQL editor before the Persister will work at all. By default
117
+ * it expects a table called `tinybase` with a text primary key called `_id` and
118
+ * a text column called `store`:
119
+ *
120
+ * ```sql ignore
121
+ * CREATE TABLE tinybase (_id text PRIMARY KEY, store text);
122
+ * ```
123
+ *
124
+ * Row-level security applies to everything the Persister does, so it will read
125
+ * nothing and save nothing until a policy grants access to it. What that policy
126
+ * should be depends on who is allowed to see the Store: the following lets any
127
+ * signed-in user read and write it, which suits a Store that a whole team
128
+ * shares, but a per-user Store wants a policy that compares `auth.uid()` to a
129
+ * column of its own instead.
130
+ *
131
+ * ```sql ignore
132
+ * ALTER TABLE tinybase ENABLE ROW LEVEL SECURITY;
133
+ * CREATE POLICY tinybase_access ON tinybase
134
+ * FOR ALL TO authenticated USING (true) WITH CHECK (true);
135
+ * ```
136
+ *
137
+ * For the startAutoLoad method to pick up changes made elsewhere, the table
138
+ * must also be published to Supabase Realtime:
139
+ *
140
+ * ```sql ignore
141
+ * ALTER PUBLICATION supabase_realtime ADD TABLE tinybase;
142
+ * ```
143
+ *
144
+ * Note that Realtime checks a client's `SELECT` policy before it will send that
145
+ * client a change, so a Store that loads and saves correctly but never
146
+ * auto-loads is usually a policy problem rather than a publication one.
147
+ *
148
+ * If you cannot enable Realtime, set the `autoLoadIntervalSeconds` property of
149
+ * the DpcJson object, and the Persister will additionally poll the table at
150
+ * that interval. It does not poll unless you provide that property.
151
+ * @param store The Store or MergeableStore to persist.
152
+ * @param supabase The Supabase client that was returned from `createClient`.
153
+ * @param configOrStoreTableName A DpcJson object to configure the table and
154
+ * column names (or a string to set the `storeTableName` property).
155
+ * @param onIgnoredError An optional handler for the errors that the Persister
156
+ * would otherwise ignore when trying to save or load data. This is suitable for
157
+ * debugging persistence issues in a development environment.
158
+ * @returns A reference to the new SupabasePersister object.
159
+ * @example
160
+ * This example creates a SupabasePersister object and persists the Store to a
161
+ * Supabase project as a JSON serialization into the `my_tinybase` table.
162
+ *
163
+ * ```js ignore
164
+ * import {createClient} from '@supabase/supabase-js';
165
+ * import {createStore} from 'tinybase';
166
+ * import {createSupabasePersister} from 'tinybase/persisters/persister-supabase';
167
+ *
168
+ * const supabase = createClient(
169
+ * 'https://my-project.supabase.co',
170
+ * 'anon-key',
171
+ * );
172
+ * const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
173
+ * const persister = createSupabasePersister(store, supabase, 'my_tinybase');
174
+ *
175
+ * await persister.save();
176
+ * // Store will be saved to the Supabase table.
177
+ *
178
+ * console.log((await supabase.from('my_tinybase').select('store')).data);
179
+ * // -> [{store: '[{"pets":{"fido":{"species":"dog"}}},{}]'}]
180
+ *
181
+ * await persister.destroy();
182
+ * ```
183
+ * @example
184
+ * This example creates a SupabasePersister object that automatically loads
185
+ * changes made to the table by other clients, over Supabase Realtime.
186
+ *
187
+ * ```js ignore
188
+ * import {createClient} from '@supabase/supabase-js';
189
+ * import {createStore} from 'tinybase';
190
+ * import {createSupabasePersister} from 'tinybase/persisters/persister-supabase';
191
+ *
192
+ * const supabase = createClient(
193
+ * 'https://my-project.supabase.co',
194
+ * 'anon-key',
195
+ * );
196
+ * const store = createStore();
197
+ * const persister = createSupabasePersister(store, supabase, {
198
+ * mode: 'json',
199
+ * storeTableName: 'my_tinybase',
200
+ * });
201
+ *
202
+ * await persister.startAutoLoad();
203
+ * await persister.startAutoSave();
204
+ *
205
+ * // Changes made by other clients now arrive over Realtime.
206
+ *
207
+ * await persister.destroy();
208
+ * ```
209
+ * @category Creation
210
+ * @since 9.6.0
211
+ */
212
+ export function createSupabasePersister<Schemas extends OptionalSchemas>(
213
+ store: Store<Schemas> | MergeableStore<Schemas>,
214
+ supabase: SupabaseClient,
215
+ configOrStoreTableName?: DpcJson | string,
216
+ onIgnoredError?: (error: any) => void,
217
+ ): SupabasePersister<Schemas>;
@@ -2110,11 +2110,18 @@ export function createCustomPersister<
2110
2110
  * ideas on how to build your own Persister type, and as functional examples.
2111
2111
  * Examine the implementation of the createSqlite3Persister function as a good
2112
2112
  * starting point, for example.
2113
+ *
2114
+ * Your `executeCommand` function is given SQL that uses SQLite's anonymous `?`
2115
+ * placeholders, with the parameters to bind to them in order. SQLite drivers
2116
+ * agree on that form, whereas they vary in how they handle the numbered
2117
+ * placeholders that PostgreSQL requires, so you should not need to rewrite
2118
+ * statements before passing them on.
2113
2119
  * @param store The Store to persist.
2114
2120
  * @param configOrStoreTableName A DatabasePersisterConfig object, or a string
2115
2121
  * that will be used as the name of the Store's table in the database.
2116
2122
  * @param executeCommand A function that will execute a command against the
2117
- * database.
2123
+ * database. It receives SQL with anonymous `?` placeholders, and the parameters
2124
+ * to bind to them in order.
2118
2125
  * @param addChangeListener A function that will register a listener for changes
2119
2126
  * to the database.
2120
2127
  * @param delChangeListener A function that will unregister the listener for