@reactionary/provider-fake 0.0.29 → 0.0.31

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/index.js CHANGED
@@ -1,181 +1,38 @@
1
1
  // core/src/cache/redis-cache.ts
2
2
  import { Redis } from "@upstash/redis";
3
3
 
4
- // otel/src/sdk.ts
5
- import { NodeSDK } from "@opentelemetry/sdk-node";
6
- import { resourceFromAttributes, defaultResource } from "@opentelemetry/resources";
7
- import { SEMRESATTRS_SERVICE_NAME, SEMRESATTRS_SERVICE_VERSION, SEMRESATTRS_DEPLOYMENT_ENVIRONMENT } from "@opentelemetry/semantic-conventions";
4
+ // otel/src/trpc-middleware.ts
5
+ import { TRPCError } from "@trpc/server";
8
6
  import {
9
- BatchSpanProcessor
10
- } from "@opentelemetry/sdk-trace-base";
11
- import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
12
- import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";
7
+ SpanKind as SpanKind2,
8
+ SpanStatusCode as SpanStatusCode3
9
+ } from "@opentelemetry/api";
13
10
 
14
- // otel/src/config.ts
15
- import { ConsoleSpanExporter } from "@opentelemetry/sdk-trace-base";
16
- import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
17
- import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
18
- import { PeriodicExportingMetricReader, ConsoleMetricExporter } from "@opentelemetry/sdk-metrics";
19
- function isBrowser() {
20
- return typeof window !== "undefined" && typeof process === "undefined";
21
- }
22
- function getConfigFromEnv() {
23
- if (isBrowser()) {
24
- return {
25
- serviceName: "browser-service",
26
- serviceVersion: void 0,
27
- environment: "browser",
28
- otlpEndpoint: void 0,
29
- otlpHeaders: void 0,
30
- traceEnabled: false,
31
- metricsEnabled: false,
32
- metricExportIntervalMillis: 6e4
33
- };
34
- }
35
- const tracesExporter = process.env["OTEL_TRACES_EXPORTER"] || "console";
36
- const metricsExporter = process.env["OTEL_METRICS_EXPORTER"] || "console";
37
- return {
38
- serviceName: process.env["OTEL_SERVICE_NAME"] || "unknown_service",
39
- serviceVersion: process.env["OTEL_SERVICE_VERSION"],
40
- environment: process.env["DEPLOYMENT_ENVIRONMENT"] || process.env["NODE_ENV"] || "development",
41
- otlpEndpoint: process.env["OTEL_EXPORTER_OTLP_ENDPOINT"],
42
- otlpHeaders: process.env["OTEL_EXPORTER_OTLP_HEADERS"] ? parseHeaders(process.env["OTEL_EXPORTER_OTLP_HEADERS"]) : void 0,
43
- traceEnabled: tracesExporter !== "none",
44
- metricsEnabled: metricsExporter !== "none",
45
- metricExportIntervalMillis: process.env["OTEL_METRIC_EXPORT_INTERVAL"] ? parseInt(process.env["OTEL_METRIC_EXPORT_INTERVAL"], 10) : 6e4
46
- // Default 60 seconds per OTEL spec
47
- };
48
- }
49
- function parseHeaders(headerString) {
50
- const headers = {};
51
- headerString.split(",").forEach((header) => {
52
- const [key, value] = header.split("=");
53
- if (key && value) {
54
- headers[key.trim()] = value.trim();
55
- }
56
- });
57
- return headers;
58
- }
59
- function createTraceExporter(config) {
60
- if (!config.traceEnabled) {
61
- return void 0;
62
- }
63
- const tracesExporter = process.env["OTEL_TRACES_EXPORTER"] || "console";
64
- switch (tracesExporter) {
65
- case "otlp":
66
- case "otlp/http": {
67
- if (!config.otlpEndpoint) {
68
- console.warn("OTEL_EXPORTER_OTLP_ENDPOINT not set, falling back to console");
69
- return new ConsoleSpanExporter();
70
- }
71
- const tracesEndpoint = process.env["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"] || `${config.otlpEndpoint}/v1/traces`;
72
- return new OTLPTraceExporter({
73
- url: tracesEndpoint,
74
- headers: config.otlpHeaders
75
- });
76
- }
77
- case "console":
78
- return new ConsoleSpanExporter();
79
- case "none":
80
- default:
81
- return void 0;
82
- }
83
- }
84
- function createMetricReader(config) {
85
- if (!config.metricsEnabled) {
86
- return void 0;
87
- }
88
- const metricsExporter = process.env["OTEL_METRICS_EXPORTER"] || "console";
89
- let exporter;
90
- switch (metricsExporter) {
91
- case "otlp":
92
- case "otlp/http": {
93
- if (!config.otlpEndpoint) {
94
- console.warn("OTEL_EXPORTER_OTLP_ENDPOINT not set, falling back to console");
95
- exporter = new ConsoleMetricExporter();
96
- } else {
97
- const metricsEndpoint = process.env["OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"] || `${config.otlpEndpoint}/v1/metrics`;
98
- exporter = new OTLPMetricExporter({
99
- url: metricsEndpoint,
100
- headers: config.otlpHeaders
101
- });
102
- }
103
- break;
104
- }
105
- case "console":
106
- exporter = new ConsoleMetricExporter();
107
- break;
108
- case "none":
109
- default:
110
- return void 0;
111
- }
112
- return new PeriodicExportingMetricReader({
113
- exporter,
114
- exportIntervalMillis: config.metricExportIntervalMillis
115
- });
116
- }
11
+ // otel/src/tracer.ts
12
+ import {
13
+ trace,
14
+ SpanStatusCode,
15
+ context as otelContext
16
+ } from "@opentelemetry/api";
117
17
 
118
18
  // otel/src/sdk.ts
19
+ import { NodeSDK } from "@opentelemetry/sdk-node";
119
20
  var sdk = null;
120
21
  var isInitialized = false;
121
22
  var initializationPromise = null;
122
- function isBrowser2() {
23
+ function isBrowser() {
123
24
  return typeof window !== "undefined" && typeof process === "undefined";
124
25
  }
125
26
  function ensureInitialized() {
126
27
  if (isInitialized || initializationPromise) {
127
28
  return;
128
29
  }
129
- if (isBrowser2()) {
30
+ if (isBrowser()) {
130
31
  isInitialized = true;
131
32
  return;
132
33
  }
133
34
  initializationPromise = Promise.resolve().then(() => {
134
- const config = getConfigFromEnv();
135
- if (process.env["OTEL_LOG_LEVEL"] === "debug") {
136
- console.log("OpenTelemetry auto-initializing with config:", {
137
- serviceName: config.serviceName,
138
- environment: config.environment,
139
- tracesExporter: process.env["OTEL_TRACES_EXPORTER"] || "console",
140
- metricsExporter: process.env["OTEL_METRICS_EXPORTER"] || "console"
141
- });
142
- }
143
- const attributes = {
144
- [SEMRESATTRS_SERVICE_NAME]: config.serviceName
145
- };
146
- if (config.serviceVersion) {
147
- attributes[SEMRESATTRS_SERVICE_VERSION] = config.serviceVersion;
148
- }
149
- if (config.environment) {
150
- attributes[SEMRESATTRS_DEPLOYMENT_ENVIRONMENT] = config.environment;
151
- }
152
- const customResource = resourceFromAttributes(attributes);
153
- const resource = defaultResource().merge(customResource);
154
- const traceExporter = createTraceExporter(config);
155
- const metricReader = createMetricReader(config);
156
- const instrumentations = [
157
- new HttpInstrumentation({
158
- requestHook: (span, request) => {
159
- const req = request;
160
- if (req.headers) {
161
- span.setAttribute("http.request.body.size", req.headers["content-length"] || 0);
162
- }
163
- },
164
- responseHook: (span, response) => {
165
- const res = response;
166
- if (res.headers) {
167
- span.setAttribute("http.response.body.size", res.headers["content-length"] || 0);
168
- }
169
- }
170
- }),
171
- new ExpressInstrumentation()
172
- ];
173
- sdk = new NodeSDK({
174
- resource,
175
- spanProcessors: traceExporter ? [new BatchSpanProcessor(traceExporter)] : [],
176
- metricReader,
177
- instrumentations
178
- });
35
+ sdk = new NodeSDK();
179
36
  sdk.start();
180
37
  isInitialized = true;
181
38
  process.on("SIGTERM", async () => {
@@ -203,11 +60,6 @@ function isOtelInitialized() {
203
60
  }
204
61
 
205
62
  // otel/src/tracer.ts
206
- import {
207
- trace,
208
- SpanStatusCode,
209
- context as otelContext
210
- } from "@opentelemetry/api";
211
63
  import { SpanKind, SpanStatusCode as SpanStatusCode2 } from "@opentelemetry/api";
212
64
  var TRACER_NAME = "@reactionary/otel";
213
65
  var TRACER_VERSION = "0.0.1";
@@ -303,13 +155,6 @@ function getMetrics() {
303
155
  return metricsInstance;
304
156
  }
305
157
 
306
- // otel/src/trpc-middleware.ts
307
- import { TRPCError } from "@trpc/server";
308
- import {
309
- SpanKind as SpanKind2,
310
- SpanStatusCode as SpanStatusCode3
311
- } from "@opentelemetry/api";
312
-
313
158
  // otel/src/provider-instrumentation.ts
314
159
  import { SpanKind as SpanKind3 } from "@opentelemetry/api";
315
160
  async function withProviderSpan(options, fn) {
@@ -387,16 +232,15 @@ function createProviderInstrumentation(providerName) {
387
232
  };
388
233
  }
389
234
 
390
- // otel/src/index.ts
391
- import { trace as trace2, context, SpanKind as SpanKind4, SpanStatusCode as SpanStatusCode4 } from "@opentelemetry/api";
392
-
393
235
  // core/src/providers/base.provider.ts
236
+ import * as crypto from "crypto";
394
237
  var BaseProvider = class {
395
- constructor(schema, querySchema, mutationSchema) {
238
+ constructor(schema, querySchema, mutationSchema, cache) {
396
239
  this.schema = schema;
397
240
  this.querySchema = querySchema;
398
241
  this.mutationSchema = mutationSchema;
399
242
  this.instrumentation = createProviderInstrumentation(this.constructor.name);
243
+ this.cache = cache;
400
244
  }
401
245
  /**
402
246
  * Validates that the final domain model constructed by the provider
@@ -422,11 +266,44 @@ var BaseProvider = class {
422
266
  "query",
423
267
  async (span) => {
424
268
  span.setAttribute("provider.query.count", queries.length);
425
- const results = await this.fetch(queries, session);
426
- for (const result of results) {
427
- this.assert(result);
269
+ let cacheHits = 0;
270
+ let cacheMisses = 0;
271
+ const results = [];
272
+ for (const query of queries) {
273
+ let result = null;
274
+ const cacheInfo = this.getCacheEvaluation(query, session);
275
+ if (cacheInfo.canCache && cacheInfo.key) {
276
+ try {
277
+ result = await this.cache.get(cacheInfo.key, this.schema);
278
+ if (result) {
279
+ cacheHits++;
280
+ span.setAttribute("provider.cache.hit", true);
281
+ }
282
+ } catch (error) {
283
+ console.warn(`Cache get error for ${this.constructor.name}:`, error);
284
+ }
285
+ }
286
+ if (!result) {
287
+ const singleResult = await this.fetch([query], session);
288
+ result = singleResult[0];
289
+ cacheMisses++;
290
+ if (result && cacheInfo.canCache && cacheInfo.key) {
291
+ try {
292
+ await this.cache.put(cacheInfo.key, result, cacheInfo.cacheDurationInSeconds);
293
+ } catch (error) {
294
+ console.warn(`Cache put error for ${this.constructor.name}:`, error);
295
+ }
296
+ }
297
+ }
298
+ if (result) {
299
+ this.assert(result);
300
+ results.push(result);
301
+ }
428
302
  }
429
303
  span.setAttribute("provider.result.count", results.length);
304
+ span.setAttribute("provider.cache.hits", cacheHits);
305
+ span.setAttribute("provider.cache.misses", cacheMisses);
306
+ span.setAttribute("provider.cache.hit_ratio", cacheHits / (cacheHits + cacheMisses));
430
307
  return results;
431
308
  },
432
309
  { queryCount: queries.length }
@@ -448,18 +325,75 @@ var BaseProvider = class {
448
325
  { mutationCount: mutations.length }
449
326
  );
450
327
  }
328
+ /**
329
+ * Provider-specific cache evaluation logic.
330
+ * Returns information about how this query should be cached.
331
+ * Override this method to enable caching with custom keys and TTL.
332
+ * Default implementation returns no caching.
333
+ */
334
+ getCacheEvaluation(query, session) {
335
+ const providerName = this.constructor.name.toLowerCase();
336
+ const userId = session.identity?.id || "anonymous";
337
+ const queryHash = crypto.createHash("md5").update(JSON.stringify(query)).digest("hex").substring(0, 12);
338
+ const key = `${providerName}:${userId}:${queryHash}`;
339
+ return {
340
+ key,
341
+ cacheDurationInSeconds: 0,
342
+ canCache: false
343
+ };
344
+ }
451
345
  };
452
346
 
453
347
  // core/src/providers/identity.provider.ts
454
348
  var IdentityProvider = class extends BaseProvider {
349
+ getCacheEvaluation(_query, session) {
350
+ const providerName = this.constructor.name.toLowerCase();
351
+ const userId = session.identity?.id;
352
+ const key = userId ? `${providerName}:identity:${userId}` : `${providerName}:identity:anonymous`;
353
+ return {
354
+ key,
355
+ cacheDurationInSeconds: 0,
356
+ canCache: false
357
+ };
358
+ }
455
359
  };
456
360
 
457
361
  // core/src/providers/product.provider.ts
362
+ import * as crypto2 from "crypto";
458
363
  var ProductProvider = class extends BaseProvider {
364
+ getCacheEvaluation(query, _session) {
365
+ const providerName = this.constructor.name.toLowerCase();
366
+ let key;
367
+ if (query.query === "slug") {
368
+ key = `${providerName}:product:slug:${query.slug}`;
369
+ } else if (query.query === "id") {
370
+ key = `${providerName}:product:id:${query.id}`;
371
+ } else {
372
+ const queryHash = crypto2.createHash("md5").update(JSON.stringify(query)).digest("hex").substring(0, 12);
373
+ key = `${providerName}:product:${queryHash}`;
374
+ }
375
+ return {
376
+ key,
377
+ cacheDurationInSeconds: 300,
378
+ // Products are moderately stable - 5 minutes
379
+ canCache: true
380
+ };
381
+ }
459
382
  };
460
383
 
461
384
  // core/src/providers/search.provider.ts
385
+ import * as crypto3 from "crypto";
462
386
  var SearchProvider = class extends BaseProvider {
387
+ getCacheEvaluation(query, _session) {
388
+ const providerName = this.constructor.name.toLowerCase();
389
+ const searchHash = crypto3.createHash("md5").update(JSON.stringify(query.search)).digest("hex").substring(0, 12);
390
+ const key = `${providerName}:search:${searchHash}`;
391
+ return {
392
+ key,
393
+ cacheDurationInSeconds: 0,
394
+ canCache: false
395
+ };
396
+ }
463
397
  };
464
398
 
465
399
  // core/src/schemas/capabilities.schema.ts
@@ -919,8 +853,8 @@ var SearchQuerySchema = z25.union([SearchQueryByTermSchema]);
919
853
  // providers/fake/src/providers/product.provider.ts
920
854
  import { base, en, Faker } from "@faker-js/faker";
921
855
  var FakeProductProvider = class extends ProductProvider {
922
- constructor(config, schema, querySchema, mutationSchema) {
923
- super(schema, querySchema, mutationSchema);
856
+ constructor(config, schema, querySchema, mutationSchema, cache) {
857
+ super(schema, querySchema, mutationSchema, cache);
924
858
  this.config = config;
925
859
  }
926
860
  async fetch(queries, session) {
@@ -980,8 +914,8 @@ function gaussianRandom(mean, deviation) {
980
914
 
981
915
  // providers/fake/src/providers/search.provider.ts
982
916
  var FakeSearchProvider = class extends SearchProvider {
983
- constructor(config, schema, querySchema, mutationSchema) {
984
- super(schema, querySchema, mutationSchema);
917
+ constructor(config, schema, querySchema, mutationSchema, cache) {
918
+ super(schema, querySchema, mutationSchema, cache);
985
919
  this.config = config;
986
920
  }
987
921
  async fetch(queries, session) {
@@ -1082,8 +1016,8 @@ var FakeSearchProvider = class extends SearchProvider {
1082
1016
  // providers/fake/src/providers/identity.provider.ts
1083
1017
  import { faker } from "@faker-js/faker";
1084
1018
  var FakeIdentityProvider = class extends IdentityProvider {
1085
- constructor(config, schema, querySchema, mutationSchema) {
1086
- super(schema, querySchema, mutationSchema);
1019
+ constructor(config, schema, querySchema, mutationSchema, cache) {
1020
+ super(schema, querySchema, mutationSchema, cache);
1087
1021
  this.config = config;
1088
1022
  }
1089
1023
  async fetch(queries, session) {
@@ -1130,17 +1064,19 @@ var FakeIdentityProvider = class extends IdentityProvider {
1130
1064
 
1131
1065
  // providers/fake/src/core/initialize.ts
1132
1066
  function withFakeCapabilities(configuration, capabilities) {
1133
- const client = {};
1134
- if (capabilities.product) {
1135
- client.product = new FakeProductProvider(configuration, ProductSchema, ProductQuerySchema, ProductMutationSchema);
1136
- }
1137
- if (capabilities.search) {
1138
- client.search = new FakeSearchProvider(configuration, SearchResultSchema, SearchQuerySchema, SearchMutationSchema);
1139
- }
1140
- if (capabilities.identity) {
1141
- client.identity = new FakeIdentityProvider(configuration, IdentitySchema, IdentityQuerySchema, IdentityMutationSchema);
1142
- }
1143
- return client;
1067
+ return (cache) => {
1068
+ const client = {};
1069
+ if (capabilities.product) {
1070
+ client.product = new FakeProductProvider(configuration, ProductSchema, ProductQuerySchema, ProductMutationSchema, cache);
1071
+ }
1072
+ if (capabilities.search) {
1073
+ client.search = new FakeSearchProvider(configuration, SearchResultSchema, SearchQuerySchema, SearchMutationSchema, cache);
1074
+ }
1075
+ if (capabilities.identity) {
1076
+ client.identity = new FakeIdentityProvider(configuration, IdentitySchema, IdentityQuerySchema, IdentityMutationSchema, cache);
1077
+ }
1078
+ return client;
1079
+ };
1144
1080
  }
1145
1081
 
1146
1082
  // providers/fake/src/schema/capabilities.schema.ts
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@reactionary/provider-fake",
3
- "version": "0.0.29",
3
+ "version": "0.0.31",
4
4
  "dependencies": {
5
- "@reactionary/core": "0.0.29",
5
+ "@reactionary/core": "0.0.31",
6
6
  "zod": "4.0.0-beta.20250430T185432",
7
7
  "@faker-js/faker": "^9.8.0"
8
8
  }
@@ -1,4 +1,4 @@
1
- import { Client } from "@reactionary/core";
1
+ import { Client, Cache } from "@reactionary/core";
2
2
  import { FakeConfiguration } from "../schema/configuration.schema";
3
3
  import { FakeCapabilities } from "../schema/capabilities.schema";
4
- export declare function withFakeCapabilities(configuration: FakeConfiguration, capabilities: FakeCapabilities): Partial<Client>;
4
+ export declare function withFakeCapabilities(configuration: FakeConfiguration, capabilities: FakeCapabilities): (cache: Cache) => Partial<Client>;
@@ -3,7 +3,7 @@ import { FakeConfiguration } from '../schema/configuration.schema';
3
3
  import z from 'zod';
4
4
  export declare class FakeIdentityProvider<T extends Identity = Identity, Q extends IdentityQuery = IdentityQuery, M extends IdentityMutation = IdentityMutation> extends IdentityProvider<T, Q, M> {
5
5
  protected config: FakeConfiguration;
6
- constructor(config: FakeConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>);
6
+ constructor(config: FakeConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>, cache: any);
7
7
  protected fetch(queries: Q[], session: Session): Promise<T[]>;
8
8
  protected process(mutations: M[], session: Session): Promise<T>;
9
9
  protected login(payload: IdentityMutationLogin, session: Session): Promise<T>;
@@ -3,7 +3,7 @@ import z from 'zod';
3
3
  import { FakeConfiguration } from '../schema/configuration.schema';
4
4
  export declare class FakeProductProvider<T extends Product = Product, Q extends ProductQuery = ProductQuery, M extends ProductMutation = ProductMutation> extends ProductProvider<T, Q, M> {
5
5
  protected config: FakeConfiguration;
6
- constructor(config: FakeConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>);
6
+ constructor(config: FakeConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>, cache: any);
7
7
  protected fetch(queries: Q[], session: Session): Promise<T[]>;
8
8
  protected process(mutation: BaseMutation[], session: Session): Promise<T>;
9
9
  }
@@ -3,7 +3,7 @@ import z from 'zod';
3
3
  import { FakeConfiguration } from '../schema/configuration.schema';
4
4
  export declare class FakeSearchProvider<T extends SearchResult = SearchResult, Q extends SearchQuery = SearchQuery, M extends SearchMutation = SearchMutation> extends SearchProvider<T, Q, M> {
5
5
  protected config: FakeConfiguration;
6
- constructor(config: FakeConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>);
6
+ constructor(config: FakeConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>, cache: any);
7
7
  protected fetch(queries: Q[], session: Session): Promise<T[]>;
8
8
  protected process(mutations: M[], session: Session): Promise<T>;
9
9
  get(identifier: SearchIdentifier): Promise<T>;
@@ -1,10 +1,10 @@
1
1
  import { z } from 'zod';
2
2
  export declare const FakeCapabilitiesSchema: z.ZodInterface<{
3
- identity: z.ZodOptional<z.ZodBoolean>;
4
3
  search: z.ZodOptional<z.ZodBoolean>;
5
4
  product: z.ZodOptional<z.ZodBoolean>;
5
+ identity: z.ZodOptional<z.ZodBoolean>;
6
6
  }, {
7
- optional: "identity" | "search" | "product";
7
+ optional: "search" | "product" | "identity";
8
8
  defaulted: never;
9
9
  extra: Record<string, unknown>;
10
10
  }>;