@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.
Files changed (67) hide show
  1. package/LICENSE.md +7 -0
  2. package/README.md +9 -0
  3. package/dist/compiler.js +50 -0
  4. package/dist/compiler.js.map +1 -0
  5. package/dist/contract.js +24 -0
  6. package/dist/contract.js.map +1 -0
  7. package/dist/formats.js +127 -0
  8. package/dist/formats.js.map +1 -0
  9. package/dist/guards/blob.js +3 -0
  10. package/dist/guards/blob.js.map +1 -0
  11. package/dist/guards/event.js +3 -0
  12. package/dist/guards/event.js.map +1 -0
  13. package/dist/guards/native-enum.js +3 -0
  14. package/dist/guards/native-enum.js.map +1 -0
  15. package/dist/guards/nullable.js +2 -0
  16. package/dist/guards/nullable.js.map +1 -0
  17. package/dist/guards/procedure.js +3 -0
  18. package/dist/guards/procedure.js.map +1 -0
  19. package/dist/guards/service.js +3 -0
  20. package/dist/guards/service.js.map +1 -0
  21. package/dist/guards/subscription.js +3 -0
  22. package/dist/guards/subscription.js.map +1 -0
  23. package/dist/guards/union-enum.js +3 -0
  24. package/dist/guards/union-enum.js.map +1 -0
  25. package/dist/guards.js +20 -0
  26. package/dist/guards.js.map +1 -0
  27. package/dist/schemas/blob.js +26 -0
  28. package/dist/schemas/blob.js.map +1 -0
  29. package/dist/schemas/event.js +12 -0
  30. package/dist/schemas/event.js.map +1 -0
  31. package/dist/schemas/native-enum.js +14 -0
  32. package/dist/schemas/native-enum.js.map +1 -0
  33. package/dist/schemas/nullable.js +5 -0
  34. package/dist/schemas/nullable.js.map +1 -0
  35. package/dist/schemas/procedure.js +14 -0
  36. package/dist/schemas/procedure.js.map +1 -0
  37. package/dist/schemas/service.js +35 -0
  38. package/dist/schemas/service.js.map +1 -0
  39. package/dist/schemas/subscription.js +16 -0
  40. package/dist/schemas/subscription.js.map +1 -0
  41. package/dist/schemas/union-enum.js +13 -0
  42. package/dist/schemas/union-enum.js.map +1 -0
  43. package/dist/utils.js +11 -0
  44. package/dist/utils.js.map +1 -0
  45. package/package.json +41 -0
  46. package/src/compiler.ts +63 -0
  47. package/src/contract.ts +62 -0
  48. package/src/formats.ts +181 -0
  49. package/src/guards/blob.ts +5 -0
  50. package/src/guards/event.ts +6 -0
  51. package/src/guards/native-enum.ts +7 -0
  52. package/src/guards/nullable.ts +14 -0
  53. package/src/guards/procedure.ts +6 -0
  54. package/src/guards/service.ts +6 -0
  55. package/src/guards/subscription.ts +10 -0
  56. package/src/guards/union-enum.ts +7 -0
  57. package/src/guards.ts +21 -0
  58. package/src/schemas/blob.ts +58 -0
  59. package/src/schemas/event.ts +35 -0
  60. package/src/schemas/native-enum.ts +37 -0
  61. package/src/schemas/nullable.ts +4 -0
  62. package/src/schemas/procedure.ts +61 -0
  63. package/src/schemas/service.ts +126 -0
  64. package/src/schemas/subscription.ts +82 -0
  65. package/src/schemas/union-enum.ts +43 -0
  66. package/src/utils.ts +16 -0
  67. 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
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "../../tsconfig.json"
3
+ }