Skip to content

Commit 1213da3

Browse files
committed
fix: Add static type interfaces for tables and resources
1 parent 14130b9 commit 1213da3

File tree

5 files changed

+203
-56
lines changed

5 files changed

+203
-56
lines changed

resources/DatabaseInterface.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { RocksDatabase } from '@harperfast/rocksdb-js';
2+
import { Database, type RootDatabase } from 'lmdb';
3+
4+
export interface LMDBDatabase extends Database {
5+
customIndex?: any;
6+
isIndexing?: boolean;
7+
indexNulls?: boolean;
8+
}
9+
10+
export interface LMDBRootDatabase extends RootDatabase {
11+
auditStore?: LMDBRootDatabase;
12+
databaseName?: string;
13+
dbisDb?: LMDBDatabase;
14+
isLegacy?: boolean;
15+
needsDeletion?: boolean;
16+
path?: string;
17+
status?: 'open' | 'closed';
18+
}
19+
20+
export interface RocksDatabaseEx extends RocksDatabase {
21+
customIndex?: any;
22+
env: Record<string, any>;
23+
isLegacy?: boolean;
24+
isIndexing?: boolean;
25+
indexNulls?: boolean;
26+
getEntry?: (id: string | number | (string | number)[] | Buffer, options?: any) => { value: any };
27+
}
28+
29+
export interface RocksRootDatabase extends RocksDatabaseEx {
30+
auditStore?: RocksDatabaseEx;
31+
databaseName?: string;
32+
dbisDb?: RocksDatabaseEx;
33+
}
34+
35+
export type DBI =
36+
| LMDBDatabase
37+
| (RocksDatabase & {
38+
customIndex?: any;
39+
isIndexing?: boolean;
40+
indexNulls?: boolean;
41+
rootStore?: RocksRootDatabase;
42+
});

resources/ResourceInterface.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { TableInterface } from '#js/resources/TableInterface';
12
import type { User } from '../security/user.ts';
23
import type { OperationFunctionName } from '../server/serverHelpers/serverUtilities.ts';
34
import { DatabaseTransaction } from './DatabaseTransaction.ts';
@@ -58,6 +59,86 @@ export interface ResourceInterface<Record extends object = any>
5859
getCurrentUser(): User | undefined;
5960
}
6061

62+
export interface ResourceStaticInterface<Record extends object = any> {
63+
new (
64+
identifier?: Id,
65+
source?: ResourceInterface<Record> & ResourceStaticInterface<Record>
66+
): ResourceInterface<Record>;
67+
68+
loadAsInstance?: boolean;
69+
70+
get?(
71+
target: RequestTargetOrId,
72+
dataOrContext?: Record | Context,
73+
context?: Context
74+
): Promise<Record> | Record | Record[] | Promise<Record[]>;
75+
put?(
76+
target: RequestTargetOrId,
77+
dataOrContext?: Record | Context,
78+
context?: Context
79+
): Promise<Record> | Record | void | Promise<void>;
80+
put?(
81+
target: RequestTargetOrId,
82+
dataOrContext?: Record[] | Context,
83+
context?: Context
84+
): Promise<Record[]> | Record[] | void | Promise<void>;
85+
patch?(
86+
target: RequestTargetOrId,
87+
dataOrContext?: Record | Context,
88+
context?: Context
89+
): Promise<Record> | Record | void | Promise<void>;
90+
delete?(
91+
target: RequestTargetOrId,
92+
dataOrContext?: Record | Context,
93+
context?: Context
94+
): Promise<Record> | Record | void | Promise<void>;
95+
96+
invalidate?(target: RequestTargetOrId, dataOrContext?: Record | Context, context?: Context): Promise<Record> | Record;
97+
post?(
98+
target: RequestTargetOrId,
99+
dataOrContext?: Record | Context,
100+
context?: Context
101+
): Promise<Record> | Record | void | Promise<void>;
102+
post?(
103+
target: RequestTargetOrId,
104+
dataOrContext?: Record[] | Context,
105+
context?: Context
106+
): Promise<Record[]> | Record[] | void | Promise<void>;
107+
update?(target: RequestTargetOrId, dataOrContext?: Record | Context, context?: Context): Promise<Record> | Record;
108+
connect?(target: RequestTargetOrId, dataOrContext?: Record | Context, context?: Context): IterableEventQueue;
109+
subscribe?(target: RequestTargetOrId, dataOrContext?: Record | Context, context?: Context): AsyncIterable<Record>;
110+
publish?(target: RequestTargetOrId, dataOrContext?: Record | Context, context?: Context): void;
111+
search?(target: RequestTargetOrId, dataOrContext?: Record | Context, context?: Context): Promise<Record[]> | Record[];
112+
query?(target: RequestTargetOrId, dataOrContext?: Record | Context, context?: Context): Promise<Record[]> | Record[];
113+
copy?(
114+
target: RequestTargetOrId,
115+
dataOrContext?: Record | Context,
116+
context?: Context
117+
): Promise<Record> | Record | void | Promise<void>;
118+
move?(
119+
target: RequestTargetOrId,
120+
dataOrContext?: Record | Context,
121+
context?: Context
122+
): Promise<Record> | Record | void | Promise<void>;
123+
post?(target: RequestTargetOrId, dataOrContext?: Record | Context, context?: Context): Promise<Record> | Record;
124+
125+
create?(idPrefix: Id, record: Record, context: Context): Promise<Id>;
126+
create?(record: Record, context: Context): Promise<Id>;
127+
create?(idPrefix: any, record: Record, context?: Context): Promise<Id>;
128+
129+
getNewId?(): Id;
130+
coerceId?(id: Id): Id;
131+
parseQuery?(search: any, query: any): any;
132+
parsePath?(path: any, context: Context, query: any): any;
133+
isCollection: boolean;
134+
135+
getResource(
136+
target: RequestTargetOrId,
137+
request: Context | SourceContext,
138+
resourceOptions: any
139+
): Promise<ResourceInterface> | ResourceInterface;
140+
}
141+
61142
export interface Session {
62143
id?: any;
63144
user?: User;

resources/Table.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,15 @@ import type {
1717
SubSelect,
1818
RequestTargetOrId,
1919
} from './ResourceInterface.ts';
20-
import { Table as TableType, TableInterface, TableStaticInterface, Attribute } from './TableInterface.ts';
20+
import {
21+
Table as TableType,
22+
TableInterface,
23+
TableStaticInterface,
24+
Attribute,
25+
type ExpirationOptions,
26+
type IntermediateSourceOptions,
27+
type ExpirationParam,
28+
} from './TableInterface.ts';
2129
import type { User } from '../security/user.ts';
2230
import lmdbProcessRows from '../dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbProcessRows.js';
2331
import { Resource, transformForSelect } from './Resource.ts';
@@ -223,7 +231,10 @@ export function makeTable(options): TableStaticInterface {
223231
* @param options
224232
* @returns
225233
*/
226-
static sourcedFrom(source, options) {
234+
static sourcedFrom<Record extends object = any>(
235+
source: TableInterface<Record> & TableStaticInterface<Record>,
236+
options?: ExpirationOptions & IntermediateSourceOptions
237+
) {
227238
// define a source for retrieving invalidated entries for caching purposes
228239
if (options) {
229240
this.sourceOptions = options;
@@ -589,7 +600,7 @@ export function makeTable(options): TableStaticInterface {
589600
});
590601
}
591602
}
592-
static getNewId(): any {
603+
static getNewId(): Id {
593604
const type = primaryKeyAttribute?.type;
594605
// the default Resource behavior is to return a GUID, but for a table we can return incrementing numeric keys if the type is (or can be) numeric
595606
if (type === 'String' || type === 'ID') return super.getNewId();
@@ -763,7 +774,7 @@ export function makeTable(options): TableStaticInterface {
763774
* @param expirationTime Time in seconds until records expire (are stale)
764775
* @param evictionTime Time in seconds until records are evicted (removed)
765776
*/
766-
static setTTLExpiration(expiration: number | { expiration: number; eviction?: number; scanInterval?: number }) {
777+
static setTTLExpiration(expiration: ExpirationParam) {
767778
// we set up a timer to remove expired entries. we only want the timer/reaper to run in one thread,
768779
// so we use the first one
769780
if (typeof expiration === 'number') {

resources/TableInterface.ts

Lines changed: 60 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
import { Database } from 'lmdb';
2-
import { ResourceInterface, Id, Context } from './ResourceInterface.ts';
3-
4-
export interface Attribute {
5-
assignCreatedTime?: boolean;
6-
assignUpdatedTime?: boolean;
7-
expiresAt?: boolean;
8-
isPrimaryKey?: boolean;
9-
name: string;
10-
type: string;
11-
}
2+
import { Context, Id, ResourceInterface, type ResourceStaticInterface } from './ResourceInterface.ts';
3+
import { DBI } from './DatabaseInterface.ts';
124

135
export interface Table<Record extends object = any> extends TableInterface<Record> {
146
attributes: Attribute[];
@@ -35,21 +27,53 @@ export interface TableInterface<Record extends object = any> extends ResourceInt
3527
getUpdatedTime(): number;
3628
}
3729

38-
export interface TableStaticInterface {
39-
new <Record extends object = any>(identifier?: Id, source?: any): TableInterface<Record>;
30+
export interface Attribute {
31+
assignCreatedTime?: boolean;
32+
assignUpdatedTime?: boolean;
33+
dbi: DBI;
34+
expiresAt?: boolean;
35+
indexingPID?: number;
36+
isPrimaryKey?: boolean;
37+
key: string;
38+
lastIndexedKey: string;
39+
name: string;
40+
resolve?: (id: Id) => any;
41+
type: string;
42+
}
43+
44+
export interface Index {
45+
clear: () => Promise<void>;
46+
clearAsync?: () => void;
47+
customIndex?: {
48+
index: (id: Id, value: unknown, existingValue?: unknown, options?: unknown) => void;
49+
propertyResolver: (value: unknown, context: Context, entry: unknown) => unknown;
50+
};
51+
drop: () => void;
52+
getRange: (options: { start: boolean; values: boolean; end: number; snapshot: boolean }) => void;
53+
getValues: (key: Id) => Id[];
54+
indexNulls: boolean;
55+
isIndexing: boolean;
56+
prefetch?: (valuesToPrefetch, noop) => void;
57+
put: (valueToAdd, id: Id, options?: unknown) => unknown;
58+
remove: (valueToRemove, id: Id, options?: unknown) => void;
59+
}
60+
61+
export interface TableStaticInterface<Record extends object = any> extends ResourceStaticInterface<Record> {
62+
new (identifier?: Id, source?: TableInterface<Record> & TableStaticInterface<Record>): TableInterface<Record>;
4063

4164
attributes: Attribute[];
42-
audit: any;
65+
audit: boolean;
4366
auditStore: Database;
4467
createdTimeProperty: Attribute;
4568
databaseName: string;
4669
databasePath: string;
47-
dbisDB: any;
70+
dbisDB: DBI;
4871
expirationMS: number;
4972
getResidencyById: (id: Id) => number | void;
5073
indexingOperation?: Promise<void>;
51-
indices: any;
74+
indices: Map<string, Index>;
5275
intermediateSource: boolean;
76+
name: string;
5377
origin?: string;
5478
primaryKey: string;
5579
primaryStore: Database;
@@ -58,23 +82,40 @@ export interface TableStaticInterface {
5882
schemaDefined: boolean;
5983
schemaVersion?: number;
6084
sealed: boolean;
61-
source?: any;
62-
sourceOptions: any;
85+
source?: TableInterface<Record> & TableStaticInterface<Record>;
86+
sourceOptions?: ExpirationOptions & IntermediateSourceOptions;
6387
splitSegments: boolean;
6488
tableId: number;
6589
tableName: string;
6690
updatedTimeProperty: Attribute;
6791
userResolvers: any;
6892

93+
cleanup(): void;
94+
clear(): unknown;
95+
dropTable(): Promise<void>;
96+
evict(id: Id, existingRecord: unknown, existingVersion: unknown): unknown;
97+
operation(operation: unknown, context: Context): unknown;
98+
6999
addAttributes(attributesToAdd: Attribute[]): Promise<void>;
100+
coerceId(id: Id): Id;
101+
enableAuditing(value?: boolean): void;
70102
getAuditSize(): number;
71103
getRecordCount(options?: {
72104
exactCount?: boolean;
73105
}): Promise<number | { recordCount: number; estimatedRange: number[] }>;
74106
getResource(target: any, request: Context, resourceOptions: any): Promise<TableInterface>;
75107
getSize(): number;
108+
getNewId(): Id;
109+
isCaching(): boolean;
76110
getStorageStats(): { available: number; free: number; size: number };
77111
removeAttributes(names: string[]): Promise<void>;
78-
setTTLExpiration(expiration: any): void;
79-
sourcedFrom(source: any, options: any): void;
112+
setTTLExpiration(expiration: ExpirationParam): void;
113+
sourcedFrom(
114+
source: TableInterface<Record> & TableStaticInterface<Record>,
115+
options?: ExpirationOptions & IntermediateSourceOptions
116+
): void;
80117
}
118+
119+
export type ExpirationParam = number | ExpirationOptions;
120+
export type ExpirationOptions = { expiration: number; eviction?: number; scanInterval?: number };
121+
export type IntermediateSourceOptions = { intermediateSource?: boolean };

resources/databases.ts

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1+
import type { LMDBRootDatabase, RocksDatabaseEx, RocksRootDatabase } from './DatabaseInterface.ts';
12
import { initSync, getHdbBasePath, get as envGet } from '../utility/environment/environmentManager.js';
23
import { INTERNAL_DBIS_NAME } from '../utility/lmdb/terms.js';
3-
import { open, compareKeys, type Database, type RootDatabase } from 'lmdb';
4+
import { open, compareKeys } from 'lmdb';
45
import { join, extname, basename } from 'path';
56
import { existsSync, readdirSync, readFileSync, mkdirSync } from 'node:fs';
67
import { unlink } from 'node:fs/promises';
78
import {
89
getBaseSchemaPath,
910
getTransactionAuditStoreBasePath,
1011
} from '../dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js';
11-
import { makeTable, type TableInterface } from './Table.ts';
12+
import { type Attribute, makeTable, type TableInterface, type TableStaticInterface } from './Table.ts';
1213
import OpenEnvironmentObject from '../utility/lmdb/OpenEnvironmentObject.js';
1314
import { CONFIG_PARAMS, LEGACY_DATABASES_DIR_NAME, DATABASES_DIR_NAME } from '../utility/hdbTerms.ts';
1415
import { _assignPackageExport } from '../globals.js';
@@ -55,43 +56,14 @@ export const NON_REPLICATING_SYSTEM_TABLES = [
5556
export { Table };
5657

5758
export interface Tables {
58-
[tableName: string]: Table;
59+
[tableName: string]: Table & TableStaticInterface;
5960
[DEFINED_TABLES]?: Set<string>;
6061
}
6162
export interface Databases {
6263
[databaseName: string]: Tables;
6364
}
6465

6566
// note: technically `Database` is either a `LMDBStore` or a `CachingStore`
66-
interface LMDBDatabase extends Database {
67-
customIndex?: any;
68-
isIndexing?: boolean;
69-
indexNulls?: boolean;
70-
}
71-
interface LMDBRootDatabase extends RootDatabase {
72-
auditStore?: LMDBRootDatabase;
73-
databaseName?: string;
74-
dbisDb?: LMDBDatabase;
75-
isLegacy?: boolean;
76-
needsDeletion?: boolean;
77-
path?: string;
78-
status?: 'open' | 'closed';
79-
}
80-
81-
interface RocksDatabaseEx extends RocksDatabase {
82-
customIndex?: any;
83-
env: Record<string, any>;
84-
isLegacy?: boolean;
85-
isIndexing?: boolean;
86-
indexNulls?: boolean;
87-
getEntry?: (id: string | number | (string | number)[] | Buffer, options?: any) => { value: any };
88-
}
89-
90-
interface RocksRootDatabase extends RocksDatabaseEx {
91-
auditStore?: RocksDatabaseEx;
92-
databaseName?: string;
93-
dbisDb?: RocksDatabaseEx;
94-
}
9567

9668
export type RootDatabaseKind = LMDBRootDatabase | RocksRootDatabase;
9769

@@ -1113,7 +1085,7 @@ export function table<TableResourceType = TableInterface>(tableDefinition: Table
11131085
}
11141086
const MAX_OUTSTANDING_INDEXING = 1000;
11151087
const MIN_OUTSTANDING_INDEXING = 10;
1116-
async function runIndexing(Table, attributes, indicesToRemove) {
1088+
async function runIndexing(Table, attributes: Attribute[], indicesToRemove) {
11171089
try {
11181090
logger.info(`Indexing ${Table.tableName} attributes`, attributes);
11191091
await signalling.signalSchemaChange(

0 commit comments

Comments
 (0)