Skip to content

Commit 5b801b5

Browse files
authored
feat(cli): Add typed client to a generated app (#2669)
1 parent 24e4bc0 commit 5b801b5

File tree

15 files changed

+117
-50
lines changed

15 files changed

+117
-50
lines changed

packages/authentication-oauth/src/index.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,14 @@ export const setup = (options: OauthSetupSettings) => (app: Application) => {
4242
}
4343
}
4444

45-
const grant = defaultsDeep(
46-
{},
47-
omit(oauth, ['redirect', 'origins']),
48-
{
49-
defaults: {
50-
prefix: '/oauth',
51-
origin: `${protocol}://${host}`,
52-
transport: 'session',
53-
response: ['tokens', 'raw', 'profile']
54-
}
45+
const grant = defaultsDeep({}, omit(oauth, ['redirect', 'origins']), {
46+
defaults: {
47+
prefix: '/oauth',
48+
origin: `${protocol}://${host}`,
49+
transport: 'session',
50+
response: ['tokens', 'raw', 'profile']
5551
}
56-
)
52+
})
5753

5854
const getUrl = (url: string) => {
5955
const { defaults } = grant
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { generator, toFile } from '@feathershq/pinion'
2+
import { renderSource } from '../../commons'
3+
import { AppGeneratorContext } from '../index'
4+
5+
const template = ({}: AppGeneratorContext) =>
6+
`import { feathers, type Service, type TransportConnection } from '@feathersjs/feathers'
7+
8+
// A mapping of client side services
9+
export interface ServiceTypes {
10+
}
11+
12+
export const createClient = <Configuration = any> (connection: TransportConnection<ServiceTypes>) => {
13+
const client = feathers<ServiceTypes, Configuration>()
14+
15+
client.configure(connection)
16+
17+
return client
18+
}
19+
`
20+
21+
export const generate = async (ctx: AppGeneratorContext) =>
22+
generator(ctx).then(
23+
renderSource(
24+
template,
25+
toFile<AppGeneratorContext>(({ lib }) => lib, 'client')
26+
)
27+
)

packages/cli/src/app/templates/package.json.tpl.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ const jsPackageJson = (lib: string) => ({
1313
const tsPackageJson = (lib: string) => ({
1414
scripts: {
1515
dev: `nodemon -x ts-node ${lib}/index.ts`,
16-
compile: 'shx rm -rf lib/ && tsc',
17-
start: 'npm run compile && node lib/',
16+
compile: 'shx rm -rf dist/ && tsc',
17+
start: 'npm run compile && node dist/',
1818
test: 'mocha test/ --require ts-node/register --recursive --extension .ts --exit'
1919
}
2020
})
@@ -54,6 +54,7 @@ const packageJson = ({
5454
test
5555
},
5656
main: `${lib}/`,
57+
browser: language === 'ts' ? 'dist/client' : `${lib}/client`,
5758
...(language === 'ts' ? tsPackageJson(lib) : jsPackageJson(lib))
5859
})
5960

packages/cli/src/app/templates/tsconfig.json.tpl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const generate = (ctx: AppGeneratorContext) =>
1313
compilerOptions: {
1414
target: 'es2020',
1515
module: 'commonjs',
16-
outDir: './lib',
16+
outDir: './dist',
1717
rootDir: `./${lib}`,
1818
strict: true,
1919
esModuleInterop: true

packages/cli/src/authentication/templates/declarations.tpl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { generator, inject, before, toFile, when, append } from '@feathershq/pin
22
import { AuthenticationGeneratorContext } from '../index'
33

44
const importTemplate = ({ upperName, schemaPath }: AuthenticationGeneratorContext) =>
5-
`import { ${upperName}Result } from './schemas/${schemaPath}'
5+
`import { ${upperName}Result } from './${schemaPath}'
66
`
77

88
const paramsTemplate = ({

packages/cli/src/authentication/templates/user.schema.tpl.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export const ${camelName}ResultSchema = schema({
5151
$id: '${upperName}Result',
5252
type: 'object',
5353
additionalProperties: false,
54-
required: [ ...${camelName}DataSchema.required, '${type === 'mongodb' ? '_id' : 'id'}' ],
54+
required: [ '${type === 'mongodb' ? '_id' : 'id'}' ],
5555
properties: {
5656
...${camelName}DataSchema.properties,
5757
${type === 'mongodb' ? '_id' : 'id'}: {
@@ -62,14 +62,16 @@ export const ${camelName}ResultSchema = schema({
6262
6363
export type ${upperName}Result = Infer<typeof ${camelName}ResultSchema>
6464
65+
// Queries shouldn't allow doing anything with the password
66+
const { password, ...${camelName}QueryProperties } = ${camelName}ResultSchema.properties
6567
6668
// Schema for allowed query properties
6769
export const ${camelName}QuerySchema = schema({
6870
$id: '${upperName}Query',
6971
type: 'object',
7072
additionalProperties: false,
7173
properties: {
72-
...querySyntax(${camelName}ResultSchema.properties)
74+
...querySyntax(${camelName}QueryProperties)
7375
}
7476
} as const)
7577

packages/cli/src/commons.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export const initializeBaseContext =
6969
...ctx,
7070
lib: ctx.pkg?.directories?.lib || 'src',
7171
test: ctx.pkg?.directories?.test || 'test',
72+
language: ctx.pkg?.feathers?.language || 'ts',
7273
feathers: ctx.pkg?.feathers
7374
}))
7475

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { generator, inject, toFile, when, after } from '@feathershq/pinion'
2+
import { ServiceGeneratorContext } from '../index'
3+
4+
const schemaImports = ({ upperName, schemaPath }: ServiceGeneratorContext) => `import type {
5+
${upperName}Data,
6+
${upperName}Result,
7+
${upperName}Query,
8+
} from './${schemaPath}'`
9+
const declarationTemplate = ({ path, upperName }: ServiceGeneratorContext) =>
10+
` '${path}': Service<${upperName}Data, ${upperName}Result, Params<${upperName}Query>>`
11+
12+
const toClientFile = toFile<ServiceGeneratorContext>(({ lib, language }) => [lib, `client.${language}`])
13+
14+
export const generate = async (ctx: ServiceGeneratorContext) =>
15+
generator(ctx)
16+
.then(
17+
when(
18+
(ctx) => ctx.language === 'ts',
19+
inject(schemaImports, after("from '@feathersjs/feathers'"), toClientFile)
20+
)
21+
)
22+
.then(
23+
when(
24+
(ctx) => ctx.language === 'ts',
25+
inject(declarationTemplate, after('export interface ServiceTypes'), toClientFile)
26+
)
27+
)

packages/feathers/src/declarations.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,19 @@ type OptionalPick<T, K extends PropertyKey> = Pick<T, Extract<keyof T, K>>
66

77
export type { NextFunction }
88

9+
/**
10+
* The object returned from `.find` call by standard database adapters
11+
*/
912
export interface Paginated<T> {
1013
total: number
1114
limit: number
1215
skip: number
1316
data: T[]
1417
}
1518

19+
/**
20+
* Options that can be passed when registering a service via `app.use(name, service, options)`
21+
*/
1622
export interface ServiceOptions {
1723
events?: string[]
1824
methods?: string[]
@@ -89,6 +95,21 @@ export type CustomMethods<T extends { [key: string]: [any, any] }> = {
8995
[K in keyof T]: (data: T[K][0], params?: Params) => Promise<T[K][1]>
9096
}
9197

98+
/**
99+
* An interface usually use by transport clients that represents a e.g. HTTP or websocket
100+
* connection that can be configured on the application.
101+
*/
102+
export type TransportConnection<Services = any> = {
103+
(app: Application<Services>): void
104+
Service: any
105+
service: <L extends keyof Services & string>(
106+
name: L
107+
) => keyof any extends keyof Services ? ServiceInterface : Services[L]
108+
}
109+
110+
/**
111+
* The interface for a custom service method. Can e.g. be used to type client side services.
112+
*/
92113
export type CustomMethod<T = any, R = T, P extends Params = Params> = (data: T, params?: P) => Promise<R>
93114

94115
export type ServiceMixin<A> = (service: FeathersService<A>, path: string, options: ServiceOptions) => void

packages/rest-client/src/index.ts

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Application, defaultServiceMethods } from '@feathersjs/feathers'
1+
import { Application, TransportConnection, defaultServiceMethods } from '@feathersjs/feathers'
22

33
import { Base } from './base'
44
import { AxiosClient } from './axios'
@@ -13,34 +13,21 @@ const transports = {
1313
axios: AxiosClient
1414
}
1515

16-
interface HandlerResult extends Function {
17-
/**
18-
* initialize service
19-
*/
20-
(): void
21-
22-
/**
23-
* Transport Service
24-
*/
25-
Service: any
26-
27-
/**
28-
* default Service
29-
*/
30-
service: any
31-
}
32-
33-
export type Handler = (connection: any, options?: any, Service?: any) => HandlerResult
16+
export type Handler<ServiceTypes> = (
17+
connection: any,
18+
options?: any,
19+
Service?: any
20+
) => TransportConnection<ServiceTypes>
3421

35-
export interface Transport {
36-
superagent: Handler
37-
fetch: Handler
38-
axios: Handler
22+
export interface Transport<ServiceTypes> {
23+
superagent: Handler<ServiceTypes>
24+
fetch: Handler<ServiceTypes>
25+
axios: Handler<ServiceTypes>
3926
}
4027

4128
export type RestService<T = any, D = Partial<any>> = Base<T, D>
4229

43-
export default function restClient(base = '') {
30+
export default function restClient<ServiceTypes = any>(base = '') {
4431
const result: any = { Base }
4532

4633
Object.keys(transports).forEach((key) => {
@@ -81,7 +68,7 @@ export default function restClient(base = '') {
8168
}
8269
})
8370

84-
return result as Transport
71+
return result as Transport<ServiceTypes>
8572
}
8673

8774
if (typeof module !== 'undefined') {

0 commit comments

Comments
 (0)