@proteinjs/db 1.43.0 → 1.45.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.
- package/CHANGELOG.md +22 -0
- package/dist/generated/index.js +1 -1
- package/dist/generated/index.js.map +1 -1
- package/dist/generated/test/index.d.ts.map +1 -1
- package/dist/generated/test/index.js +3 -1
- package/dist/generated/test/index.js.map +1 -1
- package/dist/src/Table.d.ts +37 -1
- package/dist/src/Table.d.ts.map +1 -1
- package/dist/src/Table.js.map +1 -1
- package/dist/src/auth/TableAuth.d.ts +11 -1
- package/dist/src/auth/TableAuth.d.ts.map +1 -1
- package/dist/src/auth/TableAuth.js +39 -13
- package/dist/src/auth/TableAuth.js.map +1 -1
- package/dist/src/source/SourceRecord.d.ts +2 -3
- package/dist/src/source/SourceRecord.d.ts.map +1 -1
- package/dist/src/source/SourceRecord.js +16 -2
- package/dist/src/source/SourceRecord.js.map +1 -1
- package/dist/src/source/SourceRecordLoader.d.ts +12 -0
- package/dist/src/source/SourceRecordLoader.d.ts.map +1 -1
- package/dist/src/source/SourceRecordLoader.js +72 -50
- package/dist/src/source/SourceRecordLoader.js.map +1 -1
- package/dist/src/tables/MigrationTable.d.ts +22 -4
- package/dist/src/tables/MigrationTable.d.ts.map +1 -1
- package/dist/src/tables/MigrationTable.js +38 -16
- package/dist/src/tables/MigrationTable.js.map +1 -1
- package/dist/test/SourceRecordLoader.test.js +151 -0
- package/dist/test/SourceRecordLoader.test.js.map +1 -1
- package/dist/test/TableAuthIdentity.test.d.ts +2 -0
- package/dist/test/TableAuthIdentity.test.d.ts.map +1 -0
- package/dist/test/TableAuthIdentity.test.js +110 -0
- package/dist/test/TableAuthIdentity.test.js.map +1 -0
- package/dist/test/reusable/SourceRecordSyncTests.d.ts.map +1 -1
- package/dist/test/reusable/SourceRecordSyncTests.js +124 -0
- package/dist/test/reusable/SourceRecordSyncTests.js.map +1 -1
- package/dist/test/util/tables/sourceRecordSyncTestTables.d.ts +17 -0
- package/dist/test/util/tables/sourceRecordSyncTestTables.d.ts.map +1 -1
- package/dist/test/util/tables/sourceRecordSyncTestTables.js +26 -1
- package/dist/test/util/tables/sourceRecordSyncTestTables.js.map +1 -1
- package/generated/index.ts +1 -1
- package/generated/test/index.ts +3 -1
- package/package.json +2 -2
- package/src/Table.ts +38 -1
- package/src/auth/TableAuth.ts +45 -20
- package/src/source/SourceRecord.ts +17 -4
- package/src/source/SourceRecordLoader.ts +30 -3
- package/src/tables/MigrationTable.ts +43 -16
- package/test/SourceRecordLoader.test.ts +90 -0
- package/test/TableAuthIdentity.test.ts +115 -0
- package/test/reusable/SourceRecordSyncTests.ts +72 -0
- package/test/util/tables/sourceRecordSyncTestTables.ts +25 -0
|
@@ -149,6 +149,7 @@ export const sourceRecordSyncTests = (
|
|
|
149
149
|
await db.delete(machineTable, {});
|
|
150
150
|
await db.delete(defaultPolicyTable, {});
|
|
151
151
|
await db.delete(sourceRecordSyncTestTables.InheritedStamp, {});
|
|
152
|
+
await db.delete(sourceRecordSyncTestTables.SyncDerivedName, {});
|
|
152
153
|
RecordingMachineAccountWatcher.updates = [];
|
|
153
154
|
});
|
|
154
155
|
|
|
@@ -729,6 +730,77 @@ export const sourceRecordSyncTests = (
|
|
|
729
730
|
});
|
|
730
731
|
});
|
|
731
732
|
|
|
733
|
+
describe('stamp hygiene: an unchanged declaration is never rewritten', () => {
|
|
734
|
+
const versioned = (displayName: string) => ({
|
|
735
|
+
table: defaultPolicyTable,
|
|
736
|
+
record: { id: 'v-1', email: 'v1@test.local', displayName },
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
test('a version bump alone re-stamps nothing: the next release leaves unchanged rows (and their updated stamp) alone', async () => {
|
|
740
|
+
const db = getDbAsSystem();
|
|
741
|
+
await bootAsSource('@test/pkg-a', [versioned('same')], '1.23.0');
|
|
742
|
+
const stamped = await db.get(defaultPolicyTable, { id: 'v-1' });
|
|
743
|
+
expect(stamped.sourcePackageVersion).toBe('1.23.0');
|
|
744
|
+
|
|
745
|
+
// The next release: the SAME declaration under a newer package version (every release
|
|
746
|
+
// bumps the declaring package). Pre-fix this counted the version stamp as drift and
|
|
747
|
+
// rewrote every row on every boot — `updated` churned on rows nobody changed.
|
|
748
|
+
const summary = await bootAsSource('@test/pkg-a', [versioned('same')], '1.24.0');
|
|
749
|
+
expect(summary[defaultPolicyTable.name]).toMatchObject({ updates: 0, unchanged: 1 });
|
|
750
|
+
const idle = await db.get(defaultPolicyTable, { id: 'v-1' });
|
|
751
|
+
expect(idle.updated.valueOf()).toBe(stamped.updated.valueOf());
|
|
752
|
+
// The stamp records the version that last WROTE the row's definition.
|
|
753
|
+
expect(idle.sourcePackageVersion).toBe('1.23.0');
|
|
754
|
+
|
|
755
|
+
// A genuine redefinition still lands, and moves the stamp with it.
|
|
756
|
+
const redefined = await bootAsSource('@test/pkg-a', [versioned('changed')], '1.24.0');
|
|
757
|
+
expect(redefined[defaultPolicyTable.name].updates).toBe(1);
|
|
758
|
+
const rewritten = await db.get(defaultPolicyTable, { id: 'v-1' });
|
|
759
|
+
expect(rewritten).toMatchObject({ displayName: 'changed', sourcePackageVersion: '1.24.0' });
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
test('a row with no version stamp is stamped exactly once', async () => {
|
|
763
|
+
const db = getDbAsSystem();
|
|
764
|
+
// Written before versions existed (or by a build that could not resolve its version).
|
|
765
|
+
await bootAsSource('@test/pkg-a', [versioned('same')]);
|
|
766
|
+
expect((await db.get(defaultPolicyTable, { id: 'v-1' })).sourcePackageVersion).toBeFalsy();
|
|
767
|
+
|
|
768
|
+
const first = await bootAsSource('@test/pkg-a', [versioned('same')], '2.0.0');
|
|
769
|
+
expect(first[defaultPolicyTable.name].updates).toBe(1);
|
|
770
|
+
expect((await db.get(defaultPolicyTable, { id: 'v-1' })).sourcePackageVersion).toBe('2.0.0');
|
|
771
|
+
|
|
772
|
+
const second = await bootAsSource('@test/pkg-a', [versioned('same')], '2.0.0');
|
|
773
|
+
expect(second[defaultPolicyTable.name]).toMatchObject({ updates: 0, unchanged: 1 });
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
test('fromDeclaration: derived fields land on insert, backfill existing rows exactly once, and never re-stamp', async () => {
|
|
777
|
+
const db = getDbAsSystem();
|
|
778
|
+
const derivedTable = sourceRecordSyncTestTables.SyncDerivedName;
|
|
779
|
+
// bootBuild names each seeded loader `${source}/SeededLoader${i}` — the declaration's
|
|
780
|
+
// own name is the part after the package, exactly what a migration's class name is.
|
|
781
|
+
const declaration = { table: derivedTable, record: { id: 'd-1', email: 'd1@test.local' } };
|
|
782
|
+
|
|
783
|
+
await boot([declaration]);
|
|
784
|
+
expect(await db.get(derivedTable, { id: 'd-1' })).toMatchObject({ name: 'SeededLoader0' });
|
|
785
|
+
|
|
786
|
+
// A row written before the derived field existed: backfilled ONCE by the next boot…
|
|
787
|
+
await db.update(derivedTable, { id: 'd-1', name: null });
|
|
788
|
+
const backfill = await boot([declaration]);
|
|
789
|
+
expect(backfill[derivedTable.name].updates).toBe(1);
|
|
790
|
+
const backfilled = await db.get(derivedTable, { id: 'd-1' });
|
|
791
|
+
expect(backfilled.name).toBe('SeededLoader0');
|
|
792
|
+
|
|
793
|
+
// …and never again: the derived value is part of the drift comparison like any field.
|
|
794
|
+
const idle = await boot([declaration]);
|
|
795
|
+
expect(idle[derivedTable.name]).toMatchObject({ updates: 0, unchanged: 1 });
|
|
796
|
+
expect((await db.get(derivedTable, { id: 'd-1' })).updated.valueOf()).toBe(backfilled.updated.valueOf());
|
|
797
|
+
|
|
798
|
+
// The derivation owns the field: a value typed into the record does not survive it.
|
|
799
|
+
await boot([{ table: derivedTable, record: { id: 'd-1', email: 'd1@test.local', name: 'typed-by-hand' } }]);
|
|
800
|
+
expect((await db.get(derivedTable, { id: 'd-1' })).name).toBe('SeededLoader0');
|
|
801
|
+
});
|
|
802
|
+
});
|
|
803
|
+
|
|
732
804
|
test('a natural key without a unique guarantee fails boot loudly', async () => {
|
|
733
805
|
// Local (unregistered, never created) on purpose: a registered table with an invalid
|
|
734
806
|
// naturalKey would poison every loader run in the process — validation fires before any
|
|
@@ -97,8 +97,33 @@ export class InheritedStampTable extends Table<InheritedStampRecord> {
|
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
export interface SyncDerivedNameRecord extends SourceRecord {
|
|
101
|
+
/** Derived by the sync from the DECLARATION (its loader's name) — never typed into the record. */
|
|
102
|
+
name?: string | null;
|
|
103
|
+
email: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The migration-ledger shape: a `name` column the sync fills from the declaration itself
|
|
108
|
+
* (`sourceRecordOptions.fromDeclaration` — the declaring loader's class name), kept on removal
|
|
109
|
+
* like the ledger. The options literal is cast so the suite compiles red-first against a
|
|
110
|
+
* loader that does not yet know the option.
|
|
111
|
+
*/
|
|
112
|
+
export class SyncDerivedNameTable extends Table<SyncDerivedNameRecord> {
|
|
113
|
+
name = 'db_test_sync_derived_name';
|
|
114
|
+
columns: Table<SyncDerivedNameRecord>['columns'] = withSourceRecordColumns<SyncDerivedNameRecord>({
|
|
115
|
+
name: new StringColumn('name'),
|
|
116
|
+
email: new StringColumn('email'),
|
|
117
|
+
});
|
|
118
|
+
sourceRecordOptions = {
|
|
119
|
+
onSourceRemoved: 'keep',
|
|
120
|
+
fromDeclaration: (declaration: { name: string }) => ({ name: declaration.name }),
|
|
121
|
+
} as Table<SyncDerivedNameRecord>['sourceRecordOptions'];
|
|
122
|
+
}
|
|
123
|
+
|
|
100
124
|
export const sourceRecordSyncTestTables = {
|
|
101
125
|
SyncMachineAccount: new SyncMachineAccountTable() as Table<SyncMachineAccount>,
|
|
102
126
|
SyncDefaultPolicy: new SyncDefaultPolicyTable() as Table<SyncDefaultPolicyRecord>,
|
|
103
127
|
InheritedStamp: new InheritedStampTable() as Table<InheritedStampRecord>,
|
|
128
|
+
SyncDerivedName: new SyncDerivedNameTable() as Table<SyncDerivedNameRecord>,
|
|
104
129
|
};
|