Skip to content

Commit 598fd96

Browse files
committed
Encode new schema options
1 parent b3205e2 commit 598fd96

File tree

4 files changed

+112
-18
lines changed

4 files changed

+112
-18
lines changed

packages/common/src/db/schema/Table.ts

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,22 @@ import { Index } from './Index.js';
1010
import { IndexedColumn } from './IndexedColumn.js';
1111
import { TableV2 } from './TableV2.js';
1212

13-
export interface TableOptions {
13+
interface SharedTableOptions {
14+
localOnly?: boolean;
15+
insertOnly?: boolean;
16+
viewName?: string;
17+
includeOld?: boolean | 'when-changed';
18+
includeMetadata?: boolean;
19+
ignoreEmptyUpdate?: boolean;
20+
}
21+
22+
export interface TableOptions extends SharedTableOptions {
1423
/**
1524
* The synced table name, matching sync rules
1625
*/
1726
name: string;
1827
columns: Column[];
1928
indexes?: Index[];
20-
localOnly?: boolean;
21-
insertOnly?: boolean;
22-
viewName?: string;
2329
}
2430

2531
export type RowType<T extends TableV2<any>> = {
@@ -30,17 +36,17 @@ export type RowType<T extends TableV2<any>> = {
3036

3137
export type IndexShorthand = Record<string, string[]>;
3238

33-
export interface TableV2Options {
39+
export interface TableV2Options extends SharedTableOptions {
3440
indexes?: IndexShorthand;
35-
localOnly?: boolean;
36-
insertOnly?: boolean;
37-
viewName?: string;
3841
}
3942

4043
export const DEFAULT_TABLE_OPTIONS = {
4144
indexes: [],
4245
insertOnly: false,
43-
localOnly: false
46+
localOnly: false,
47+
includeOld: false,
48+
includeMetadata: false,
49+
ignoreEmptyUpdate: false,
4450
};
4551

4652
export const InvalidSQLCharacters = /["'%,.#\s[\]]/;
@@ -144,10 +150,9 @@ export class Table<Columns extends ColumnsType = ColumnsType> {
144150
private initTableV1(options: TableOptions) {
145151
this.options = {
146152
...options,
147-
indexes: options.indexes || [],
148-
insertOnly: options.insertOnly ?? DEFAULT_TABLE_OPTIONS.insertOnly,
149-
localOnly: options.localOnly ?? DEFAULT_TABLE_OPTIONS.localOnly
153+
indexes: options.indexes || []
150154
};
155+
this.applyDefaultOptions();
151156
}
152157

153158
private initTableV2(columns: Columns, options?: TableV2Options) {
@@ -173,14 +178,26 @@ export class Table<Columns extends ColumnsType = ColumnsType> {
173178
name: '',
174179
columns: convertedColumns,
175180
indexes: convertedIndexes,
176-
insertOnly: options?.insertOnly ?? DEFAULT_TABLE_OPTIONS.insertOnly,
177-
localOnly: options?.localOnly ?? DEFAULT_TABLE_OPTIONS.localOnly,
178-
viewName: options?.viewName
181+
viewName: options?.viewName,
182+
insertOnly: options?.insertOnly,
183+
localOnly: options?.localOnly,
184+
includeOld: options?.includeOld,
185+
includeMetadata: options?.includeMetadata,
186+
ignoreEmptyUpdate: options?.ignoreEmptyUpdate
179187
};
188+
this.applyDefaultOptions();
180189

181190
this._mappedColumns = columns;
182191
}
183192

193+
private applyDefaultOptions() {
194+
this.options.insertOnly ??= DEFAULT_TABLE_OPTIONS.insertOnly;
195+
this.options.localOnly ??= DEFAULT_TABLE_OPTIONS.localOnly;
196+
this.options.includeOld ??= DEFAULT_TABLE_OPTIONS.includeOld;
197+
this.options.includeMetadata ??= DEFAULT_TABLE_OPTIONS.includeMetadata;
198+
this.options.ignoreEmptyUpdate ??= DEFAULT_TABLE_OPTIONS.ignoreEmptyUpdate;
199+
}
200+
184201
get name() {
185202
return this.options.name;
186203
}
@@ -212,11 +229,23 @@ export class Table<Columns extends ColumnsType = ColumnsType> {
212229
}
213230

214231
get localOnly() {
215-
return this.options.localOnly ?? false;
232+
return this.options.localOnly!;
216233
}
217234

218235
get insertOnly() {
219-
return this.options.insertOnly ?? false;
236+
return this.options.insertOnly!;
237+
}
238+
239+
get includeOld() {
240+
return this.options.includeOld!;
241+
}
242+
243+
get includeMetadata() {
244+
return this.options.includeMetadata!;
245+
}
246+
247+
get ignoreEmptyUpdate() {
248+
return this.options.ignoreEmptyUpdate!;
220249
}
221250

222251
get internalName() {
@@ -250,6 +279,13 @@ export class Table<Columns extends ColumnsType = ColumnsType> {
250279
throw new Error(`Table has too many columns. The maximum number of columns is ${MAX_AMOUNT_OF_COLUMNS}.`);
251280
}
252281

282+
if (this.includeMetadata && this.localOnly) {
283+
throw new Error(`Can't include metadata for local-only tables.`);
284+
}
285+
if (this.includeOld != false && this.localOnly) {
286+
throw new Error(`Can't include old values for local-only tables.`);
287+
}
288+
253289
const columnNames = new Set<string>();
254290
columnNames.add('id');
255291
for (const column of this.columns) {
@@ -291,6 +327,10 @@ export class Table<Columns extends ColumnsType = ColumnsType> {
291327
view_name: this.viewName,
292328
local_only: this.localOnly,
293329
insert_only: this.insertOnly,
330+
include_old: this.includeOld != false,
331+
include_old_only_when_changed: this.includeOld == 'when-changed',
332+
include_metadata: this.includeMetadata,
333+
ignore_empty_update: this.ignoreEmptyUpdate,
294334
columns: this.columns.map((c) => c.toJSON()),
295335
indexes: this.indexes.map((e) => e.toJSON(this))
296336
};

packages/common/tests/db/schema/Schema.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ describe('Schema', () => {
9090
view_name: 'users',
9191
local_only: false,
9292
insert_only: false,
93+
ignore_empty_update: false,
94+
include_metadata: false,
95+
include_old: false,
96+
include_old_only_when_changed: false,
9397
columns: [
9498
{ name: 'name', type: 'TEXT' },
9599
{ name: 'age', type: 'INTEGER' }
@@ -101,6 +105,10 @@ describe('Schema', () => {
101105
view_name: 'posts',
102106
local_only: false,
103107
insert_only: false,
108+
ignore_empty_update: false,
109+
include_metadata: false,
110+
include_old: false,
111+
include_old_only_when_changed: false,
104112
columns: [
105113
{ name: 'title', type: 'TEXT' },
106114
{ name: 'content', type: 'TEXT' }

packages/common/tests/db/schema/Table.test.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it, expect } from 'vitest';
2-
import { Table } from '../../../src/db/schema/Table';
2+
import { Table, TableV2Options } from '../../../src/db/schema/Table';
33
import { column, Column, ColumnType } from '../../../src/db/schema/Column';
44
import { Index } from '../../../src/db/schema/Index';
55
import { IndexedColumn } from '../../../src/db/schema/IndexedColumn';
@@ -103,6 +103,10 @@ describe('Table', () => {
103103
view_name: 'customView',
104104
local_only: false,
105105
insert_only: false,
106+
ignore_empty_update: false,
107+
include_metadata: false,
108+
include_old: false,
109+
include_old_only_when_changed: false,
106110
columns: [
107111
{ name: 'name', type: 'TEXT' },
108112
{ name: 'age', type: 'INTEGER' }
@@ -126,6 +130,22 @@ describe('Table', () => {
126130
expect(table.indexes[0].columns[0].ascending).toBe(false);
127131
});
128132

133+
it('should handle options', () => {
134+
function createTable(options: TableV2Options) {
135+
return new Table({name: column.text}, options);
136+
}
137+
138+
expect(createTable({}).toJSON().include_metadata).toBe(false);
139+
expect(createTable({includeMetadata: true}).toJSON().include_metadata).toBe(true);
140+
141+
expect(createTable({includeOld: true}).toJSON().include_old).toBe(true);
142+
expect(createTable({includeOld: true}).toJSON().include_old_only_when_changed).toBe(false);
143+
expect(createTable({includeOld: 'when-changed'}).toJSON().include_old).toBe(true);
144+
expect(createTable({includeOld: 'when-changed'}).toJSON().include_old_only_when_changed).toBe(true);
145+
146+
expect(createTable({ignoreEmptyUpdate: true}).toJSON().ignore_empty_update).toBe(true);
147+
});
148+
129149
describe('validate', () => {
130150
it('should throw an error for invalid view names', () => {
131151
expect(() => {
@@ -173,5 +193,27 @@ describe('Table', () => {
173193
}).validate()
174194
).toThrowError('Invalid characters in column name: #invalid-name');
175195
});
196+
197+
it('should throw an error for local-only tables with metadata', () => {
198+
expect(() =>
199+
new Table({
200+
name: column.text,
201+
}, { localOnly: true, includeMetadata: true }).validate()
202+
).toThrowError("Can't include metadata for local-only tables.");
203+
});
204+
205+
it('should throw an error for local-only tables tracking old values', () => {
206+
expect(() =>
207+
new Table({
208+
name: column.text,
209+
}, { localOnly: true, includeOld: true }).validate()
210+
).toThrowError("Can't include old values for local-only tables.");
211+
212+
expect(() =>
213+
new Table({
214+
name: column.text,
215+
}, { localOnly: true, includeOld: 'when-changed' }).validate()
216+
).toThrowError("Can't include old values for local-only tables.");
217+
});
176218
});
177219
});

packages/common/tests/db/schema/TableV2.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ describe('TableV2', () => {
7979
view_name: 'customView',
8080
local_only: false,
8181
insert_only: false,
82+
ignore_empty_update: false,
83+
include_metadata: false,
84+
include_old: false,
85+
include_old_only_when_changed: false,
8286
columns: [
8387
{ name: 'name', type: 'TEXT' },
8488
{ name: 'age', type: 'INTEGER' }

0 commit comments

Comments
 (0)