@nmtjs/contract 0.16.1 → 0.17.0-beta.2

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 CHANGED
@@ -4,4 +4,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
4
 
5
5
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
6
 
7
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,9 +1,49 @@
1
1
  # NeemataJS - RPC application server for real-time applications (proof of concept)
2
2
 
3
3
  ### Built with following in mind:
4
+
4
5
  - transport-agnostic (like WebSockets, WebTransport, .etc)
5
6
  - format-agnostic (like JSON, MessagePack, BSON, .etc)
6
7
  - binary data streaming and event subscriptions
7
8
  - contract-based API
8
9
  - end-to-end type safety
9
- - CPU-intensive task execution on separate workers
10
+ - CPU-intensive task execution on separate workers
11
+
12
+ ## Neem CLI draft
13
+
14
+ `neem build` compiles config, app entries, plugin entries, and plugin-declared
15
+ artifacts into `dist` by default. It writes an internal `neem.manifest.json`
16
+ with relative artifact paths.
17
+
18
+ `neem start` consumes an existing built output directory. It reads the manifest
19
+ for executable artifacts and serialized runtime config, registers built plugin
20
+ hooks, and starts app workers in production mode.
21
+
22
+ `neem dev` uses `.neem` by default as a build-like watched output directory. It
23
+ uses the same manifest shape as `start`, restarts app workers after successful
24
+ config/app rebuilds, reloads plugin hooks after plugin entry rebuilds, and keeps
25
+ existing workers alive on rebuild errors.
26
+
27
+ ## Service integration tests
28
+
29
+ Service-backed package integration tests live beside package owners under
30
+ `packages/*/tests/integration`.
31
+
32
+ Local services:
33
+
34
+ ```sh
35
+ docker compose up -d --wait redis valkey kafka
36
+ ```
37
+
38
+ Run required service tests:
39
+
40
+ ```sh
41
+ NMTJS_REQUIRE_SERVICE_TESTS=1 \
42
+ REDIS_URL=redis://localhost:6379 \
43
+ VALKEY_URL=redis://localhost:6380 \
44
+ KAFKA_BROKERS=localhost:9092 \
45
+ pnpm run test:integration:services
46
+ ```
47
+
48
+ Without service env, these tests skip in normal package/root test runs. In CI,
49
+ `NMTJS_REQUIRE_SERVICE_TESTS=1` makes missing service env fail instead of skip.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { SubscriptionContract } from './schemas/subscription.ts';
1
2
  export * from './schemas/event.ts';
2
3
  export * from './schemas/procedure.ts';
3
4
  export * from './schemas/router.ts';
@@ -11,29 +12,18 @@ export declare namespace contract {
11
12
  schemaOptions?: import("./utils.ts").ContractSchemaOptions;
12
13
  name?: string;
13
14
  }>(options: Options) => import("./schemas/procedure.ts").TProcedureContract<Options['input'] extends import("@nmtjs/type").BaseType ? Options['input'] : import("@nmtjs/type/never").NeverType, Options['output'] extends import("@nmtjs/type").BaseType ? Options['output'] : import("@nmtjs/type/never").NeverType, Options['stream'] extends true ? true : undefined, Options['name'] extends string ? Options['name'] : undefined>;
14
- const event: <Payload extends import("@nmtjs/type").BaseType, Name extends string | undefined = undefined, Options extends import("./schemas/subscription.ts").SubcriptionOptions | undefined = undefined>(options?: {
15
+ const event: <Payload extends import("@nmtjs/type").BaseType = import("@nmtjs/type/never").NeverType>(options?: {
15
16
  payload?: Payload;
16
17
  schemaOptions?: import("./utils.ts").ContractSchemaOptions;
17
- name?: Name;
18
- }) => import("./schemas/event.ts").TEventContract<Payload, Name, Options>;
19
- const subscription: (<const Options extends {
20
- events: Record<string, import("./schemas/event.ts").TAnyEventContract>;
21
- name?: string;
22
- schemaOptions?: import("./utils.ts").ContractSchemaOptions;
23
- }, SubOpt extends import("./schemas/subscription.ts").SubcriptionOptions = null>(options: Options) => import("./schemas/subscription.ts").TSubscriptionContract<SubOpt, Options["events"], Options["name"]>) & {
24
- withOptions: <Options extends import("./schemas/subscription.ts").SubcriptionOptions>() => <T extends {
25
- events: Record<string, import("./schemas/event.ts").TAnyEventContract>;
26
- name?: string;
27
- schemaOptions?: import("./utils.ts").ContractSchemaOptions;
28
- }>(options: T) => import("./schemas/subscription.ts").TSubscriptionContract<Options, T["events"], T["name"]>;
29
- };
18
+ }) => import("./schemas/event.ts").TEventContract<Payload>;
19
+ const subscription: typeof SubscriptionContract;
30
20
  const router: <const Options extends {
31
21
  routes: Record<string, import("./schemas/router.ts").TRouteContract>;
32
22
  name?: string;
33
23
  timeout?: number;
34
24
  schemaOptions?: import("./utils.ts").ContractSchemaOptions;
35
25
  }>(options: Options) => import("./schemas/router.ts").TRouterContract<Options["routes"], Options["name"] extends string ? Options["name"] : undefined>;
36
- const blob: (options?: import("./types/blob.ts").BlobOptions) => import("@nmtjs/type/custom").CustomType<import("@nmtjs/protocol").ProtocolBlobInterface>;
37
26
  }
27
+ export { BlobType as blobType } from './types/blob.ts';
38
28
  export { contract as c };
39
29
  export default contract;
package/dist/index.js CHANGED
@@ -2,7 +2,6 @@ import { EventContract } from './schemas/event.js';
2
2
  import { ProcedureContract } from './schemas/procedure.js';
3
3
  import { RouterContract } from './schemas/router.js';
4
4
  import { SubscriptionContract } from './schemas/subscription.js';
5
- import { BlobType } from './types/blob.js';
6
5
  export * from './schemas/event.js';
7
6
  export * from './schemas/procedure.js';
8
7
  export * from './schemas/router.js';
@@ -13,8 +12,8 @@ export var contract;
13
12
  contract.event = EventContract;
14
13
  contract.subscription = SubscriptionContract;
15
14
  contract.router = RouterContract;
16
- contract.blob = BlobType;
17
15
  })(contract || (contract = {}));
16
+ export { BlobType as blobType } from './types/blob.js';
18
17
  export { contract as c };
19
18
  export default contract;
20
19
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAE1C,cAAc,oBAAoB,CAAA;AAClC,cAAc,wBAAwB,CAAA;AACtC,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA;AAEzC,MAAM,KAAW,QAAQ,CAMxB;AAND,WAAiB,QAAQ;IACV,kBAAS,GAAG,iBAAiB,CAAA;IAC7B,cAAK,GAAG,aAAa,CAAA;IACrB,qBAAY,GAAG,oBAAoB,CAAA;IACnC,eAAM,GAAG,cAAc,CAAA;IACvB,aAAI,GAAG,QAAQ,CAAA;AAC9B,CAAC,EANgB,QAAQ,KAAR,QAAQ,QAMxB;AAED,OAAO,EAAE,QAAQ,IAAI,CAAC,EAAE,CAAA;AAExB,eAAe,QAAQ,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAEhE,cAAc,oBAAoB,CAAA;AAClC,cAAc,wBAAwB,CAAA;AACtC,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA;AAEzC,MAAM,KAAW,QAAQ,CAKxB;AALD,WAAiB,QAAQ;IACV,kBAAS,GAAG,iBAAiB,CAAA;IAC7B,cAAK,GAAG,aAAa,CAAA;IACrB,qBAAY,GAAG,oBAAoB,CAAA;IACnC,eAAM,GAAG,cAAc,CAAA;AACtC,CAAC,EALgB,QAAQ,KAAR,QAAQ,QAKxB;AAED,OAAO,EAAE,QAAQ,IAAI,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAEtD,OAAO,EAAE,QAAQ,IAAI,CAAC,EAAE,CAAA;AAExB,eAAe,QAAQ,CAAA"}
@@ -1,20 +1,16 @@
1
- import type { BaseType } from '@nmtjs/type';
1
+ import type { BaseType, BaseTypeAny } from '@nmtjs/type';
2
2
  import { t } from '@nmtjs/type';
3
3
  import type { ContractSchemaOptions } from '../utils.ts';
4
- import type { SubcriptionOptions } from './subscription.ts';
5
4
  import { Kind } from '../constants.ts';
6
5
  export declare const EventKind: unique symbol;
7
- export type TAnyEventContract = TEventContract<BaseType, string | undefined, SubcriptionOptions | undefined>;
8
- export interface TEventContract<Payload extends BaseType = t.NeverType, Name extends string | undefined = undefined, Options extends SubcriptionOptions | undefined = undefined> {
6
+ export type TAnyEventContract = TEventContract<BaseTypeAny>;
7
+ export interface TEventContract<Payload extends BaseType = t.NeverType> {
9
8
  readonly [Kind]: typeof EventKind;
10
9
  readonly type: 'neemata:event';
11
- readonly name: Name;
12
10
  readonly payload: Payload;
13
- readonly options: Options;
14
11
  }
15
- export declare const EventContract: <Payload extends BaseType, Name extends string | undefined = undefined, Options extends SubcriptionOptions | undefined = undefined>(options?: {
12
+ export declare const EventContract: <Payload extends BaseType = t.NeverType>(options?: {
16
13
  payload?: Payload;
17
14
  schemaOptions?: ContractSchemaOptions;
18
- name?: Name;
19
- }) => TEventContract<Payload, Name, Options>;
15
+ }) => TEventContract<Payload>;
20
16
  export declare function IsEventContract(value: any): value is TAnyEventContract;
@@ -3,14 +3,12 @@ import { Kind } from '../constants.js';
3
3
  import { createSchema } from '../utils.js';
4
4
  export const EventKind = Symbol('NeemataEvent');
5
5
  export const EventContract = (options) => {
6
- const { payload = t.never(), schemaOptions = {}, name = undefined, } = options ?? {};
6
+ const { payload = t.never(), schemaOptions = {} } = options ?? {};
7
7
  return createSchema({
8
8
  ...schemaOptions,
9
9
  [Kind]: EventKind,
10
10
  type: 'neemata:event',
11
11
  payload,
12
- name,
13
- options: undefined,
14
12
  });
15
13
  };
16
14
  export function IsEventContract(value) {
@@ -1 +1 @@
1
- {"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/schemas/event.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,aAAa,CAAA;AAI/B,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC,CAAA;AAoB/C,MAAM,CAAC,MAAM,aAAa,GAAG,CAI3B,OAID,EAAE,EAAE;IACH,MAAM,EACJ,OAAO,GAAG,CAAC,CAAC,KAAK,EAAwB,EACzC,aAAa,GAAG,EAAE,EAClB,IAAI,GAAG,SAAgB,GACxB,GAAG,OAAO,IAAI,EAAE,CAAA;IACjB,OAAO,YAAY,CAAyC;QAC1D,GAAG,aAAa;QAChB,CAAC,IAAI,CAAC,EAAE,SAAS;QACjB,IAAI,EAAE,eAAe;QACrB,OAAO;QACP,IAAI;QACJ,OAAO,EAAE,SAAoB;KAC9B,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,UAAU,eAAe,CAAC,KAAU;IACxC,OAAO,IAAI,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,SAAS,CAAA;AACnD,CAAC"}
1
+ {"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/schemas/event.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,aAAa,CAAA;AAG/B,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC,CAAA;AAU/C,MAAM,CAAC,MAAM,aAAa,GAAG,CAE3B,OAGD,EAAE,EAAE;IACH,MAAM,EAAE,OAAO,GAAG,CAAC,CAAC,KAAK,EAAwB,EAAE,aAAa,GAAG,EAAE,EAAE,GACrE,OAAO,IAAI,EAAE,CAAA;IACf,OAAO,YAAY,CAA0B;QAC3C,GAAG,aAAa;QAChB,CAAC,IAAI,CAAC,EAAE,SAAS;QACjB,IAAI,EAAE,eAAe;QACrB,OAAO;KACR,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,UAAU,eAAe,CAAC,KAAU;IACxC,OAAO,IAAI,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,SAAS,CAAA;AACnD,CAAC"}
@@ -1,27 +1,52 @@
1
+ import type { AnyCompatibleType, BaseType, BaseTypeAny, t } from '@nmtjs/type';
1
2
  import type { ContractSchemaOptions } from '../utils.ts';
2
3
  import type { TAnyEventContract, TEventContract } from './event.ts';
3
4
  import { Kind } from '../constants.ts';
4
5
  export declare const SubscriptionKind: unique symbol;
5
- export type SubcriptionOptions = Record<string, string | number | boolean> | null;
6
- export type TAnySubscriptionContract = TSubscriptionContract<SubcriptionOptions, Record<string, TAnyEventContract>, string | undefined>;
7
- export interface TSubscriptionContract<Options extends SubcriptionOptions = null, Events extends Record<string, unknown> = {}, Name extends string | undefined = undefined> {
6
+ export type SubscriptionParamsType = AnyCompatibleType<Record<string, string | number | boolean | null>>;
7
+ export type SubscriptionKey<Params extends BaseType> = Params extends t.NeverType ? undefined : (params: t.infer.decode.output<Params>) => string;
8
+ export type TAnySubscriptionContract = TSubscriptionContract<any, Record<string, TAnyEventContract>, string>;
9
+ export type TAnySubscriptionEventContract = TSubscriptionEventContract<BaseTypeAny, string, TAnySubscriptionContract>;
10
+ export type SubscriptionParams<Contract extends TAnySubscriptionContract> = t.infer.decode.output<Contract['params']>;
11
+ export type SubscriptionEventMessage<E extends TAnySubscriptionEventContract> = {
12
+ event: E['event'];
13
+ payload: t.infer.decode.output<E['payload']>;
14
+ };
15
+ export type SubscriptionPublishInput<E extends TAnySubscriptionEventContract> = t.infer.encode.input<E['payload']>;
16
+ export type SubscriptionEventUnion<Events extends Record<string, TAnySubscriptionEventContract>> = {
17
+ [K in keyof Events]: SubscriptionEventMessage<Events[K]>;
18
+ }[keyof Events];
19
+ export type SubscriptionSelectedEventUnion<Contract extends TAnySubscriptionContract, Events extends Partial<Record<keyof Contract['events'], true>>> = {} extends Events ? SubscriptionEventUnion<Contract['events']> : {
20
+ [K in keyof Events]: K extends keyof Contract['events'] ? SubscriptionEventMessage<Contract['events'][K]> : never;
21
+ }[keyof Events];
22
+ export interface TSubscriptionEventContract<Payload extends BaseType = BaseTypeAny, Event extends string = string, Subscription = TAnySubscriptionContract> extends TEventContract<Payload> {
23
+ readonly event: Event;
24
+ readonly subscription: Subscription;
25
+ }
26
+ export interface TSubscriptionContract<Params extends BaseType = t.NeverType, Events extends Record<string, unknown> = {}, Namespace extends string = string> {
8
27
  readonly [Kind]: typeof SubscriptionKind;
9
28
  readonly type: 'neemata:subscription';
10
- readonly name: Name;
11
- readonly options: Options;
29
+ readonly namespace: Namespace;
30
+ readonly params: Params;
31
+ readonly key: SubscriptionKey<Params>;
12
32
  readonly events: {
13
- [K in keyof Events]: Events[K] extends TAnyEventContract ? TEventContract<Events[K]['payload'], Name extends string ? `${Name}/${Extract<K, string>}` : Extract<K, string>, Options> : never;
33
+ [K in keyof Events]: Events[K] extends TAnyEventContract ? TSubscriptionEventContract<Events[K]['payload'], Extract<K, string>, TSubscriptionContract<Params, Events, Namespace>> : never;
14
34
  };
15
35
  }
16
- export declare const SubscriptionContract: (<const Options extends {
17
- events: Record<string, TAnyEventContract>;
18
- name?: string;
36
+ type SubscriptionContractBaseOptions<Namespace extends string, Events extends Record<string, TAnyEventContract>> = {
37
+ namespace: Namespace;
38
+ events: Events;
19
39
  schemaOptions?: ContractSchemaOptions;
20
- }, SubOpt extends SubcriptionOptions = null>(options: Options) => TSubscriptionContract<SubOpt, Options["events"], Options["name"]>) & {
21
- withOptions: <Options extends SubcriptionOptions>() => <T extends {
22
- events: Record<string, TAnyEventContract>;
23
- name?: string;
24
- schemaOptions?: ContractSchemaOptions;
25
- }>(options: T) => TSubscriptionContract<Options, T["events"], T["name"]>;
26
40
  };
41
+ type SubscriptionContractNoParamsOptions<Namespace extends string, Events extends Record<string, TAnyEventContract>> = SubscriptionContractBaseOptions<Namespace, Events> & {
42
+ params?: undefined;
43
+ key?: undefined;
44
+ };
45
+ type SubscriptionContractWithParamsOptions<Namespace extends string, Params extends SubscriptionParamsType, Events extends Record<string, TAnyEventContract>> = SubscriptionContractBaseOptions<Namespace, Events> & {
46
+ params: Params;
47
+ key: (params: t.infer.decode.output<Params>) => string;
48
+ };
49
+ export declare function SubscriptionContract<const Namespace extends string, const Events extends Record<string, TAnyEventContract>>(options: SubscriptionContractNoParamsOptions<Namespace, Events>): TSubscriptionContract<t.NeverType, Events, Namespace>;
50
+ export declare function SubscriptionContract<const Namespace extends string, const Params extends SubscriptionParamsType, const Events extends Record<string, TAnyEventContract>>(options: SubscriptionContractWithParamsOptions<Namespace, Params, Events>): TSubscriptionContract<Params, Events, Namespace>;
27
51
  export declare function IsSubscriptionContract(contract: any): contract is TAnySubscriptionContract;
52
+ export {};
@@ -1,28 +1,30 @@
1
+ import { t as types } from '@nmtjs/type';
1
2
  import { Kind } from '../constants.js';
2
- import { concatFullName, createSchema } from '../utils.js';
3
+ import { createSchema } from '../utils.js';
3
4
  export const SubscriptionKind = Symbol('NeemataSubscription');
4
- const _SubscriptionContract = (options) => {
5
- const { schemaOptions = {}, name } = options;
6
- const _events = {};
7
- for (const key in options.events) {
8
- const event = options.events[key];
9
- const fullName = concatFullName(name, key);
10
- _events[key] = createSchema({ ...event, name: fullName });
11
- }
12
- return createSchema({
5
+ export function SubscriptionContract(options) {
6
+ const { schemaOptions = {} } = options;
7
+ const params = options.params ?? types.never();
8
+ const events = {};
9
+ const subscription = createSchema({
13
10
  ...schemaOptions,
14
11
  [Kind]: SubscriptionKind,
15
12
  type: 'neemata:subscription',
16
- events: _events,
17
- name: name,
18
- options: undefined,
13
+ namespace: options.namespace,
14
+ params,
15
+ key: options.key,
16
+ events,
19
17
  });
20
- };
21
- export const SubscriptionContract = Object.assign(_SubscriptionContract, {
22
- withOptions: () => {
23
- return (options) => _SubscriptionContract(options);
24
- },
25
- });
18
+ for (const eventName in options.events) {
19
+ const event = options.events[eventName];
20
+ events[eventName] = createSchema({
21
+ ...event,
22
+ event: eventName,
23
+ subscription,
24
+ });
25
+ }
26
+ return subscription;
27
+ }
26
28
  export function IsSubscriptionContract(contract) {
27
29
  return Kind in contract && contract[Kind] === SubscriptionKind;
28
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"subscription.js","sourceRoot":"","sources":["../../src/schemas/subscription.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AACtC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1D,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAA;AAmC7D,MAAM,qBAAqB,GAAG,CAQ5B,OAAgB,EAChB,EAAE;IACF,MAAM,EAAE,aAAa,GAAG,EAAE,EAAE,IAAI,EAAE,GAAG,OAAO,CAAA;IAC5C,MAAM,OAAO,GAAG,EAAS,CAAA;IACzB,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACjC,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;QAC1C,OAAO,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC3D,CAAC;IACD,OAAO,YAAY,CAEjB;QACA,GAAG,aAAa;QAChB,CAAC,IAAI,CAAC,EAAE,gBAAgB;QACxB,IAAI,EAAE,sBAAsB;QAC5B,MAAM,EAAE,OAAO;QACf,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,SAA8B;KACxC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC,qBAAqB,EAAE;IACvE,WAAW,EAAE,GAAuC,EAAE;QACpD,OAAO,CAOL,OAAU,EACV,EAAE,CAAC,qBAAqB,CAAa,OAAO,CAAC,CAAA;IACjD,CAAC;CACF,CAAC,CAAA;AAEF,MAAM,UAAU,sBAAsB,CACpC,QAAa;IAEb,OAAO,IAAI,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,gBAAgB,CAAA;AAChE,CAAC"}
1
+ {"version":3,"file":"subscription.js","sourceRoot":"","sources":["../../src/schemas/subscription.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,IAAI,KAAK,EAAE,MAAM,aAAa,CAAA;AAIxC,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAA;AAsH7D,MAAM,UAAU,oBAAoB,CAAC,OAMpC;IACC,MAAM,EAAE,aAAa,GAAG,EAAE,EAAE,GAAG,OAAO,CAAA;IACtC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,EAAE,CAAA;IAC9C,MAAM,MAAM,GAAG,EAAmD,CAAA;IAClE,MAAM,YAAY,GAAG,YAAY,CAAM;QACrC,GAAG,aAAa;QAChB,CAAC,IAAI,CAAC,EAAE,gBAAgB;QACxB,IAAI,EAAE,sBAAsB;QAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,MAAM;QACN,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,MAAM;KACP,CAAC,CAAA;IAEF,KAAK,MAAM,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QACvC,MAAM,CAAC,SAAS,CAAC,GAAG,YAAY,CAAgC;YAC9D,GAAG,KAAK;YACR,KAAK,EAAE,SAAS;YAChB,YAAY;SACb,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,YAAY,CAAA;AACrB,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,QAAa;IAEb,OAAO,IAAI,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,gBAAgB,CAAA;AAChE,CAAC"}
@@ -4,12 +4,12 @@ export const BlobType = (options = {}) => CustomType.factory({
4
4
  decode: (value) => value,
5
5
  encode: (value) => value,
6
6
  validation: {
7
- decode(value, { addIssue }) {
7
+ decode(value, payload) {
8
8
  if (isBlobInterface(value)) {
9
9
  if (options.maxSize) {
10
10
  const size = value.metadata.size;
11
11
  if (typeof size !== 'undefined' && size > options.maxSize) {
12
- addIssue({
12
+ payload.addIssue({
13
13
  code: 'custom',
14
14
  message: `Blob size unknown or exceeds maximum allowed size of ${options.maxSize} bytes`,
15
15
  });
@@ -17,15 +17,15 @@ export const BlobType = (options = {}) => CustomType.factory({
17
17
  }
18
18
  }
19
19
  else {
20
- addIssue({
20
+ payload.addIssue({
21
21
  code: 'custom',
22
22
  message: 'Value is not a Neemata Blob. Make sure to use transport that supports encoded streams.',
23
23
  });
24
24
  }
25
25
  },
26
- encode(value, { addIssue }) {
26
+ encode(value, payload) {
27
27
  if (!isBlobInterface(value)) {
28
- addIssue({
28
+ payload.addIssue({
29
29
  code: 'custom',
30
30
  message: 'Value is not a Neemata Blob. Make sure to use transport that supports encoded streams.',
31
31
  });
@@ -1 +1 @@
1
- {"version":3,"file":"blob.js","sourceRoot":"","sources":["../../src/types/blob.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAO/C,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,OAAO,GAAgB,EAAE,EACU,EAAE,CACrC,UAAU,CAAC,OAAO,CAAC;IACjB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;IACxB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;IACxB,UAAU,EAAE;QACV,MAAM,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE;YACxB,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3B,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;oBACpB,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAA;oBAChC,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;wBAC1D,QAAQ,CAAC;4BACP,IAAI,EAAE,QAAQ;4BACd,OAAO,EAAE,wDAAwD,OAAO,CAAC,OAAO,QAAQ;yBACzF,CAAC,CAAA;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC;oBACP,IAAI,EAAE,QAAQ;oBACd,OAAO,EACL,wFAAwF;iBAC3F,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QACD,MAAM,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE;YACxB,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC5B,QAAQ,CAAC;oBACP,IAAI,EAAE,QAAQ;oBACd,OAAO,EACL,wFAAwF;iBAC3F,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;KACF;CACF,CAAC,CAAA"}
1
+ {"version":3,"file":"blob.js","sourceRoot":"","sources":["../../src/types/blob.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAO/C,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,OAAO,GAAgB,EAAE,EACU,EAAE,CACrC,UAAU,CAAC,OAAO,CAAC;IACjB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;IACxB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;IACxB,UAAU,EAAE;QACV,MAAM,CAAC,KAAK,EAAE,OAAO;YACnB,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3B,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;oBACpB,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAA;oBAChC,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;wBAC1D,OAAO,CAAC,QAAQ,CAAC;4BACf,IAAI,EAAE,QAAQ;4BACd,OAAO,EAAE,wDAAwD,OAAO,CAAC,OAAO,QAAQ;yBACzF,CAAC,CAAA;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,QAAQ,CAAC;oBACf,IAAI,EAAE,QAAQ;oBACd,OAAO,EACL,wFAAwF;iBAC3F,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QACD,MAAM,CAAC,KAAK,EAAE,OAAO;YACnB,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC5B,OAAO,CAAC,QAAQ,CAAC;oBACf,IAAI,EAAE,QAAQ;oBACd,OAAO,EACL,wFAAwF;iBAC3F,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;KACF;CACF,CAAC,CAAA"}
package/package.json CHANGED
@@ -10,8 +10,8 @@
10
10
  }
11
11
  },
12
12
  "dependencies": {
13
- "@nmtjs/protocol": "0.16.1",
14
- "@nmtjs/type": "0.16.1"
13
+ "@nmtjs/protocol": "0.17.0-beta.2",
14
+ "@nmtjs/type": "0.17.0-beta.2"
15
15
  },
16
16
  "devDependencies": {
17
17
  "zod": "^4.0.0"
@@ -22,12 +22,9 @@
22
22
  "LICENSE.md",
23
23
  "README.md"
24
24
  ],
25
- "version": "0.16.1",
25
+ "version": "0.17.0-beta.2",
26
26
  "repository": {
27
27
  "type": "git",
28
28
  "url": "https://github.com/neematajs/neemata"
29
- },
30
- "scripts": {
31
- "clean-build": "rm -rf ./dist"
32
29
  }
33
30
  }
package/src/index.ts CHANGED
@@ -2,7 +2,6 @@ import { EventContract } from './schemas/event.ts'
2
2
  import { ProcedureContract } from './schemas/procedure.ts'
3
3
  import { RouterContract } from './schemas/router.ts'
4
4
  import { SubscriptionContract } from './schemas/subscription.ts'
5
- import { BlobType } from './types/blob.ts'
6
5
 
7
6
  export * from './schemas/event.ts'
8
7
  export * from './schemas/procedure.ts'
@@ -14,9 +13,10 @@ export namespace contract {
14
13
  export const event = EventContract
15
14
  export const subscription = SubscriptionContract
16
15
  export const router = RouterContract
17
- export const blob = BlobType
18
16
  }
19
17
 
18
+ export { BlobType as blobType } from './types/blob.ts'
19
+
20
20
  export { contract as c }
21
21
 
22
22
  export default contract
@@ -1,52 +1,33 @@
1
- import type { BaseType } from '@nmtjs/type'
1
+ import type { BaseType, BaseTypeAny } from '@nmtjs/type'
2
2
  import { t } from '@nmtjs/type'
3
3
 
4
4
  import type { ContractSchemaOptions } from '../utils.ts'
5
- import type { SubcriptionOptions } from './subscription.ts'
6
5
  import { Kind } from '../constants.ts'
7
6
  import { createSchema } from '../utils.ts'
8
7
 
9
8
  export const EventKind = Symbol('NeemataEvent')
10
9
 
11
- export type TAnyEventContract = TEventContract<
12
- BaseType,
13
- string | undefined,
14
- SubcriptionOptions | undefined
15
- >
10
+ export type TAnyEventContract = TEventContract<BaseTypeAny>
16
11
 
17
- export interface TEventContract<
18
- Payload extends BaseType = t.NeverType,
19
- Name extends string | undefined = undefined,
20
- Options extends SubcriptionOptions | undefined = undefined,
21
- > {
12
+ export interface TEventContract<Payload extends BaseType = t.NeverType> {
22
13
  readonly [Kind]: typeof EventKind
23
14
  readonly type: 'neemata:event'
24
- readonly name: Name
25
15
  readonly payload: Payload
26
- readonly options: Options
27
16
  }
28
17
 
29
18
  export const EventContract = <
30
- Payload extends BaseType,
31
- Name extends string | undefined = undefined,
32
- Options extends SubcriptionOptions | undefined = undefined,
19
+ Payload extends BaseType = t.NeverType,
33
20
  >(options?: {
34
21
  payload?: Payload
35
22
  schemaOptions?: ContractSchemaOptions
36
- name?: Name
37
23
  }) => {
38
- const {
39
- payload = t.never() as unknown as Payload,
40
- schemaOptions = {},
41
- name = undefined as any,
42
- } = options ?? {}
43
- return createSchema<TEventContract<Payload, Name, Options>>({
24
+ const { payload = t.never() as unknown as Payload, schemaOptions = {} } =
25
+ options ?? {}
26
+ return createSchema<TEventContract<Payload>>({
44
27
  ...schemaOptions,
45
28
  [Kind]: EventKind,
46
29
  type: 'neemata:event',
47
30
  payload,
48
- name,
49
- options: undefined as Options,
50
31
  })
51
32
  }
52
33
 
@@ -1,85 +1,160 @@
1
+ import type { AnyCompatibleType, BaseType, BaseTypeAny, t } from '@nmtjs/type'
2
+ import { t as types } from '@nmtjs/type'
3
+
1
4
  import type { ContractSchemaOptions } from '../utils.ts'
2
5
  import type { TAnyEventContract, TEventContract } from './event.ts'
3
6
  import { Kind } from '../constants.ts'
4
- import { concatFullName, createSchema } from '../utils.ts'
7
+ import { createSchema } from '../utils.ts'
5
8
 
6
9
  export const SubscriptionKind = Symbol('NeemataSubscription')
7
10
 
8
- export type SubcriptionOptions = Record<
9
- string,
10
- string | number | boolean
11
- > | null
11
+ export type SubscriptionParamsType = AnyCompatibleType<
12
+ Record<string, string | number | boolean | null>
13
+ >
14
+
15
+ export type SubscriptionKey<Params extends BaseType> =
16
+ Params extends t.NeverType
17
+ ? undefined
18
+ : (params: t.infer.decode.output<Params>) => string
12
19
 
13
20
  export type TAnySubscriptionContract = TSubscriptionContract<
14
- SubcriptionOptions,
21
+ any,
15
22
  Record<string, TAnyEventContract>,
16
- string | undefined
23
+ string
24
+ >
25
+
26
+ export type TAnySubscriptionEventContract = TSubscriptionEventContract<
27
+ BaseTypeAny,
28
+ string,
29
+ TAnySubscriptionContract
17
30
  >
18
31
 
32
+ export type SubscriptionParams<Contract extends TAnySubscriptionContract> =
33
+ t.infer.decode.output<Contract['params']>
34
+
35
+ export type SubscriptionEventMessage<E extends TAnySubscriptionEventContract> =
36
+ { event: E['event']; payload: t.infer.decode.output<E['payload']> }
37
+
38
+ export type SubscriptionPublishInput<E extends TAnySubscriptionEventContract> =
39
+ t.infer.encode.input<E['payload']>
40
+
41
+ export type SubscriptionEventUnion<
42
+ Events extends Record<string, TAnySubscriptionEventContract>,
43
+ > = {
44
+ [K in keyof Events]: SubscriptionEventMessage<Events[K]>
45
+ }[keyof Events]
46
+
47
+ export type SubscriptionSelectedEventUnion<
48
+ Contract extends TAnySubscriptionContract,
49
+ Events extends Partial<Record<keyof Contract['events'], true>>,
50
+ > = {} extends Events
51
+ ? SubscriptionEventUnion<Contract['events']>
52
+ : {
53
+ [K in keyof Events]: K extends keyof Contract['events']
54
+ ? SubscriptionEventMessage<Contract['events'][K]>
55
+ : never
56
+ }[keyof Events]
57
+
58
+ export interface TSubscriptionEventContract<
59
+ Payload extends BaseType = BaseTypeAny,
60
+ Event extends string = string,
61
+ Subscription = TAnySubscriptionContract,
62
+ > extends TEventContract<Payload> {
63
+ readonly event: Event
64
+ readonly subscription: Subscription
65
+ }
66
+
19
67
  export interface TSubscriptionContract<
20
- Options extends SubcriptionOptions = null,
68
+ Params extends BaseType = t.NeverType,
21
69
  Events extends Record<string, unknown> = {},
22
- Name extends string | undefined = undefined,
70
+ Namespace extends string = string,
23
71
  > {
24
72
  readonly [Kind]: typeof SubscriptionKind
25
73
  readonly type: 'neemata:subscription'
26
- readonly name: Name
27
- readonly options: Options
74
+ readonly namespace: Namespace
75
+ readonly params: Params
76
+ readonly key: SubscriptionKey<Params>
28
77
  readonly events: {
29
78
  [K in keyof Events]: Events[K] extends TAnyEventContract
30
- ? TEventContract<
79
+ ? TSubscriptionEventContract<
31
80
  Events[K]['payload'],
32
- Name extends string
33
- ? `${Name}/${Extract<K, string>}`
34
- : Extract<K, string>,
35
- Options
81
+ Extract<K, string>,
82
+ TSubscriptionContract<Params, Events, Namespace>
36
83
  >
37
84
  : never
38
85
  }
39
86
  }
40
87
 
41
- const _SubscriptionContract = <
42
- const Options extends {
43
- events: Record<string, TAnyEventContract>
44
- name?: string
45
- schemaOptions?: ContractSchemaOptions
46
- },
47
- SubOpt extends SubcriptionOptions = null,
88
+ type SubscriptionContractBaseOptions<
89
+ Namespace extends string,
90
+ Events extends Record<string, TAnyEventContract>,
91
+ > = {
92
+ namespace: Namespace
93
+ events: Events
94
+ schemaOptions?: ContractSchemaOptions
95
+ }
96
+
97
+ type SubscriptionContractNoParamsOptions<
98
+ Namespace extends string,
99
+ Events extends Record<string, TAnyEventContract>,
100
+ > = SubscriptionContractBaseOptions<Namespace, Events> & {
101
+ params?: undefined
102
+ key?: undefined
103
+ }
104
+
105
+ type SubscriptionContractWithParamsOptions<
106
+ Namespace extends string,
107
+ Params extends SubscriptionParamsType,
108
+ Events extends Record<string, TAnyEventContract>,
109
+ > = SubscriptionContractBaseOptions<Namespace, Events> & {
110
+ params: Params
111
+ key: (params: t.infer.decode.output<Params>) => string
112
+ }
113
+
114
+ export function SubscriptionContract<
115
+ const Namespace extends string,
116
+ const Events extends Record<string, TAnyEventContract>,
48
117
  >(
49
- options: Options,
50
- ) => {
51
- const { schemaOptions = {}, name } = options
52
- const _events = {} as any
53
- for (const key in options.events) {
54
- const event = options.events[key]
55
- const fullName = concatFullName(name, key)
56
- _events[key] = createSchema({ ...event, name: fullName })
57
- }
58
- return createSchema<
59
- TSubscriptionContract<SubOpt, Options['events'], Options['name']>
60
- >({
118
+ options: SubscriptionContractNoParamsOptions<Namespace, Events>,
119
+ ): TSubscriptionContract<t.NeverType, Events, Namespace>
120
+ export function SubscriptionContract<
121
+ const Namespace extends string,
122
+ const Params extends SubscriptionParamsType,
123
+ const Events extends Record<string, TAnyEventContract>,
124
+ >(
125
+ options: SubscriptionContractWithParamsOptions<Namespace, Params, Events>,
126
+ ): TSubscriptionContract<Params, Events, Namespace>
127
+ export function SubscriptionContract(options: {
128
+ namespace: string
129
+ events: Record<string, TAnyEventContract>
130
+ params?: BaseType
131
+ key?: (params: any) => string
132
+ schemaOptions?: ContractSchemaOptions
133
+ }) {
134
+ const { schemaOptions = {} } = options
135
+ const params = options.params ?? types.never()
136
+ const events = {} as Record<string, TAnySubscriptionEventContract>
137
+ const subscription = createSchema<any>({
61
138
  ...schemaOptions,
62
139
  [Kind]: SubscriptionKind,
63
140
  type: 'neemata:subscription',
64
- events: _events,
65
- name: name,
66
- options: undefined as unknown as SubOpt,
141
+ namespace: options.namespace,
142
+ params,
143
+ key: options.key,
144
+ events,
67
145
  })
68
- }
69
146
 
70
- export const SubscriptionContract = Object.assign(_SubscriptionContract, {
71
- withOptions: <Options extends SubcriptionOptions>() => {
72
- return <
73
- T extends {
74
- events: Record<string, TAnyEventContract>
75
- name?: string
76
- schemaOptions?: ContractSchemaOptions
77
- },
78
- >(
79
- options: T,
80
- ) => _SubscriptionContract<T, Options>(options)
81
- },
82
- })
147
+ for (const eventName in options.events) {
148
+ const event = options.events[eventName]
149
+ events[eventName] = createSchema<TAnySubscriptionEventContract>({
150
+ ...event,
151
+ event: eventName,
152
+ subscription,
153
+ })
154
+ }
155
+
156
+ return subscription
157
+ }
83
158
 
84
159
  export function IsSubscriptionContract(
85
160
  contract: any,
package/src/types/blob.ts CHANGED
@@ -14,28 +14,28 @@ export const BlobType = (
14
14
  decode: (value) => value,
15
15
  encode: (value) => value,
16
16
  validation: {
17
- decode(value, { addIssue }) {
17
+ decode(value, payload) {
18
18
  if (isBlobInterface(value)) {
19
19
  if (options.maxSize) {
20
20
  const size = value.metadata.size
21
21
  if (typeof size !== 'undefined' && size > options.maxSize) {
22
- addIssue({
22
+ payload.addIssue({
23
23
  code: 'custom',
24
24
  message: `Blob size unknown or exceeds maximum allowed size of ${options.maxSize} bytes`,
25
25
  })
26
26
  }
27
27
  }
28
28
  } else {
29
- addIssue({
29
+ payload.addIssue({
30
30
  code: 'custom',
31
31
  message:
32
32
  'Value is not a Neemata Blob. Make sure to use transport that supports encoded streams.',
33
33
  })
34
34
  }
35
35
  },
36
- encode(value, { addIssue }) {
36
+ encode(value, payload) {
37
37
  if (!isBlobInterface(value)) {
38
- addIssue({
38
+ payload.addIssue({
39
39
  code: 'custom',
40
40
  message:
41
41
  'Value is not a Neemata Blob. Make sure to use transport that supports encoded streams.',