Skip to content

Commit

Permalink
breakup query utils into smaller files and fixup ts
Browse files Browse the repository at this point in the history
  • Loading branch information
wernst committed Jun 14, 2024
1 parent e93bc84 commit d69b273
Show file tree
Hide file tree
Showing 18 changed files with 182 additions and 142 deletions.
5 changes: 0 additions & 5 deletions packages/authjs-adapter/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,9 @@ export function TriplitAdapter(
],
// We could make schema optional and do a manual join here
include: {
// @ts-expect-error needs to read schema to do this
user: null,
},
});
// @ts-expect-error
return account?.user?.get(account.userId) ?? null;
},
async updateUser(user) {
Expand Down Expand Up @@ -105,17 +103,14 @@ export function TriplitAdapter(
collectionName: collectionNames.session,
where: [['sessionToken', '=', sessionToken]],
include: {
// @ts-expect-error needs to read schema to do this
user: null,
},
});
if (!sessionWithUser) return null;
const { user: userMap, ...session } = sessionWithUser;
// @ts-expect-error
const user = userMap?.get(session.userId);
if (!user) return null;

// @ts-expect-error
return { session, user } as {
session: AdapterSession;
user: AdapterUser;
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/client-logger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LogLevel, Logger } from '@triplit/types/logger.js';
import { LogLevel, Logger } from '@triplit/types/logger';
import superjson from 'superjson';

export type LogListener = ((log: any) => void) | undefined;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
import {
CollectionNameFromModels,
CollectionQuery,
FetchByIdQueryParams,
Models,
QueryBuilder,
ReturnTypeFromQuery,
QuerySelectionValue,
RelationSubquery,
Unalias,
QUERY_INPUT_TRANSFORMERS,
ModelFromModels,
AfterInput,
Expand All @@ -21,48 +12,28 @@ import {
CollectionQueryModels,
CollectionQuerySelection,
RelationAttributes,
CollectionNameFromModels,
QuerySelectionValue,
Models,
RelationSubquery,
} from '@triplit/db';
import {
ClientSchema,
ClientQuery,
ClientQueryDefault,
SyncStatus,
} from './types';

// There is some odd behavior when using infer with intersection types
// Our query types are set up as:
// CollectionQuery<...> = Query<...> & { ... }
// ClientQuery<...> = CollectionQuery<...> & { ... }
//
// However, if you attempt to infer the generic of a base object (ex. CollectionQuery<infer M>) with the intersected object (ClientQuery<any>) the inferred type M is overly generic
//
// Recreating the fetch result type here to avoid this issue
// Playground: https://www.typescriptlang.org/play?#code/KYDwDg9gTgLgBDAnmYcCyEAmwA2BnAHgCg44BhCHHYAYxgEsIA7AOQEMBbVUGYJzPHDwwo9JgHMANCTgAVODz4C4AJVrRMBYaImShIseIB8RI3AC8q9VE0UqtBs3Zc9sowG4iRJCjgAhNjxgAjQFEF5+QQxsfAI2JkQ9eMQjPTIWMIjlAGtgRAgAM3QzSwBvGTYYEQBGAC50AG10gF1PAF8vH1QAUXClYE1QxUj0LFxCZKSE1PIM4Zy8wuKLf0DgtDSWMwAyOFLKkQAmeu1DNs9vZFRZYGFqgnl5wQCguISplJK5TKVntbfEnBkmYAPxwADkYECeHBcHq4IKbHoOHBni6cluMEODx+IxewUmQOmX0efTx-zEBWAUDgAFUPqC6XCIYjkajOlc4ABJJhgACu8EsvSyAwIpV4wnq+3hBQgEHBbTaenBEpg4I8HN8ajwfJwMGqKxudwIPP5MA16O1uqxhsx2NNAo8QA
/**
* Results from a query based on the query's model in the format `Map<id, entity>`
*/
export type ClientFetchResult<C extends ClientQuery<any, any>> = Map<
string,
ClientFetchResultEntity<C>
>;

export type ClientSchema = Models<any, any>;

export type ClientFetchResultEntity<C extends ClientQuery<any, any, any, any>> =
ReturnTypeFromQuery<C>;

export type SyncStatus = 'pending' | 'confirmed' | 'all';

export type Entity<
M extends ClientSchema,
CN extends CollectionNameFromModels<M>
> = Unalias<ReturnTypeFromQuery<ClientQueryDefault<M, CN>>>;

export type ClientQuery<
export function clientQueryBuilder<
M extends ClientSchema | undefined,
CN extends CollectionNameFromModels<M>,
Selection extends QuerySelectionValue<M, CN> = QuerySelectionValue<M, CN>,
Inclusions extends Record<string, RelationSubquery<M, any>> = Record<
string,
RelationSubquery<M, any>
>
> = {
syncStatus?: SyncStatus;
} & CollectionQuery<M, CN, Selection, Inclusions>;
CN extends CollectionNameFromModels<M>
>(collectionName: CN, params?: Omit<ClientQuery<M, CN>, 'collectionName'>) {
const query = {
collectionName,
...params,
};
return new ClientQueryBuilder<ClientQueryDefault<M, CN>>(query);
}

// The fact that builder methods will update generics makes it tough to re-use the builder from the db
// - DB builder returns specific type QueryBuilder<...Params>
Expand Down Expand Up @@ -207,65 +178,3 @@ export class ClientQueryBuilder<
return this;
}
}

export type ClientQueryDefault<
M extends ClientSchema | undefined,
CN extends CollectionNameFromModels<M>
> = ClientQuery<M, CN, QuerySelectionValue<M, CN>, {}>;

export function clientQueryBuilder<
M extends ClientSchema | undefined,
CN extends CollectionNameFromModels<M>
>(collectionName: CN, params?: Omit<ClientQuery<M, CN>, 'collectionName'>) {
const query = {
collectionName,
...params,
};
return new ClientQueryBuilder<ClientQueryDefault<M, CN>>(query);
}

export class HttpClientQueryBuilder<
CQ extends CollectionQuery<any, any, any, any>
> extends QueryBuilder<CQ> {
constructor(query: CQ) {
super(query);
}
}

export function httpClientQueryBuilder<
M extends ClientSchema | undefined,
CN extends CollectionNameFromModels<M>
// syncStatus doesn't apply for the remote client
>(collectionName: CN, params?: Omit<CollectionQuery<M, CN>, 'collectionName'>) {
const query: CollectionQuery<M, CN> = {
collectionName,
...params,
};
return new QueryBuilder<
CollectionQuery<M, CN, QuerySelectionValue<M, CN>, {}>
>(query);
}

export function prepareFetchOneQuery<CQ extends ClientQuery<any, any>>(
query: CQ
): CQ {
return { ...query, limit: 1 };
}

export function prepareFetchByIdQuery<
M extends ClientSchema | undefined,
CN extends CollectionNameFromModels<M>
>(collectionName: CN, id: string, queryParams?: FetchByIdQueryParams<M, CN>) {
let query = clientQueryBuilder<M, CN>(collectionName).entityId(id);
if (queryParams?.include) {
for (const [relation, subquery] of Object.entries(queryParams.include)) {
if (subquery) {
query = query.include(relation, subquery);
} else {
// @ts-expect-error TODO: fixup builder type
query = query.include(relation);
}
}
}
return query.build();
}
6 changes: 3 additions & 3 deletions packages/client/src/client/triplit-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ import {
ClientQuery,
ClientQueryDefault,
ClientSchema,
clientQueryBuilder,
} from '../utils/query.js';
} from './types';
import { clientQueryBuilder } from './query-builder.js';
import { HttpClient } from '../http-client/http-client.js';
import { Logger } from '@triplit/types/logger.js';
import { Logger } from '@triplit/types/logger';
import { DefaultLogger } from '../client-logger.js';

export interface SyncOptions {
Expand Down
2 changes: 2 additions & 0 deletions packages/client/src/client/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './query.js';
export * from './results.js';
43 changes: 43 additions & 0 deletions packages/client/src/client/types/query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type {
CollectionNameFromModels,
CollectionQuery,
Models,
QuerySelectionValue,
RelationSubquery,
} from '@triplit/db';

/**
* Possible values for the syncStatus field in a query.
* - pending: Items that are in the outbox
* - confirmed: Items that have been confirmed by the server
* - all: All items
*/
export type SyncStatus = 'pending' | 'confirmed' | 'all';

/**
* Query that can be passed to a Triplit Client.
*/
export type ClientQuery<
M extends ClientSchema | undefined,
CN extends CollectionNameFromModels<M>,
Selection extends QuerySelectionValue<M, CN> = QuerySelectionValue<M, CN>,
Inclusions extends Record<string, RelationSubquery<M, any>> = Record<
string,
RelationSubquery<M, any>
>
> = {
syncStatus?: SyncStatus;
} & CollectionQuery<M, CN, Selection, Inclusions>;

/**
* A client query with default selection and inclusion.
*/
export type ClientQueryDefault<
M extends ClientSchema | undefined,
CN extends CollectionNameFromModels<M>
> = ClientQuery<M, CN, QuerySelectionValue<M, CN>, {}>;

/**
* Friendly alias for Models type.
*/
export type ClientSchema = Models<any, any>;
38 changes: 38 additions & 0 deletions packages/client/src/client/types/results.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// There is some odd behavior when using infer with intersection types
// Our query types are set up as:
// CollectionQuery<...> = Query<...> & { ... }
// ClientQuery<...> = CollectionQuery<...> & { ... }
//
// However, if you attempt to infer the generic of a base object (ex. CollectionQuery<infer M>) with the intersected object (ClientQuery<any>) the inferred type M is overly generic
//
// Recreating the fetch result types here to avoid this issue
// Playground: https://www.typescriptlang.org/play?#code/KYDwDg9gTgLgBDAnmYcCyEAmwA2BnAHgCg44BhCHHYAYxgEsIA7AOQEMBbVUGYJzPHDwwo9JgHMANCTgAVODz4C4AJVrRMBYaImShIseIB8RI3AC8q9VE0UqtBs3Zc9sowG4iRJCjgAhNjxgAjQFEF5+QQxsfAI2JkQ9eMQjPTIWMIjlAGtgRAgAM3QzSwBvGTYYEQBGAC50AG10gF1PAF8vH1QAUXClYE1QxUj0LFxCZKSE1PIM4Zy8wuKLf0DgtDSWMwAyOFLKkQAmeu1DNs9vZFRZYGFqgnl5wQCguISplJK5TKVntbfEnBkmYAPxwADkYECeHBcHq4IKbHoOHBni6cluMEODx+IxewUmQOmX0efTx-zEBWAUDgAFUPqC6XCIYjkajOlc4ABJJhgACu8EsvSyAwIpV4wnq+3hBQgEHBbTaenBEpg4I8HN8ajwfJwMGqKxudwIPP5MA16O1uqxhsx2NNAo8QA

import {
CollectionNameFromModels,
ReturnTypeFromQuery,
Unalias,
} from '@triplit/db';
import { ClientQuery, ClientQueryDefault, ClientSchema } from './query.js';

/**
* Results from a query based on the query's model in the format `Map<id, entity>`
*/
export type ClientFetchResult<C extends ClientQuery<any, any>> = Map<
string,
ClientFetchResultEntity<C>
>;

/**
* Entity from a query based on the query's model
*/
export type ClientFetchResultEntity<C extends ClientQuery<any, any, any, any>> =
ReturnTypeFromQuery<C>;

/**
* The fully selected type of an entity, including all fields but not relations
*/
export type Entity<
M extends ClientSchema,
CN extends CollectionNameFromModels<M>
> = Unalias<ReturnTypeFromQuery<ClientQueryDefault<M, CN>>>;
9 changes: 3 additions & 6 deletions packages/client/src/http-client/http-client.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import {
UpdateTypeFromModel,
Models,
CollectionNameFromModels,
ModelFromModels,
InsertTypeFromModel,
FetchByIdQueryParams,
ChangeTracker,
createUpdateProxy,
Attribute,
Expand All @@ -21,9 +19,8 @@ import {
ClientFetchResultEntity,
ClientQuery,
ClientSchema,
prepareFetchByIdQuery,
httpClientQueryBuilder,
} from '../utils/query.js';
} from '../client/types';
import { httpClientQueryBuilder } from './query-builder.js';

function parseError(error: string) {
try {
Expand Down Expand Up @@ -220,7 +217,7 @@ export class HttpClient<M extends ClientSchema | undefined> {
*/
const schema = await this.schema();
const collectionSchema = schema?.[collectionName]?.schema;
const entityQuery = prepareFetchByIdQuery<M, CN>(collectionName, entityId);
const entityQuery = this.query(collectionName).id(entityId).build();
const triples = await this.queryTriples(entityQuery);
// TODO we should handle errors or non-existent entities
const entity = constructEntity(
Expand Down
29 changes: 29 additions & 0 deletions packages/client/src/http-client/query-builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
CollectionNameFromModels,
CollectionQuery,
QueryBuilder,
QuerySelectionValue,
} from '@triplit/db';
import { ClientSchema } from '../client/types';

export class HttpClientQueryBuilder<
CQ extends CollectionQuery<any, any, any, any>
> extends QueryBuilder<CQ> {
constructor(query: CQ) {
super(query);
}
}

export function httpClientQueryBuilder<
M extends ClientSchema | undefined,
CN extends CollectionNameFromModels<M>
// syncStatus doesn't apply for the remote client
>(collectionName: CN, params?: Omit<CollectionQuery<M, CN>, 'collectionName'>) {
const query: CollectionQuery<M, CN> = {
collectionName,
...params,
};
return new QueryBuilder<
CollectionQuery<M, CN, QuerySelectionValue<M, CN>, {}>
>(query);
}
4 changes: 2 additions & 2 deletions packages/client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export type {
ClientFetchResult,
ClientFetchResultEntity,
ClientQuery,
ClientQueryBuilder,
ClientQueryDefault,
ClientSchema,
Entity,
} from './utils/query.js';
} from './client/types';
export type { ClientQueryBuilder } from './client/query-builder.js';
2 changes: 1 addition & 1 deletion packages/client/src/sync-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
RemoteSyncFailedError,
} from './errors.js';
import { Value } from '@sinclair/typebox/value';
import { ClientFetchResult, ClientQuery } from './utils/query.js';
import { ClientFetchResult, ClientQuery } from './client/types';
import { Logger } from '@triplit/types/logger';

type OnMessageReceivedCallback = (message: ServerSyncMessage) => void;
Expand Down
6 changes: 3 additions & 3 deletions packages/client/src/worker-client/worker-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ import {
createUpdateProxy,
schemaToJSON,
} from '@triplit/db';
import { ConnectionStatus } from '../transport/transport.js';
import {
ClientFetchResult,
ClientFetchResultEntity,
ClientQuery,
ClientQueryDefault,
ClientSchema,
clientQueryBuilder,
} from '../utils/query.js';
import { ConnectionStatus } from '../transport/transport.js';
} from '../client/types';
import { clientQueryBuilder } from '../client/query-builder.js';

export class WorkerClient<M extends ClientSchema | undefined = undefined> {
initialized: Promise<void>;
Expand Down
2 changes: 1 addition & 1 deletion packages/client/test/typecheck/friendly-types.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expectTypeOf, test } from 'vitest';
import { Schema as S } from '@triplit/db';
import { Entity } from '../../src/utils/query';
import { Entity } from '../../dist/client/types';

test('Entity', () => {
const schema = {
Expand Down
Loading

0 comments on commit d69b273

Please sign in to comment.