Feat/introduce web client - #307
Conversation
…ipt-client into web/split-nice-grpc-connection
- Refactor into a monorepo composed for four packages: - @weaviate/core: shared functionality - @weaviate/node: Node-client that uses `grpc-js` and `fs` - @weaviate/web: Browser-client that uses `nice-grpc` and `FileReader` - @weaviate/test: Tests the public APIs of the core/node pkgs - Replace `jest` with `vitest` - Use `pnpm` for managing the monorepo - Use dependency injection to pipe node/browser deps into the common functionality of the client
There was a problem hiding this comment.
Orca Security Scan Summary
| Status | Check | Issues by priority | |
|---|---|---|---|
| Infrastructure as Code | View in Orca | ||
| SAST | View in Orca | ||
| Secrets | View in Orca | ||
| Vulnerabilities | View in Orca |
…into feat/introduce-web-client
|
Great to see you again! Thanks for the contribution. |
…into feat/introduce-web-client
There was a problem hiding this comment.
Orca Security Scan Summary
| Status | Check | Issues by priority | |
|---|---|---|---|
| Infrastructure as Code | View in Orca | ||
| SAST | View in Orca | ||
| Secrets | View in Orca | ||
| Vulnerabilities | View in Orca |
☢️ The following Vulnerabilities (CVEs) have been detected
| PACKAGE | FILE | CVE ID | INSTALLED VERSION | FIXED VERSION | ||
|---|---|---|---|---|---|---|
| ws | ./pnpm-lock.yaml | CVE-2026-48779 | 8.18.2 | 5.2.5, 6.2.4, 7.5.11, 8.21.0 | View in code |
…ve-node-https-from-web chore(agent): remove node https module from web bundle
There was a problem hiding this comment.
Orca Security Scan Summary
| Status | Check | Issues by priority | |
|---|---|---|---|
| Infrastructure as Code | View in Orca | ||
| SAST | View in Orca | ||
| Secrets | View in Orca | ||
| Vulnerabilities | View in Orca |
☢️ The following Vulnerabilities (CVEs) have been detected
| PACKAGE | FILE | CVE ID | INSTALLED VERSION | FIXED VERSION | ||
|---|---|---|---|---|---|---|
| ws | ./pnpm-lock.yaml | CVE-2026-48779 | 8.18.2 | 5.2.5, 6.2.4, 7.5.11, 8.21.0 | View in code | |
| ws | ./pnpm-lock.yaml | CVE-2026-45736 | 8.18.2 | 8.20.1 | View in code |
| 'username and password, so this method is not supported by this client' | ||
| ); | ||
| } | ||
| this.openidConfig.scopes.push('offline_access'); |
There was a problem hiding this comment.
issue: this is a breaking change, client should continue to add offline_access scope.
Sneaky having that at the end of something called "validate config" 😅
I missed it too while cherry-picking the commit but good thing the tests caught it.
There was a problem hiding this comment.
Orca Security Scan Summary
| Status | Check | Issues by priority | |
|---|---|---|---|
| Infrastructure as Code | View in Orca | ||
| SAST | View in Orca | ||
| Secrets | View in Orca | ||
| Vulnerabilities | View in Orca |
☢️ The following Vulnerabilities (CVEs) have been detected
| PACKAGE | FILE | CVE ID | INSTALLED VERSION | FIXED VERSION | ||
|---|---|---|---|---|---|---|
| ws | ./pnpm-lock.yaml | CVE-2026-48779 | 8.18.2 | 5.2.5, 6.2.4, 7.5.11, 8.21.0 | View in code | |
| ws | ./pnpm-lock.yaml | CVE-2026-45736 | 8.18.2 | 8.20.1 | View in code |
| (name.charAt(0).toUpperCase() + name.slice(1)) as N; | ||
|
|
||
| const collection = <T, N, V>( | ||
| const collection = <T, N, V, M>( |
There was a problem hiding this comment.
nit/suggestion: if renaming generic parameters and formatting changes were their own PR(s) it'd be easier to focus on the key areas of the diff in this one
There was a problem hiding this comment.
Having went through the whole PR I want to emphasize much easier :)
| private constructor(params: GrpcConnectionParams & { grpcMaxMessageLength: number }) { | ||
| private constructor( | ||
| transports: Transports, | ||
| params: GrpcConnectionParams & { grpcMaxMessageLength: number } |
There was a problem hiding this comment.
question: Can grpcMaxMessageLength be included in the GrpcConnectionParams definition above on line 19?
| if (!params.skipInitChecks) { | ||
| grpcMaxMessageLength = await Promise.all([ | ||
| (rest.get('/meta', true) as Promise<Meta>).then( | ||
| (res: Meta) => res.grpcMaxMessageSize || MAX_GRPC_MESSAGE_LENGTH |
There was a problem hiding this comment.
issue: it feels wrong that skipInitChecks can also make the client ignore the server's gRPC message size limit; perhaps it should only control compatibility checks?
There was a problem hiding this comment.
I understand that calling /meta with skipInitChecks: false will effectively perform an init check despite their having been disabled. At the same time, respecting the server's transport configuration seems more important (?)
WDYT?
| tenant | ||
| ), | ||
| close: () => channel.close(), | ||
| close: () => {}, |
There was a problem hiding this comment.
question: why is close a no-op now?
| return health | ||
| return transports.health | ||
| .check( | ||
| { service: '/grpc.health.v1.Health/Check' }, |
There was a problem hiding this comment.
It's quite surprising that HealthClient from Google's gRPC package doesn't have the name of the service "baked in", what?
| const agent = context.agentMaker?.(httpSecure); | ||
|
|
||
| const { connection, dbVersionProvider, dbVersionSupport } = await ConnectionGRPC.use( | ||
| context.transportsMaker, |
There was a problem hiding this comment.
tl;dr: I find the fact that ConnectionGRPC satisfies ConnectionREST so confusing.
Initially I wanted to ask this:
question: if
agentMaker"Creates the HTTP(S) keep-alive agent for the REST/GraphQL connection", then why is it a part of the top-level Context and not some internal component of ConnectionREST?
Before posting that, I decided to look around and answer this for myself. But I couldn't find ConnectionREST mentioned anywhere here, and instead I saw connection returned by ConnectionGRPC.use() passed to alias(connection) which expects ConnectionREST (!).
I was rather surprised to learn that ConnectionGRPC extended ConnectionGQL even though they use completely different transports, and that ConnectionGQL was a thing separate from ConnectionREST even though both use the same transport / connection.
suggestion (wrt agentMaker): I see from it's description that web/ and node/ inject different agent makers. Perhaps each could instead provide a complete ConnectionREST object and make the injection "privately"?
| host: `${scheme}://${httpHost}:${httpPort}${httpPath || ''}`, | ||
| scheme: scheme, | ||
| headers: params.headers, | ||
| grpcAddress: `${isGrpcWeb ? (grpcSecure ? 'https://' : 'http://') : ''}${grpcHost}:${grpcPort}${ |
There was a problem hiding this comment.
question: why are there both httpSecure and grpcSecure parameters? Is there a real use-case for having one connection be secured and the other one not?
| grpcAddress: `${isGrpcWeb ? (grpcSecure ? 'https://' : 'http://') : ''}${grpcHost}:${grpcPort}${ | |
| grpcAddress: `${isGrpcWeb ? (grpcSecure ? 'https' : 'http') : ''}://${grpcHost}:${grpcPort}${ |
nit: simplify the expression
| public static tenants<T, M>(tenants: T[], mapper: (tenant: T) => M): M[][] { | ||
| const mapped = []; | ||
| const mapped: M[][] = []; | ||
| const batches = Math.ceil(tenants.length / 100); | ||
| for (let i = 0; i < batches; i++) { | ||
| const batch = tenants.slice(i * 100, (i + 1) * 100); |
There was a problem hiding this comment.
question: why does this method split the tenants array into batches or 100?
|
|
||
| it('should be able to create a simple collection with a generic using a schema var with type', async () => { | ||
| const collectionName = 'TestCollectionSimpleGenericVarType'; | ||
| afters.push(() => contextionary.collections.delete(collectionName)); |
There was a problem hiding this comment.
suggestion: would it make sense to add a test util for creating collections that would "schedule" the deletion automatically?
It's exactly as what you have with afters here, but packaged so that it's easier to reuse across tests and a tad more succinct. Just something I've thought about doing every time I had to create a collection in a test :)
// testutil.ts
export class Collectioner {
private client WeaviateClient;
private collections: []string;
public create(c: CollectionConfig): Promise<CollectionHandle> {
this.collections.push(c.name);
return this.client.collections.create(c);
}
public cleanup(): Promise<void> {
return Promise.all(this.collections.map(c => this.client.collections.delete(c)));
}
}Then use it in a test:
describe('stuff', async () => {
const client = new WeaviateClient(...);
const collections = new Collectioner(client);
afterAll(() => collections.cleanup());
...
})| params.connectionParams.grpc = { | ||
| host: params.connectionParams.http.host, | ||
| port: params.connectionParams.http.port, | ||
| secure: params.connectionParams.http.secure, | ||
| path: '/v1/grpc-web', | ||
| }; |
There was a problem hiding this comment.
question: could host, port, and secure be mapped in helpers? Seems like something that'd be common to both node and web, as opposed to path: /v1/grpc-web which is clearly specific to web/ package.
|
Let's go @tsmith023 !! The time for a TS web client has finally come ✨ |
@weaviate/core: shared functionality@weaviate/node: Node-client that usesnice-grpcandfs@weaviate/web: Browser-client that usesnice-grpc-webandFileReader@weaviate/test: Tests the public APIs of the core/node pkgspnpmfor managing the monorepo@weaviate/denopackage too with its own deno-specific libs injected in to closeCan't connect by Deno #309
/grpc-webendpoint weaviate#12024weaviate-clientfails to bundle on Cloudflare Workers due tohttp2dependency #145