@reactionary/source 0.0.30 → 0.0.32
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/CLAUDE.md +11 -0
- package/core/package.json +1 -1
- package/core/src/cache/cache-evaluation.interface.ts +19 -0
- package/core/src/cache/cache.interface.ts +38 -0
- package/core/src/cache/noop-cache.ts +42 -0
- package/core/src/cache/redis-cache.ts +55 -22
- package/core/src/client/client-builder.ts +63 -0
- package/core/src/client/client.ts +27 -3
- package/core/src/decorators/trpc.decorators.ts +144 -0
- package/core/src/index.ts +6 -1
- package/core/src/providers/analytics.provider.ts +3 -6
- package/core/src/providers/base.provider.ts +13 -63
- package/core/src/providers/cart.provider.ts +10 -6
- package/core/src/providers/identity.provider.ts +8 -5
- package/core/src/providers/inventory.provider.ts +5 -6
- package/core/src/providers/price.provider.ts +6 -6
- package/core/src/providers/product.provider.ts +6 -6
- package/core/src/providers/search.provider.ts +6 -6
- package/core/src/schemas/mutations/base.mutation.ts +0 -1
- package/core/src/schemas/mutations/cart.mutation.ts +0 -6
- package/core/src/schemas/mutations/identity.mutation.ts +0 -5
- package/core/src/schemas/mutations/product.mutation.ts +0 -1
- package/core/src/schemas/queries/base.query.ts +0 -1
- package/core/src/schemas/queries/cart.query.ts +1 -3
- package/core/src/schemas/queries/identity.query.ts +1 -3
- package/core/src/schemas/queries/inventory.query.ts +0 -1
- package/core/src/schemas/queries/price.query.ts +0 -3
- package/core/src/schemas/queries/product.query.ts +2 -7
- package/core/src/schemas/queries/search.query.ts +0 -3
- package/examples/node/package.json +1 -5
- package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +97 -0
- package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +84 -0
- package/examples/node/src/basic/basic-node-setup.spec.ts +40 -0
- package/otel/src/index.ts +3 -0
- package/otel/src/trace-decorator.ts +246 -0
- package/package.json +2 -1
- package/providers/algolia/src/core/initialize.ts +11 -9
- package/providers/algolia/src/providers/product.provider.ts +44 -11
- package/providers/algolia/src/providers/search.provider.ts +47 -66
- package/providers/commercetools/src/core/client.ts +0 -1
- package/providers/commercetools/src/core/initialize.ts +28 -24
- package/providers/commercetools/src/providers/cart.provider.ts +58 -89
- package/providers/commercetools/src/providers/identity.provider.ts +34 -50
- package/providers/commercetools/src/providers/inventory.provider.ts +16 -38
- package/providers/commercetools/src/providers/price.provider.ts +30 -35
- package/providers/commercetools/src/providers/product.provider.ts +48 -38
- package/providers/commercetools/src/providers/search.provider.ts +32 -47
- package/providers/commercetools/src/schema/capabilities.schema.ts +1 -1
- package/providers/fake/package.json +1 -0
- package/providers/fake/src/core/initialize.ts +17 -14
- package/providers/fake/src/index.ts +4 -0
- package/providers/fake/src/providers/analytics.provider.ts +19 -0
- package/providers/fake/src/providers/cart.provider.ts +107 -0
- package/providers/fake/src/providers/identity.provider.ts +78 -67
- package/providers/fake/src/providers/inventory.provider.ts +54 -0
- package/providers/fake/src/providers/price.provider.ts +60 -0
- package/providers/fake/src/providers/product.provider.ts +53 -49
- package/providers/fake/src/providers/search.provider.ts +15 -33
- package/providers/posthog/src/core/initialize.ts +6 -4
- package/trpc/__mocks__/superjson.js +25 -0
- package/trpc/jest.config.ts +14 -0
- package/trpc/package.json +2 -1
- package/trpc/src/client.ts +176 -0
- package/trpc/src/index.ts +35 -62
- package/trpc/src/integration.spec.ts +216 -0
- package/trpc/src/server.ts +123 -0
- package/trpc/src/transparent-client.spec.ts +160 -0
- package/trpc/src/types.ts +142 -0
- package/trpc/tsconfig.json +3 -0
- package/trpc/tsconfig.lib.json +2 -1
- package/trpc/tsconfig.spec.json +15 -0
- package/tsconfig.base.json +0 -2
- package/core/src/cache/caching-strategy.ts +0 -25
- package/examples/angular/e2e/example.spec.ts +0 -9
- package/examples/angular/eslint.config.mjs +0 -41
- package/examples/angular/playwright.config.ts +0 -38
- package/examples/angular/project.json +0 -86
- package/examples/angular/public/favicon.ico +0 -0
- package/examples/angular/src/app/app.component.html +0 -6
- package/examples/angular/src/app/app.component.scss +0 -22
- package/examples/angular/src/app/app.component.ts +0 -14
- package/examples/angular/src/app/app.config.ts +0 -16
- package/examples/angular/src/app/app.routes.ts +0 -25
- package/examples/angular/src/app/cart/cart.component.html +0 -4
- package/examples/angular/src/app/cart/cart.component.scss +0 -14
- package/examples/angular/src/app/cart/cart.component.ts +0 -73
- package/examples/angular/src/app/identity/identity.component.html +0 -6
- package/examples/angular/src/app/identity/identity.component.scss +0 -18
- package/examples/angular/src/app/identity/identity.component.ts +0 -49
- package/examples/angular/src/app/product/product.component.html +0 -14
- package/examples/angular/src/app/product/product.component.scss +0 -11
- package/examples/angular/src/app/product/product.component.ts +0 -42
- package/examples/angular/src/app/search/search.component.html +0 -35
- package/examples/angular/src/app/search/search.component.scss +0 -129
- package/examples/angular/src/app/search/search.component.ts +0 -50
- package/examples/angular/src/app/services/product.service.ts +0 -35
- package/examples/angular/src/app/services/search.service.ts +0 -48
- package/examples/angular/src/app/services/trpc.client.ts +0 -27
- package/examples/angular/src/index.html +0 -13
- package/examples/angular/src/main.ts +0 -7
- package/examples/angular/src/styles.scss +0 -17
- package/examples/angular/src/test-setup.ts +0 -6
- package/examples/angular/tsconfig.app.json +0 -10
- package/examples/angular/tsconfig.editor.json +0 -6
- package/examples/angular/tsconfig.json +0 -32
- package/examples/node/src/initialize-algolia.spec.ts +0 -29
- package/examples/node/src/initialize-commercetools.spec.ts +0 -31
- package/examples/node/src/initialize-extended-providers.spec.ts +0 -38
- package/examples/node/src/initialize-mixed-providers.spec.ts +0 -36
- package/examples/node/src/providers/custom-algolia-product.provider.ts +0 -18
- package/examples/node/src/schemas/custom-product.schema.ts +0 -8
- package/examples/trpc-node/.env.example +0 -52
- package/examples/trpc-node/eslint.config.mjs +0 -3
- package/examples/trpc-node/project.json +0 -61
- package/examples/trpc-node/src/assets/.gitkeep +0 -0
- package/examples/trpc-node/src/main.ts +0 -59
- package/examples/trpc-node/src/router-instance.ts +0 -52
- package/examples/trpc-node/tsconfig.app.json +0 -9
- package/examples/trpc-node/tsconfig.json +0 -13
|
@@ -1,73 +1,77 @@
|
|
|
1
1
|
import {
|
|
2
|
-
BaseMutation,
|
|
3
2
|
Product,
|
|
4
|
-
ProductMutation,
|
|
5
3
|
ProductProvider,
|
|
6
|
-
|
|
4
|
+
ProductQueryById,
|
|
5
|
+
ProductQueryBySlug,
|
|
7
6
|
Session,
|
|
7
|
+
Cache as ReactinaryCache,
|
|
8
8
|
} from '@reactionary/core';
|
|
9
9
|
import z from 'zod';
|
|
10
10
|
import { FakeConfiguration } from '../schema/configuration.schema';
|
|
11
11
|
import { base, en, Faker } from '@faker-js/faker';
|
|
12
|
+
import { traced } from '@reactionary/otel';
|
|
12
13
|
|
|
13
14
|
export class FakeProductProvider<
|
|
14
|
-
T extends Product = Product
|
|
15
|
-
|
|
16
|
-
M extends ProductMutation = ProductMutation
|
|
17
|
-
> extends ProductProvider<T, Q, M> {
|
|
15
|
+
T extends Product = Product
|
|
16
|
+
> extends ProductProvider<T> {
|
|
18
17
|
protected config: FakeConfiguration;
|
|
19
18
|
|
|
20
|
-
constructor(config: FakeConfiguration, schema: z.ZodType<T>,
|
|
21
|
-
super(schema,
|
|
19
|
+
constructor(config: FakeConfiguration, schema: z.ZodType<T>, cache: ReactinaryCache) {
|
|
20
|
+
super(schema, cache);
|
|
22
21
|
|
|
23
22
|
this.config = config;
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
@traced()
|
|
26
|
+
public override async getById(
|
|
27
|
+
payload: ProductQueryById,
|
|
28
|
+
_session: Session
|
|
29
|
+
): Promise<T> {
|
|
30
|
+
return this.parseSingle(payload);
|
|
31
|
+
}
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
public override async getBySlug(
|
|
34
|
+
payload: ProductQueryBySlug,
|
|
35
|
+
_session: Session
|
|
36
|
+
): Promise<T> {
|
|
37
|
+
return this.parseSingle(payload);
|
|
38
|
+
}
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
protected override parseSingle(body: ProductQueryById | ProductQueryBySlug): T {
|
|
41
|
+
const generator = new Faker({
|
|
42
|
+
seed: 42,
|
|
43
|
+
locale: [en, base],
|
|
44
|
+
});
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
identifier: {
|
|
40
|
-
key: key,
|
|
41
|
-
},
|
|
42
|
-
name: generator.commerce.productName(),
|
|
43
|
-
slug: slug,
|
|
44
|
-
attributes: [],
|
|
45
|
-
description: generator.commerce.productDescription(),
|
|
46
|
-
image: generator.image.urlPicsumPhotos({
|
|
47
|
-
width: 600,
|
|
48
|
-
height: 600,
|
|
49
|
-
}),
|
|
50
|
-
images: [],
|
|
51
|
-
meta: {
|
|
52
|
-
cache: {
|
|
53
|
-
hit: false,
|
|
54
|
-
key: key,
|
|
55
|
-
},
|
|
56
|
-
placeholder: false
|
|
57
|
-
},
|
|
58
|
-
skus: [],
|
|
59
|
-
};
|
|
46
|
+
const key = body.slug || body.id;
|
|
60
47
|
|
|
61
|
-
|
|
62
|
-
|
|
48
|
+
// Create a model instance based on the schema
|
|
49
|
+
const model = this.newModel();
|
|
63
50
|
|
|
64
|
-
|
|
65
|
-
|
|
51
|
+
// Merge the generated data into the model
|
|
52
|
+
Object.assign(model, {
|
|
53
|
+
identifier: {
|
|
54
|
+
key: key,
|
|
55
|
+
},
|
|
56
|
+
name: generator.commerce.productName(),
|
|
57
|
+
slug: key,
|
|
58
|
+
attributes: [],
|
|
59
|
+
description: generator.commerce.productDescription(),
|
|
60
|
+
image: generator.image.urlPicsumPhotos({
|
|
61
|
+
width: 600,
|
|
62
|
+
height: 600,
|
|
63
|
+
}),
|
|
64
|
+
images: [],
|
|
65
|
+
meta: {
|
|
66
|
+
cache: {
|
|
67
|
+
hit: false,
|
|
68
|
+
key: key,
|
|
69
|
+
},
|
|
70
|
+
placeholder: false,
|
|
71
|
+
},
|
|
72
|
+
skus: [],
|
|
73
|
+
});
|
|
66
74
|
|
|
67
|
-
|
|
68
|
-
mutation: BaseMutation[],
|
|
69
|
-
session: Session
|
|
70
|
-
): Promise<T> {
|
|
71
|
-
throw new Error('Method not implemented.');
|
|
75
|
+
return this.assert(model);
|
|
72
76
|
}
|
|
73
77
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
SearchIdentifier,
|
|
3
|
-
SearchMutation,
|
|
4
2
|
SearchProvider,
|
|
5
|
-
|
|
3
|
+
SearchQueryByTerm,
|
|
6
4
|
SearchResult,
|
|
7
5
|
SearchResultFacet,
|
|
8
6
|
SearchResultProduct,
|
|
9
7
|
Session,
|
|
8
|
+
Cache as ReactinaryCache,
|
|
10
9
|
} from '@reactionary/core';
|
|
11
10
|
import z from 'zod';
|
|
12
11
|
import { FakeConfiguration } from '../schema/configuration.schema';
|
|
@@ -14,41 +13,24 @@ import { Faker, en, base } from '@faker-js/faker';
|
|
|
14
13
|
import { jitter } from '../utilities/jitter';
|
|
15
14
|
|
|
16
15
|
export class FakeSearchProvider<
|
|
17
|
-
T extends SearchResult = SearchResult
|
|
18
|
-
|
|
19
|
-
M extends SearchMutation = SearchMutation
|
|
20
|
-
> extends SearchProvider<T, Q, M> {
|
|
16
|
+
T extends SearchResult = SearchResult
|
|
17
|
+
> extends SearchProvider<T> {
|
|
21
18
|
protected config: FakeConfiguration;
|
|
22
19
|
|
|
23
|
-
constructor(config: FakeConfiguration, schema: z.ZodType<T>,
|
|
24
|
-
super(schema,
|
|
20
|
+
constructor(config: FakeConfiguration, schema: z.ZodType<T>, cache: ReactinaryCache) {
|
|
21
|
+
super(schema, cache);
|
|
25
22
|
|
|
26
23
|
this.config = config;
|
|
27
24
|
}
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const result = await this.get(query.search);
|
|
34
|
-
|
|
35
|
-
results.push(result);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return results;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
protected override process(mutations: M[], session: Session): Promise<T> {
|
|
42
|
-
throw new Error('Method not implemented.');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
public async get(identifier: SearchIdentifier): Promise<T> {
|
|
26
|
+
public override async queryByTerm(
|
|
27
|
+
payload: SearchQueryByTerm,
|
|
28
|
+
_session: Session
|
|
29
|
+
): Promise<SearchResult> {
|
|
46
30
|
await jitter(this.config.jitter.mean, this.config.jitter.deviation);
|
|
47
31
|
|
|
48
|
-
|
|
49
|
-
}
|
|
32
|
+
const query = payload.search;
|
|
50
33
|
|
|
51
|
-
public parse(data: unknown, query: SearchIdentifier): T {
|
|
52
34
|
const querySpecificity =
|
|
53
35
|
20 - query.term.length - query.page - query.facets.length;
|
|
54
36
|
const totalProducts = 10 * querySpecificity;
|
|
@@ -77,7 +59,7 @@ export class FakeSearchProvider<
|
|
|
77
59
|
height: 300,
|
|
78
60
|
width: 300,
|
|
79
61
|
grayscale: true,
|
|
80
|
-
blur: 8
|
|
62
|
+
blur: 8,
|
|
81
63
|
}),
|
|
82
64
|
name: productGenerator.commerce.productName(),
|
|
83
65
|
slug: productGenerator.lorem.slug(),
|
|
@@ -131,10 +113,10 @@ export class FakeSearchProvider<
|
|
|
131
113
|
meta: {
|
|
132
114
|
cache: {
|
|
133
115
|
hit: false,
|
|
134
|
-
key: ''
|
|
116
|
+
key: '',
|
|
135
117
|
},
|
|
136
|
-
placeholder: false
|
|
137
|
-
}
|
|
118
|
+
placeholder: false,
|
|
119
|
+
},
|
|
138
120
|
} satisfies SearchResult;
|
|
139
121
|
|
|
140
122
|
return this.schema.parse(result);
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { Client } from "@reactionary/core";
|
|
1
|
+
import { Client, Cache } from "@reactionary/core";
|
|
2
2
|
import { PosthogConfiguration } from "../schema/configuration.schema";
|
|
3
3
|
import { PosthogCapabilities } from "../schema/capabilities.schema";
|
|
4
4
|
|
|
5
|
-
export function withPosthogCapabilities(
|
|
6
|
-
|
|
5
|
+
export function withPosthogCapabilities(_configuration: PosthogConfiguration, _capabilities: PosthogCapabilities) {
|
|
6
|
+
return (_cache: Cache) => {
|
|
7
|
+
const client: Partial<Client> = {};
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
return client;
|
|
10
|
+
};
|
|
9
11
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Mock superjson for Jest testing
|
|
2
|
+
module.exports = {
|
|
3
|
+
default: {
|
|
4
|
+
stringify: JSON.stringify,
|
|
5
|
+
parse: JSON.parse,
|
|
6
|
+
serialize: (obj) => ({ json: obj, meta: undefined }),
|
|
7
|
+
deserialize: (data) => data.json,
|
|
8
|
+
output: {
|
|
9
|
+
serialize: (obj) => ({ json: obj, meta: undefined })
|
|
10
|
+
},
|
|
11
|
+
input: {
|
|
12
|
+
deserialize: (data) => data.json
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
stringify: JSON.stringify,
|
|
16
|
+
parse: JSON.parse,
|
|
17
|
+
serialize: (obj) => ({ json: obj, meta: undefined }),
|
|
18
|
+
deserialize: (data) => data.json,
|
|
19
|
+
output: {
|
|
20
|
+
serialize: (obj) => ({ json: obj, meta: undefined })
|
|
21
|
+
},
|
|
22
|
+
input: {
|
|
23
|
+
deserialize: (data) => data.json
|
|
24
|
+
}
|
|
25
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
displayName: 'trpc',
|
|
3
|
+
preset: '../jest.preset.js',
|
|
4
|
+
testEnvironment: 'node',
|
|
5
|
+
testTimeout: 15000,
|
|
6
|
+
transform: {
|
|
7
|
+
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
|
|
8
|
+
},
|
|
9
|
+
moduleFileExtensions: ['ts', 'js', 'html'],
|
|
10
|
+
moduleNameMapper: {
|
|
11
|
+
'^superjson$': '<rootDir>/__mocks__/superjson.js',
|
|
12
|
+
},
|
|
13
|
+
coverageDirectory: '../coverage/trpc',
|
|
14
|
+
};
|
package/trpc/package.json
CHANGED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { Client, Session, isTRPCQuery, isTRPCMutation, isTRPCMethod } from '@reactionary/core';
|
|
2
|
+
import type { TransparentClient } from './types';
|
|
3
|
+
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
|
|
4
|
+
import type { TRPCClientError } from '@trpc/client';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration options for TRPC client creation
|
|
8
|
+
*/
|
|
9
|
+
export interface TRPCClientOptions {
|
|
10
|
+
/** Default session to use if not provided in method calls */
|
|
11
|
+
defaultSession?: Session;
|
|
12
|
+
/** Whether to automatically provide session from defaultSession */
|
|
13
|
+
autoSession?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a type-safe client proxy that uses the original client's type interface
|
|
18
|
+
* while routing all calls through TRPC
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* import { createTRPCProxyClient } from '@trpc/client';
|
|
23
|
+
* import { createTRPCClient } from '@reactionary/trpc/client';
|
|
24
|
+
*
|
|
25
|
+
* const trpc = createTRPCProxyClient<AppRouter>({
|
|
26
|
+
* url: 'http://localhost:3000',
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // Pass the original client type as the generic parameter
|
|
30
|
+
* const client = createTRPCClient<typeof serverClient>(trpc, {
|
|
31
|
+
* defaultSession: mySession,
|
|
32
|
+
* autoSession: true
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* // Fully typed using the original client interface!
|
|
36
|
+
* const product = await client.product.getById({ id: '123' }, session);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function createTRPCClient<TOriginalClient extends Partial<Client>>(
|
|
40
|
+
trpcClient: any,
|
|
41
|
+
options: TRPCClientOptions = {}
|
|
42
|
+
): TransparentClient<TOriginalClient> {
|
|
43
|
+
const { defaultSession, autoSession = false } = options;
|
|
44
|
+
|
|
45
|
+
return new Proxy({} as TransparentClient<TOriginalClient>, {
|
|
46
|
+
get(target, providerName: string | symbol) {
|
|
47
|
+
if (typeof providerName !== 'string') {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Return a typed proxy for the provider that intercepts method calls
|
|
52
|
+
return new Proxy({}, {
|
|
53
|
+
get(providerTarget, methodName: string | symbol) {
|
|
54
|
+
if (typeof methodName !== 'string') {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Only expose methods that are marked with TRPC decorators
|
|
59
|
+
// This eliminates the need to filter TRPC-specific properties
|
|
60
|
+
return async (payload: any, sessionArg?: Session) => {
|
|
61
|
+
// Determine session to use
|
|
62
|
+
let session = sessionArg;
|
|
63
|
+
if (!session && autoSession && defaultSession) {
|
|
64
|
+
session = defaultSession;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Prepare input for TRPC call
|
|
68
|
+
const input = {
|
|
69
|
+
payload,
|
|
70
|
+
session
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Access TRPC provider and method lazily
|
|
74
|
+
const trpcProvider = trpcClient[providerName];
|
|
75
|
+
const trpcMethod = trpcProvider[methodName];
|
|
76
|
+
|
|
77
|
+
// Use decorator metadata to determine if this is a query or mutation
|
|
78
|
+
// Note: We can't directly check the original provider here since we only have
|
|
79
|
+
// the TRPC client, so we'll fall back to the router's procedure type detection
|
|
80
|
+
if (trpcMethod?.query) {
|
|
81
|
+
return await trpcMethod.query(input);
|
|
82
|
+
} else if (trpcMethod?.mutate) {
|
|
83
|
+
return await trpcMethod.mutate(input);
|
|
84
|
+
} else {
|
|
85
|
+
throw new Error(`Method ${String(providerName)}.${String(methodName)} not found on TRPC client`);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Session provider interface for dependency injection
|
|
96
|
+
*/
|
|
97
|
+
export interface SessionProvider {
|
|
98
|
+
getSession(): Promise<Session> | Session;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Create a TRPC client with session provider for automatic session management
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* const sessionProvider: SessionProvider = {
|
|
107
|
+
* getSession: () => getCurrentUserSession()
|
|
108
|
+
* };
|
|
109
|
+
*
|
|
110
|
+
* const client = createTRPCClientWithSessionProvider(trpc, sessionProvider);
|
|
111
|
+
*
|
|
112
|
+
* // Session is automatically provided, fully typed
|
|
113
|
+
* const product = await client.product.getById({ id: '123' });
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export function createTRPCClientWithSessionProvider<TOriginalClient extends Partial<Client>>(
|
|
117
|
+
trpcClient: any,
|
|
118
|
+
sessionProvider: SessionProvider
|
|
119
|
+
): TransparentClient<TOriginalClient> {
|
|
120
|
+
return new Proxy({} as TransparentClient<TOriginalClient>, {
|
|
121
|
+
get(target, providerName: string | symbol) {
|
|
122
|
+
if (typeof providerName !== 'string') {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return new Proxy({}, {
|
|
127
|
+
get(providerTarget, methodName: string | symbol) {
|
|
128
|
+
if (typeof methodName !== 'string') {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return async (payload: any, sessionArg?: Session) => {
|
|
133
|
+
// If no session provided, get from provider
|
|
134
|
+
let session = sessionArg;
|
|
135
|
+
if (!session) {
|
|
136
|
+
session = await sessionProvider.getSession();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const input = {
|
|
140
|
+
payload,
|
|
141
|
+
session
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// Access TRPC provider and method lazily
|
|
145
|
+
const trpcProvider = trpcClient[providerName];
|
|
146
|
+
const trpcMethod = trpcProvider[methodName];
|
|
147
|
+
|
|
148
|
+
// Use TRPC client's procedure type detection
|
|
149
|
+
if (trpcMethod?.query) {
|
|
150
|
+
return await trpcMethod.query(input);
|
|
151
|
+
} else if (trpcMethod?.mutate) {
|
|
152
|
+
return await trpcMethod.mutate(input);
|
|
153
|
+
} else {
|
|
154
|
+
throw new Error(`Method ${String(providerName)}.${String(methodName)} not found on TRPC client`);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Type alias for creating typed TRPC clients
|
|
165
|
+
* Use the original client type, not the router type
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* type MyClient = typeof serverClient;
|
|
170
|
+
*
|
|
171
|
+
* function useClient(): MyClient {
|
|
172
|
+
* return createTRPCClient<MyClient>(trpcProxyClient);
|
|
173
|
+
* }
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
export type TRPCClientFromRouter<TOriginalClient> = TOriginalClient;
|
package/trpc/src/index.ts
CHANGED
|
@@ -1,70 +1,43 @@
|
|
|
1
|
+
// Re-export server utilities
|
|
2
|
+
export {
|
|
3
|
+
createTRPCServerRouter,
|
|
4
|
+
createTRPCContext,
|
|
5
|
+
router,
|
|
6
|
+
mergeRouters,
|
|
7
|
+
type TRPCRouterFromClient
|
|
8
|
+
} from './server';
|
|
9
|
+
|
|
10
|
+
// Re-export client utilities
|
|
11
|
+
export {
|
|
12
|
+
createTRPCClient,
|
|
13
|
+
createTRPCClientWithSessionProvider,
|
|
14
|
+
type TRPCClientOptions,
|
|
15
|
+
type SessionProvider,
|
|
16
|
+
type TRPCClientFromRouter
|
|
17
|
+
} from './client';
|
|
18
|
+
|
|
19
|
+
// Re-export type utilities
|
|
20
|
+
export {
|
|
21
|
+
type ClientMethodMap,
|
|
22
|
+
type MethodInfo,
|
|
23
|
+
type TRPCMethodInput,
|
|
24
|
+
type TransparentClient,
|
|
25
|
+
introspectClient,
|
|
26
|
+
isQueryMethod,
|
|
27
|
+
isMutationMethod
|
|
28
|
+
} from './types';
|
|
29
|
+
|
|
30
|
+
// Legacy exports for backward compatibility
|
|
1
31
|
import { initTRPC } from '@trpc/server';
|
|
2
|
-
import {
|
|
3
|
-
Client,
|
|
4
|
-
Session,
|
|
5
|
-
} from '@reactionary/core';
|
|
6
|
-
import superjson from 'superjson';
|
|
7
|
-
import { z } from 'zod';
|
|
8
|
-
import { BaseProvider } from '@reactionary/core';
|
|
9
|
-
import { TRPCQueryProcedure, TRPCMutationProcedure } from '@trpc/server';
|
|
32
|
+
import { Client, Session } from '@reactionary/core';
|
|
10
33
|
import { createTRPCTracing } from '@reactionary/otel';
|
|
11
34
|
|
|
12
|
-
const t = initTRPC.context<{ client: Client; session: Session }>().create({
|
|
13
|
-
transformer: superjson,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
export const router = t.router;
|
|
17
|
-
export const mergeRouters = t.mergeRouters;
|
|
35
|
+
const t = initTRPC.context<{ client: Client; session: Session }>().create({});
|
|
18
36
|
|
|
19
37
|
// Always apply tracing middleware - exporters controlled via OTEL env vars
|
|
20
38
|
const basePublicProcedure = t.procedure;
|
|
21
39
|
export const publicProcedure = basePublicProcedure.use(createTRPCTracing());
|
|
22
40
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}[keyof T];
|
|
27
|
-
|
|
28
|
-
type ReactionaryRouter = {
|
|
29
|
-
[Property in BaseProviderKeys<Client>]: TRPCQueryProcedure<{
|
|
30
|
-
input: Array<z.infer<T[Property]['querySchema']>>;
|
|
31
|
-
output: Array<z.infer<Awaited<T[Property]['schema']>>>;
|
|
32
|
-
meta: any;
|
|
33
|
-
}>;
|
|
34
|
-
} & {
|
|
35
|
-
[Property in BaseProviderKeys<Client> as `${Property}Mutation`]: TRPCMutationProcedure<{
|
|
36
|
-
input: Array<z.infer<T[Property]['mutationSchema']>>;
|
|
37
|
-
output: z.infer<Awaited<T[Property]['schema']>>;
|
|
38
|
-
meta: any;
|
|
39
|
-
}>;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const routes: Record<string, any> = {};
|
|
43
|
-
// Always enable tracing - exporters are controlled via env vars
|
|
44
|
-
const procedure = basePublicProcedure.use(createTRPCTracing());
|
|
45
|
-
|
|
46
|
-
for (const key in client) {
|
|
47
|
-
const provider = client[key];
|
|
48
|
-
|
|
49
|
-
if (provider instanceof BaseProvider) {
|
|
50
|
-
const queryKey = key as keyof ReactionaryRouter;
|
|
51
|
-
const mutationKey = key + 'Mutation' as keyof ReactionaryRouter;
|
|
52
|
-
|
|
53
|
-
routes[queryKey] = procedure
|
|
54
|
-
.input(provider.querySchema.array())
|
|
55
|
-
.output(provider.schema.array())
|
|
56
|
-
.query(async (opts) => {
|
|
57
|
-
return provider.query(opts.input, opts.ctx.session);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
routes[mutationKey] = procedure
|
|
61
|
-
.input(provider.mutationSchema.array())
|
|
62
|
-
.output(provider.schema)
|
|
63
|
-
.mutation(async (opts) => {
|
|
64
|
-
return provider.mutate(opts.input, opts.ctx.session);
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return router<ReactionaryRouter>(routes as ReactionaryRouter);
|
|
70
|
-
}
|
|
41
|
+
// Legacy function - deprecated, use createTRPCServerRouter instead
|
|
42
|
+
import { createTRPCServerRouter } from './server';
|
|
43
|
+
export const createTRPCRouter = createTRPCServerRouter;
|