@reactionary/core 0.10.26 → 0.10.28

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.
@@ -1,5 +1,8 @@
1
1
  import {} from "../schemas/session.schema.js";
2
2
  import { hasher } from "node-object-hash";
3
+ import {
4
+ REACTIONARY_ENTRYPOINT_METADATA
5
+ } from "../decorators/reactionary.metadata.js";
3
6
  class BaseCapability {
4
7
  cache;
5
8
  context;
@@ -7,6 +10,9 @@ class BaseCapability {
7
10
  this.cache = cache;
8
11
  this.context = context;
9
12
  }
13
+ getReactionaryResourceName() {
14
+ return this.getResourceName();
15
+ }
10
16
  generateDependencyIdsForModel(model) {
11
17
  const identifier = model?.identifier;
12
18
  if (!identifier) {
@@ -22,6 +28,35 @@ class BaseCapability {
22
28
  return `${scope}:${queryHash}:${locale}:${currency}`;
23
29
  }
24
30
  }
31
+ function getReactionaryEntrypoints(capability) {
32
+ const entrypoints = [];
33
+ const seenMethodNames = /* @__PURE__ */ new Set();
34
+ let prototype = Object.getPrototypeOf(capability);
35
+ while (prototype && prototype !== BaseCapability.prototype) {
36
+ for (const propertyName of Object.getOwnPropertyNames(prototype)) {
37
+ if (propertyName === "constructor" || seenMethodNames.has(propertyName)) {
38
+ continue;
39
+ }
40
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, propertyName);
41
+ if (typeof descriptor?.value !== "function") {
42
+ continue;
43
+ }
44
+ seenMethodNames.add(propertyName);
45
+ const method = descriptor.value;
46
+ const metadata = method[REACTIONARY_ENTRYPOINT_METADATA];
47
+ if (!metadata) {
48
+ continue;
49
+ }
50
+ entrypoints.push({
51
+ ...metadata,
52
+ capabilityName: capability.getReactionaryResourceName()
53
+ });
54
+ }
55
+ prototype = Object.getPrototypeOf(prototype);
56
+ }
57
+ return entrypoints;
58
+ }
25
59
  export {
26
- BaseCapability
60
+ BaseCapability,
61
+ getReactionaryEntrypoints
27
62
  };
@@ -1 +1,2 @@
1
1
  export * from "./reactionary.decorator.js";
2
+ export * from "./reactionary.metadata.js";
@@ -1,5 +1,8 @@
1
1
  import { SpanStatusCode, trace } from "@opentelemetry/api";
2
2
  import * as z from "zod";
3
+ import {
4
+ REACTIONARY_ENTRYPOINT_METADATA
5
+ } from "./reactionary.metadata.js";
3
6
  import { getReactionaryMeter } from "../metrics/metrics.js";
4
7
  import { error, success } from "../schemas/result.js";
5
8
  const TRACER_NAME = "@reactionary";
@@ -37,6 +40,14 @@ class ReactionaryDecoratorOptions {
37
40
  * The schema for the primary output type, for validation purposes
38
41
  */
39
42
  outputSchema = z.unknown();
43
+ /**
44
+ * Optional human-readable title for external surfaces such as MCP tools.
45
+ */
46
+ title = void 0;
47
+ /**
48
+ * Optional human-readable description for external surfaces such as MCP tools.
49
+ */
50
+ description = void 0;
40
51
  }
41
52
  function Reactionary(options) {
42
53
  return function(target, propertyKey, descriptor) {
@@ -59,18 +70,26 @@ function Reactionary(options) {
59
70
  return traceSpan(scope, async () => {
60
71
  meter.requestInProgress.add(1, attributes);
61
72
  try {
62
- const input = validateInput(args[0], configuration.inputSchema);
73
+ const input = validateInput(
74
+ args[0],
75
+ configuration.inputSchema
76
+ );
63
77
  if (!input.success) {
64
78
  return input;
65
79
  }
66
- const localeCacheKey = options.localeDependentCaching ? this.context.languageContext?.locale ?? "all" : "all";
67
- const currencyCacheKey = options.currencyDependentCaching ? this.context.languageContext?.currencyCode ?? "all" : "all";
68
- const cacheKey = this.generateCacheKeyForQuery(scope, input.value, localeCacheKey, currencyCacheKey);
80
+ const localeCacheKey = configuration.localeDependentCaching ? this.context.languageContext?.locale ?? "all" : "all";
81
+ const currencyCacheKey = configuration.currencyDependentCaching ? this.context.languageContext?.currencyCode ?? "all" : "all";
82
+ const cacheKey = this.generateCacheKeyForQuery(
83
+ scope,
84
+ input.value,
85
+ localeCacheKey,
86
+ currencyCacheKey
87
+ );
69
88
  let fromCache = null;
70
- if (options.cache) {
89
+ if (configuration.cache) {
71
90
  fromCache = await this.cache.get(
72
91
  cacheKey,
73
- options.outputSchema
92
+ configuration.outputSchema
74
93
  );
75
94
  }
76
95
  let result;
@@ -79,7 +98,7 @@ function Reactionary(options) {
79
98
  const r = result;
80
99
  if (r.success) {
81
100
  const dependencyIds = this.generateDependencyIdsForModel(r.value);
82
- if (options.cache) {
101
+ if (configuration.cache) {
83
102
  this.cache.put(cacheKey, r.value, {
84
103
  ttlSeconds: configuration.cacheTimeToLiveInSeconds,
85
104
  dependencyIds
@@ -123,6 +142,17 @@ function Reactionary(options) {
123
142
  }
124
143
  });
125
144
  };
145
+ descriptor.value[REACTIONARY_ENTRYPOINT_METADATA] = {
146
+ methodName: propertyKey.toString(),
147
+ inputSchema: configuration.inputSchema,
148
+ outputSchema: configuration.outputSchema,
149
+ title: configuration.title,
150
+ description: configuration.description,
151
+ cache: configuration.cache,
152
+ localeDependentCaching: configuration.localeDependentCaching,
153
+ currencyDependentCaching: configuration.currencyDependentCaching,
154
+ cacheTimeToLiveInSeconds: configuration.cacheTimeToLiveInSeconds
155
+ };
126
156
  return descriptor;
127
157
  };
128
158
  }
@@ -0,0 +1,6 @@
1
+ const REACTIONARY_ENTRYPOINT_METADATA = /* @__PURE__ */ Symbol.for(
2
+ "@reactionary/entrypoint-metadata"
3
+ );
4
+ export {
5
+ REACTIONARY_ENTRYPOINT_METADATA
6
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactionary/core",
3
- "version": "0.10.26",
3
+ "version": "0.10.28",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./src/index.d.ts",
@@ -1,18 +1,22 @@
1
1
  import * as z from "zod";
2
2
  import { MonetaryAmountSchema } from "./price.model.js";
3
+ const EmptyMonetaryAmount = {
4
+ value: 0,
5
+ currency: "EUR"
6
+ };
3
7
  const CostBreakDownSchema = z.looseObject({
4
- totalTax: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe("The amount of tax paid on the cart. This may include VAT, GST, sales tax, etc."),
5
- totalDiscount: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe("The amount of discount applied to the cart."),
6
- totalSurcharge: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe("The amount of surcharge applied to the cart."),
7
- totalShipping: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe("The amount of shipping fees for the cart."),
8
- totalProductPrice: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe("The total price of products in the cart."),
9
- grandTotal: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe("The total price for the cart including all taxes, discounts, and shipping.")
8
+ totalTax: MonetaryAmountSchema.default(EmptyMonetaryAmount).describe("The amount of tax paid on the cart. This may include VAT, GST, sales tax, etc."),
9
+ totalDiscount: MonetaryAmountSchema.default(EmptyMonetaryAmount).describe("The amount of discount applied to the cart."),
10
+ totalSurcharge: MonetaryAmountSchema.default(EmptyMonetaryAmount).describe("The amount of surcharge applied to the cart."),
11
+ totalShipping: MonetaryAmountSchema.default(EmptyMonetaryAmount).describe("The amount of shipping fees for the cart."),
12
+ totalProductPrice: MonetaryAmountSchema.default(EmptyMonetaryAmount).describe("The total price of products in the cart."),
13
+ grandTotal: MonetaryAmountSchema.default(EmptyMonetaryAmount).describe("The total price for the cart including all taxes, discounts, and shipping.")
10
14
  });
11
15
  const ItemCostBreakdownSchema = z.looseObject({
12
- unitPrice: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe("The price per single unit of the item."),
13
- unitDiscount: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe("The discount applied per single unit of the item."),
14
- totalPrice: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe("The total price for all units of the item."),
15
- totalDiscount: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe("The total discount applied to all units of the item.")
16
+ unitPrice: MonetaryAmountSchema.default(EmptyMonetaryAmount).describe("The price per single unit of the item."),
17
+ unitDiscount: MonetaryAmountSchema.default(EmptyMonetaryAmount).describe("The discount applied per single unit of the item."),
18
+ totalPrice: MonetaryAmountSchema.default(EmptyMonetaryAmount).describe("The total price for all units of the item."),
19
+ totalDiscount: MonetaryAmountSchema.default(EmptyMonetaryAmount).describe("The total discount applied to all units of the item.")
16
20
  });
17
21
  export {
18
22
  CostBreakDownSchema,
@@ -5,7 +5,8 @@ import { IdentitySchema } from "./models/identity.model.js";
5
5
  import { PersonalizationProfileSchema } from "./models/personalization-profile.model.js";
6
6
  const LanguageContextSchema = z.looseObject({
7
7
  locale: z.string().default("en-US").meta({ description: "The locale for the current request, in IETF BCP 47 format (e.g. en-US, fr-FR). This can be used for localization and internationalization purposes." }),
8
- currencyCode: CurrencySchema.default(() => CurrencySchema.parse({}))
8
+ // TODO: Replace this hardcoded fallback with a configured project/store currency.
9
+ currencyCode: CurrencySchema.default("EUR")
9
10
  });
10
11
  const IdentityContextSchema = z.looseObject({
11
12
  identity: IdentitySchema,
@@ -1,5 +1,7 @@
1
1
  import type { Cache } from '../cache/cache.interface.js';
2
2
  import { type RequestContext } from '../schemas/session.schema.js';
3
+ import { type ReactionaryEntrypointMetadata } from '../decorators/reactionary.metadata.js';
4
+ export type { ReactionaryEntrypointMetadata, ReactionaryEntrypointMethod, } from '../decorators/reactionary.metadata.js';
3
5
  /**
4
6
  * Base capability abstraction, responsible for mutations (changes) and queries (fetches)
5
7
  * for a given business object domain.
@@ -8,6 +10,7 @@ export declare abstract class BaseCapability {
8
10
  protected cache: Cache;
9
11
  protected context: RequestContext;
10
12
  constructor(cache: Cache, context: RequestContext);
13
+ getReactionaryResourceName(): string;
11
14
  generateDependencyIdsForModel(model: unknown): Array<string>;
12
15
  protected generateCacheKeyForQuery(scope: string, query: object, locale: string, currency: string): string;
13
16
  /**
@@ -15,3 +18,4 @@ export declare abstract class BaseCapability {
15
18
  */
16
19
  protected abstract getResourceName(): string;
17
20
  }
21
+ export declare function getReactionaryEntrypoints(capability: BaseCapability): ReactionaryEntrypointMetadata[];
@@ -1 +1,2 @@
1
1
  export * from './reactionary.decorator.js';
2
+ export * from './reactionary.metadata.js';
@@ -1,6 +1,6 @@
1
1
  import type { Tracer } from '@opentelemetry/api';
2
2
  import * as z from 'zod';
3
- import type { BaseCapability } from '../capabilities/index.js';
3
+ import type { BaseCapability } from '../capabilities/base.capability.js';
4
4
  import { type Result } from '../schemas/result.js';
5
5
  export declare function getTracer(): Tracer;
6
6
  /**
@@ -37,6 +37,14 @@ export declare class ReactionaryDecoratorOptions {
37
37
  * The schema for the primary output type, for validation purposes
38
38
  */
39
39
  outputSchema: z.ZodType;
40
+ /**
41
+ * Optional human-readable title for external surfaces such as MCP tools.
42
+ */
43
+ title: string | undefined;
44
+ /**
45
+ * Optional human-readable description for external surfaces such as MCP tools.
46
+ */
47
+ description: string | undefined;
40
48
  }
41
49
  /**
42
50
  * Decorator for capability functions to provide functionality such as caching, tracing and type-checked
@@ -0,0 +1,17 @@
1
+ import type * as z from 'zod';
2
+ export declare const REACTIONARY_ENTRYPOINT_METADATA: unique symbol;
3
+ export interface ReactionaryEntrypointMetadata {
4
+ capabilityName: string;
5
+ methodName: string;
6
+ inputSchema: z.ZodType;
7
+ outputSchema: z.ZodType;
8
+ title?: string;
9
+ description?: string;
10
+ cache: boolean;
11
+ localeDependentCaching: boolean;
12
+ currencyDependentCaching: boolean;
13
+ cacheTimeToLiveInSeconds: number;
14
+ }
15
+ export type ReactionaryEntrypointMethod = ((...args: unknown[]) => unknown) & {
16
+ [REACTIONARY_ENTRYPOINT_METADATA]?: Omit<ReactionaryEntrypointMetadata, 'capabilityName'>;
17
+ };