@nmtjs/contract 0.0.1
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/LICENSE.md +7 -0
- package/README.md +9 -0
- package/dist/compiler.js +50 -0
- package/dist/compiler.js.map +1 -0
- package/dist/contract.js +24 -0
- package/dist/contract.js.map +1 -0
- package/dist/formats.js +127 -0
- package/dist/formats.js.map +1 -0
- package/dist/guards/blob.js +3 -0
- package/dist/guards/blob.js.map +1 -0
- package/dist/guards/event.js +3 -0
- package/dist/guards/event.js.map +1 -0
- package/dist/guards/native-enum.js +3 -0
- package/dist/guards/native-enum.js.map +1 -0
- package/dist/guards/nullable.js +2 -0
- package/dist/guards/nullable.js.map +1 -0
- package/dist/guards/procedure.js +3 -0
- package/dist/guards/procedure.js.map +1 -0
- package/dist/guards/service.js +3 -0
- package/dist/guards/service.js.map +1 -0
- package/dist/guards/subscription.js +3 -0
- package/dist/guards/subscription.js.map +1 -0
- package/dist/guards/union-enum.js +3 -0
- package/dist/guards/union-enum.js.map +1 -0
- package/dist/guards.js +20 -0
- package/dist/guards.js.map +1 -0
- package/dist/schemas/blob.js +26 -0
- package/dist/schemas/blob.js.map +1 -0
- package/dist/schemas/event.js +12 -0
- package/dist/schemas/event.js.map +1 -0
- package/dist/schemas/native-enum.js +14 -0
- package/dist/schemas/native-enum.js.map +1 -0
- package/dist/schemas/nullable.js +5 -0
- package/dist/schemas/nullable.js.map +1 -0
- package/dist/schemas/procedure.js +14 -0
- package/dist/schemas/procedure.js.map +1 -0
- package/dist/schemas/service.js +35 -0
- package/dist/schemas/service.js.map +1 -0
- package/dist/schemas/subscription.js +16 -0
- package/dist/schemas/subscription.js.map +1 -0
- package/dist/schemas/union-enum.js +13 -0
- package/dist/schemas/union-enum.js.map +1 -0
- package/dist/utils.js +11 -0
- package/dist/utils.js.map +1 -0
- package/package.json +41 -0
- package/src/compiler.ts +63 -0
- package/src/contract.ts +62 -0
- package/src/formats.ts +181 -0
- package/src/guards/blob.ts +5 -0
- package/src/guards/event.ts +6 -0
- package/src/guards/native-enum.ts +7 -0
- package/src/guards/nullable.ts +14 -0
- package/src/guards/procedure.ts +6 -0
- package/src/guards/service.ts +6 -0
- package/src/guards/subscription.ts +10 -0
- package/src/guards/union-enum.ts +7 -0
- package/src/guards.ts +21 -0
- package/src/schemas/blob.ts +58 -0
- package/src/schemas/event.ts +35 -0
- package/src/schemas/native-enum.ts +37 -0
- package/src/schemas/nullable.ts +4 -0
- package/src/schemas/procedure.ts +61 -0
- package/src/schemas/service.ts +126 -0
- package/src/schemas/subscription.ts +82 -0
- package/src/schemas/union-enum.ts +43 -0
- package/src/utils.ts +16 -0
- package/tsconfig.json +3 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Kind, type TSchema, TypeRegistry } from '@sinclair/typebox/type'
|
|
2
|
+
import {
|
|
3
|
+
type ContractSchemaOptions,
|
|
4
|
+
applyNames,
|
|
5
|
+
createSchema,
|
|
6
|
+
} from '../utils.ts'
|
|
7
|
+
import type { TEventContract } from './event.ts'
|
|
8
|
+
import type { TProcedureContract } from './procedure.ts'
|
|
9
|
+
import { SubscriptionKind, type TSubscriptionContract } from './subscription.ts'
|
|
10
|
+
|
|
11
|
+
export const ServiceKind = 'NeemataService'
|
|
12
|
+
|
|
13
|
+
export interface TServiceContract<
|
|
14
|
+
Name extends string = string,
|
|
15
|
+
Transports extends { [K in string]?: true } = {},
|
|
16
|
+
Procedures extends Record<
|
|
17
|
+
string,
|
|
18
|
+
TProcedureContract | TSubscriptionContract
|
|
19
|
+
> = Record<string, TProcedureContract | TSubscriptionContract>,
|
|
20
|
+
Events extends Record<string, TEventContract> = Record<
|
|
21
|
+
string,
|
|
22
|
+
TEventContract
|
|
23
|
+
>,
|
|
24
|
+
> extends TSchema {
|
|
25
|
+
[Kind]: typeof ServiceKind
|
|
26
|
+
static: {
|
|
27
|
+
procedures: {
|
|
28
|
+
[K in keyof Procedures]: Procedures[K]['static']
|
|
29
|
+
}
|
|
30
|
+
subscriptions: {
|
|
31
|
+
[K in keyof Procedures]: Procedures[K]['static']
|
|
32
|
+
}
|
|
33
|
+
events: {
|
|
34
|
+
[K in keyof Events]: Events[K]['static']
|
|
35
|
+
}
|
|
36
|
+
transports: Transports
|
|
37
|
+
}
|
|
38
|
+
type: 'neemata:service'
|
|
39
|
+
name: Name
|
|
40
|
+
transports: Transports
|
|
41
|
+
procedures: {
|
|
42
|
+
[K in keyof Procedures]: Procedures[K] extends TProcedureContract<
|
|
43
|
+
infer Input,
|
|
44
|
+
infer Output
|
|
45
|
+
>
|
|
46
|
+
? TProcedureContract<Input, Output, Extract<K, string>, Name, Transports>
|
|
47
|
+
: Procedures[K] extends TSubscriptionContract<
|
|
48
|
+
infer Input,
|
|
49
|
+
infer Output,
|
|
50
|
+
infer Options,
|
|
51
|
+
infer Events
|
|
52
|
+
>
|
|
53
|
+
? TSubscriptionContract<
|
|
54
|
+
Input,
|
|
55
|
+
Output,
|
|
56
|
+
Options,
|
|
57
|
+
{
|
|
58
|
+
[EK in keyof Events]: Events[EK] extends TEventContract<
|
|
59
|
+
infer Payload
|
|
60
|
+
>
|
|
61
|
+
? TEventContract<
|
|
62
|
+
Payload,
|
|
63
|
+
Extract<EK, string>,
|
|
64
|
+
Name,
|
|
65
|
+
Extract<K, string>
|
|
66
|
+
>
|
|
67
|
+
: never
|
|
68
|
+
},
|
|
69
|
+
Extract<K, string>,
|
|
70
|
+
Name,
|
|
71
|
+
Transports
|
|
72
|
+
>
|
|
73
|
+
: never
|
|
74
|
+
}
|
|
75
|
+
events: {
|
|
76
|
+
[K in Extract<keyof Events, string>]: Events[K] extends TEventContract<
|
|
77
|
+
infer Payload
|
|
78
|
+
>
|
|
79
|
+
? TEventContract<Payload, K, Name>
|
|
80
|
+
: never
|
|
81
|
+
}
|
|
82
|
+
timeout?: number
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const ServiceContract = <
|
|
86
|
+
Name extends string,
|
|
87
|
+
Transports extends { [key: string]: true },
|
|
88
|
+
Procedures extends Record<string, TProcedureContract | TSubscriptionContract>,
|
|
89
|
+
Events extends Record<string, TEventContract>,
|
|
90
|
+
>(
|
|
91
|
+
name: Name,
|
|
92
|
+
transports: Transports,
|
|
93
|
+
procedures: Procedures = {} as Procedures,
|
|
94
|
+
events: Events = {} as Events,
|
|
95
|
+
timeout?: number,
|
|
96
|
+
schemaOptions: ContractSchemaOptions = {} as ContractSchemaOptions,
|
|
97
|
+
) => {
|
|
98
|
+
if (!TypeRegistry.Has(ServiceKind)) TypeRegistry.Set(ServiceKind, () => true)
|
|
99
|
+
|
|
100
|
+
const serviceProcedures = {}
|
|
101
|
+
|
|
102
|
+
for (const [procedureName, procedure] of Object.entries(procedures)) {
|
|
103
|
+
if (procedure[Kind] === SubscriptionKind) {
|
|
104
|
+
serviceProcedures[procedureName] = {
|
|
105
|
+
...procedure,
|
|
106
|
+
events: applyNames(procedure.events, {
|
|
107
|
+
serviceName: name,
|
|
108
|
+
subscriptionName: procedureName,
|
|
109
|
+
}),
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
serviceProcedures[procedureName] = procedure
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return createSchema<TServiceContract<Name, Transports, Procedures, Events>>({
|
|
117
|
+
...schemaOptions,
|
|
118
|
+
[Kind]: ServiceKind,
|
|
119
|
+
name: name,
|
|
120
|
+
type: 'neemata:service',
|
|
121
|
+
procedures: applyNames(procedures, { serviceName: name }),
|
|
122
|
+
events: applyNames(events, { serviceName: name }),
|
|
123
|
+
transports,
|
|
124
|
+
timeout,
|
|
125
|
+
})
|
|
126
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Kind, type TSchema, TypeRegistry } from '@sinclair/typebox/type'
|
|
2
|
+
import { type ContractSchemaOptions, createSchema } from '../utils.ts'
|
|
3
|
+
import type { TEventContract } from './event.ts'
|
|
4
|
+
import type { TBaseProcedureContract } from './procedure.ts'
|
|
5
|
+
|
|
6
|
+
export const SubscriptionKind = 'NeemataSubscription'
|
|
7
|
+
|
|
8
|
+
export type TSubcriptionOptions = { static: Record<string, string | number> }
|
|
9
|
+
|
|
10
|
+
export interface TSubscriptionContract<
|
|
11
|
+
Input extends TSchema = TSchema,
|
|
12
|
+
Output extends TSchema = TSchema,
|
|
13
|
+
Options extends TSubcriptionOptions = TSubcriptionOptions,
|
|
14
|
+
Events extends Record<string, TEventContract> = Record<
|
|
15
|
+
string,
|
|
16
|
+
TEventContract
|
|
17
|
+
>,
|
|
18
|
+
Name extends string | undefined = string | undefined,
|
|
19
|
+
ServiceName extends string | undefined = string | undefined,
|
|
20
|
+
Transports extends { [K in string]?: true } | undefined =
|
|
21
|
+
| { [K in string]?: true }
|
|
22
|
+
| undefined,
|
|
23
|
+
> extends TBaseProcedureContract<Input, Output, Name, ServiceName, Transports> {
|
|
24
|
+
[Kind]: typeof SubscriptionKind
|
|
25
|
+
type: 'neemata:subscription'
|
|
26
|
+
static: {
|
|
27
|
+
input: Input['static']
|
|
28
|
+
output: Output['static']
|
|
29
|
+
options: Options['static']
|
|
30
|
+
events: {
|
|
31
|
+
[K in keyof Events]: Events[K]['static']
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
options: Options
|
|
35
|
+
events: Events
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const SubscriptionContract = <
|
|
39
|
+
Input extends TSchema = TSchema,
|
|
40
|
+
Output extends TSchema = TSchema,
|
|
41
|
+
Options extends TSubcriptionOptions = TSubcriptionOptions,
|
|
42
|
+
Events extends Record<string, TEventContract> = Record<
|
|
43
|
+
string,
|
|
44
|
+
TEventContract
|
|
45
|
+
>,
|
|
46
|
+
Name extends string | undefined = string | undefined,
|
|
47
|
+
ServiceName extends string | undefined = string | undefined,
|
|
48
|
+
Transports extends { [K in string]?: true } | undefined =
|
|
49
|
+
| { [K in string]?: true }
|
|
50
|
+
| undefined,
|
|
51
|
+
>(
|
|
52
|
+
input: Input,
|
|
53
|
+
output: Output,
|
|
54
|
+
options: Options,
|
|
55
|
+
events: Events,
|
|
56
|
+
timeout?: number,
|
|
57
|
+
schemaOptions: ContractSchemaOptions = {} as ContractSchemaOptions,
|
|
58
|
+
) => {
|
|
59
|
+
if (!TypeRegistry.Has(SubscriptionKind))
|
|
60
|
+
TypeRegistry.Set(SubscriptionKind, () => true)
|
|
61
|
+
|
|
62
|
+
return createSchema<
|
|
63
|
+
TSubscriptionContract<
|
|
64
|
+
Input,
|
|
65
|
+
Output,
|
|
66
|
+
Options,
|
|
67
|
+
Events,
|
|
68
|
+
Name,
|
|
69
|
+
ServiceName,
|
|
70
|
+
Transports
|
|
71
|
+
>
|
|
72
|
+
>({
|
|
73
|
+
...schemaOptions,
|
|
74
|
+
[Kind]: SubscriptionKind,
|
|
75
|
+
type: 'neemata:subscription',
|
|
76
|
+
input,
|
|
77
|
+
output,
|
|
78
|
+
events,
|
|
79
|
+
options,
|
|
80
|
+
timeout,
|
|
81
|
+
})
|
|
82
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Kind,
|
|
3
|
+
type SchemaOptions,
|
|
4
|
+
type TSchema,
|
|
5
|
+
TypeRegistry,
|
|
6
|
+
} from '@sinclair/typebox/type'
|
|
7
|
+
|
|
8
|
+
export const UnionEnumKind = 'UnionEnum'
|
|
9
|
+
|
|
10
|
+
// Ref: https://github.com/sinclairzx81/typebox/blob/master/example/prototypes/union-enum.ts
|
|
11
|
+
|
|
12
|
+
// -------------------------------------------------------------------------------------
|
|
13
|
+
// TUnionEnum
|
|
14
|
+
// -------------------------------------------------------------------------------------
|
|
15
|
+
export interface TUnionEnum<T extends (string | number)[]> extends TSchema {
|
|
16
|
+
[Kind]: typeof UnionEnumKind
|
|
17
|
+
static: T[number]
|
|
18
|
+
enum: T
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// -------------------------------------------------------------------------------------
|
|
22
|
+
// UnionEnum
|
|
23
|
+
// -------------------------------------------------------------------------------------
|
|
24
|
+
/** `[Experimental]` Creates a Union type with a `enum` schema representation */
|
|
25
|
+
export function UnionEnum<T extends (string | number)[]>(
|
|
26
|
+
values: [...T],
|
|
27
|
+
options: SchemaOptions = {},
|
|
28
|
+
) {
|
|
29
|
+
function UnionEnumCheck(
|
|
30
|
+
schema: TUnionEnum<(string | number)[]>,
|
|
31
|
+
value: unknown,
|
|
32
|
+
) {
|
|
33
|
+
return (
|
|
34
|
+
(typeof value === 'string' || typeof value === 'number') &&
|
|
35
|
+
schema.enum.includes(value)
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!TypeRegistry.Has(UnionEnumKind))
|
|
40
|
+
TypeRegistry.Set(UnionEnumKind, UnionEnumCheck)
|
|
41
|
+
|
|
42
|
+
return { ...options, [Kind]: UnionEnumKind, enum: values } as TUnionEnum<T>
|
|
43
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SchemaOptions, TSchema } from '@sinclair/typebox'
|
|
2
|
+
|
|
3
|
+
export type ContractSchemaOptions = Pick<SchemaOptions, 'title' | 'description'>
|
|
4
|
+
|
|
5
|
+
export const applyNames = <T extends Record<string, { serviceName?: string }>>(
|
|
6
|
+
params: T,
|
|
7
|
+
opts: { serviceName?: string; subscriptionName?: string },
|
|
8
|
+
) => {
|
|
9
|
+
return Object.fromEntries(
|
|
10
|
+
Object.entries(params).map(([k, v]) => [k, { ...v, name: k, ...opts }]),
|
|
11
|
+
)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const createSchema = <T extends TSchema>(
|
|
15
|
+
schema: Omit<T, 'static' | 'params'>,
|
|
16
|
+
) => schema as T
|
package/tsconfig.json
ADDED