@criterionx/server 0.3.2 → 0.3.4
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/dist/index.d.ts +138 -6
- package/dist/index.js +161 -10
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import * as hono from 'hono';
|
|
2
|
+
import { Context, Next, Hono } from 'hono';
|
|
1
3
|
import { Decision, Result, JsonSchema } from '@criterionx/core';
|
|
2
4
|
export { DecisionSchema, JsonSchema, extractDecisionSchema, toJsonSchema } from '@criterionx/core';
|
|
3
|
-
import { Hono } from 'hono';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Context passed to hooks
|
|
@@ -58,6 +59,74 @@ interface MetricsOptions$1 {
|
|
|
58
59
|
/** Histogram buckets for latency in seconds */
|
|
59
60
|
buckets?: number[];
|
|
60
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Log entry emitted for each evaluation request
|
|
64
|
+
*/
|
|
65
|
+
interface LogEntry {
|
|
66
|
+
/** Unique request identifier */
|
|
67
|
+
requestId: string;
|
|
68
|
+
/** ID of the decision that was evaluated */
|
|
69
|
+
decisionId: string;
|
|
70
|
+
/** Result status or ERROR */
|
|
71
|
+
status: "OK" | "NO_MATCH" | "INVALID_INPUT" | "INVALID_OUTPUT" | "ERROR";
|
|
72
|
+
/** Duration of the evaluation in milliseconds */
|
|
73
|
+
durationMs: number;
|
|
74
|
+
/** ISO timestamp when the request completed */
|
|
75
|
+
timestamp: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Logger function type - user provides their own implementation
|
|
79
|
+
*/
|
|
80
|
+
type LoggerFn = (entry: LogEntry) => void;
|
|
81
|
+
/**
|
|
82
|
+
* Logging configuration options
|
|
83
|
+
*/
|
|
84
|
+
interface LoggingOptions {
|
|
85
|
+
/** Enable request logging (default: false) */
|
|
86
|
+
enabled?: boolean;
|
|
87
|
+
/** Custom logger function - receives structured log entries */
|
|
88
|
+
logger: LoggerFn;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Rate limit info returned by store
|
|
92
|
+
*/
|
|
93
|
+
interface RateLimitInfo {
|
|
94
|
+
/** Current request count in window */
|
|
95
|
+
count: number;
|
|
96
|
+
/** Unix timestamp (ms) when window resets */
|
|
97
|
+
resetTime: number;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Rate limit store interface for custom implementations
|
|
101
|
+
*
|
|
102
|
+
* Implement this interface to use external stores like Redis
|
|
103
|
+
* for distributed rate limiting.
|
|
104
|
+
*/
|
|
105
|
+
interface RateLimitStore {
|
|
106
|
+
/** Increment counter for key and return current info */
|
|
107
|
+
increment(key: string): Promise<RateLimitInfo>;
|
|
108
|
+
/** Reset counter for key */
|
|
109
|
+
reset(key: string): Promise<void>;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Rate limiting configuration options
|
|
113
|
+
*/
|
|
114
|
+
interface RateLimitOptions {
|
|
115
|
+
/** Enable rate limiting (default: false) */
|
|
116
|
+
enabled?: boolean;
|
|
117
|
+
/** Time window in milliseconds (default: 60000 = 1 minute) */
|
|
118
|
+
windowMs?: number;
|
|
119
|
+
/** Maximum requests per window (default: 100) */
|
|
120
|
+
max?: number;
|
|
121
|
+
/** Custom key generator function (default: client IP) */
|
|
122
|
+
keyGenerator?: (c: hono.Context) => string;
|
|
123
|
+
/** Custom handler for rate limit exceeded (default: 429 JSON response) */
|
|
124
|
+
handler?: (c: hono.Context) => Response;
|
|
125
|
+
/** Skip rate limiting for certain requests (default: skip /health, /metrics) */
|
|
126
|
+
skip?: (c: hono.Context) => boolean;
|
|
127
|
+
/** Custom store for distributed rate limiting (default: in-memory) */
|
|
128
|
+
store?: RateLimitStore;
|
|
129
|
+
}
|
|
61
130
|
/**
|
|
62
131
|
* OpenAPI info object
|
|
63
132
|
*/
|
|
@@ -111,6 +180,10 @@ interface ServerOptions {
|
|
|
111
180
|
metrics?: MetricsOptions$1;
|
|
112
181
|
/** OpenAPI spec generation configuration */
|
|
113
182
|
openapi?: OpenAPIOptions;
|
|
183
|
+
/** Request logging configuration */
|
|
184
|
+
logging?: LoggingOptions;
|
|
185
|
+
/** Rate limiting configuration */
|
|
186
|
+
rateLimit?: RateLimitOptions;
|
|
114
187
|
}
|
|
115
188
|
/**
|
|
116
189
|
* Request body for decision evaluation
|
|
@@ -118,8 +191,28 @@ interface ServerOptions {
|
|
|
118
191
|
interface EvaluateRequest {
|
|
119
192
|
/** Input data for the decision */
|
|
120
193
|
input: unknown;
|
|
121
|
-
/** Profile to use (overrides default) */
|
|
194
|
+
/** Profile to use (overrides default and profileVersion) */
|
|
122
195
|
profile?: unknown;
|
|
196
|
+
/** Profile version to use (e.g., "v1", "conservative") */
|
|
197
|
+
profileVersion?: string;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Profile version info for listing
|
|
201
|
+
*/
|
|
202
|
+
interface ProfileVersionInfo {
|
|
203
|
+
/** Version identifier (null for default) */
|
|
204
|
+
version: string | null;
|
|
205
|
+
/** Whether this is the default profile */
|
|
206
|
+
isDefault: boolean;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Response for listing profile versions
|
|
210
|
+
*/
|
|
211
|
+
interface ProfileListResponse {
|
|
212
|
+
/** Decision ID */
|
|
213
|
+
decisionId: string;
|
|
214
|
+
/** Available profile versions */
|
|
215
|
+
versions: ProfileVersionInfo[];
|
|
123
216
|
}
|
|
124
217
|
/**
|
|
125
218
|
* Decision info for listing
|
|
@@ -133,7 +226,7 @@ interface DecisionInfo {
|
|
|
133
226
|
/**
|
|
134
227
|
* Error codes for structured error responses
|
|
135
228
|
*/
|
|
136
|
-
type ErrorCode = "DECISION_NOT_FOUND" | "INVALID_JSON" | "MISSING_INPUT" | "MISSING_PROFILE" | "VALIDATION_ERROR" | "EVALUATION_ERROR" | "INTERNAL_ERROR";
|
|
229
|
+
type ErrorCode = "DECISION_NOT_FOUND" | "INVALID_JSON" | "MISSING_INPUT" | "MISSING_PROFILE" | "VALIDATION_ERROR" | "EVALUATION_ERROR" | "INTERNAL_ERROR" | "RATE_LIMIT_EXCEEDED";
|
|
137
230
|
/**
|
|
138
231
|
* Structured error response
|
|
139
232
|
*/
|
|
@@ -266,9 +359,9 @@ interface Operation {
|
|
|
266
359
|
};
|
|
267
360
|
};
|
|
268
361
|
};
|
|
269
|
-
responses: Record<string, Response>;
|
|
362
|
+
responses: Record<string, Response$1>;
|
|
270
363
|
}
|
|
271
|
-
interface Response {
|
|
364
|
+
interface Response$1 {
|
|
272
365
|
description: string;
|
|
273
366
|
content?: {
|
|
274
367
|
"application/json": {
|
|
@@ -287,6 +380,40 @@ declare function generateOpenAPISpec(decisions: Decision<any, any, any>[], info?
|
|
|
287
380
|
*/
|
|
288
381
|
declare function generateSwaggerUIHtml(specUrl: string): string;
|
|
289
382
|
|
|
383
|
+
/**
|
|
384
|
+
* Default in-memory rate limit store
|
|
385
|
+
*
|
|
386
|
+
* Simple implementation for single-instance deployments.
|
|
387
|
+
* For distributed deployments, use a custom store (e.g., Redis).
|
|
388
|
+
*/
|
|
389
|
+
declare class InMemoryRateLimitStore implements RateLimitStore {
|
|
390
|
+
private hits;
|
|
391
|
+
private windowMs;
|
|
392
|
+
constructor(windowMs: number);
|
|
393
|
+
increment(key: string): Promise<RateLimitInfo>;
|
|
394
|
+
reset(key: string): Promise<void>;
|
|
395
|
+
/**
|
|
396
|
+
* Get current info for a key (for testing/debugging)
|
|
397
|
+
*/
|
|
398
|
+
getInfo(key: string): RateLimitInfo | undefined;
|
|
399
|
+
/**
|
|
400
|
+
* Clear all entries (for testing)
|
|
401
|
+
*/
|
|
402
|
+
clear(): void;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Create rate limiting middleware for Hono
|
|
406
|
+
*
|
|
407
|
+
* @example
|
|
408
|
+
* ```typescript
|
|
409
|
+
* app.use("*", createRateLimitMiddleware({
|
|
410
|
+
* windowMs: 60000, // 1 minute
|
|
411
|
+
* max: 100, // 100 requests per window
|
|
412
|
+
* }));
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
declare function createRateLimitMiddleware(options: RateLimitOptions): (c: Context, next: Next) => Promise<void | Response>;
|
|
416
|
+
|
|
290
417
|
/**
|
|
291
418
|
* Criterion Server
|
|
292
419
|
*
|
|
@@ -302,9 +429,14 @@ declare class CriterionServer {
|
|
|
302
429
|
private metricsOptions;
|
|
303
430
|
private openApiOptions;
|
|
304
431
|
private openApiSpec;
|
|
432
|
+
private loggingOptions;
|
|
305
433
|
private startTime;
|
|
306
434
|
constructor(options: ServerOptions);
|
|
307
435
|
private setupRoutes;
|
|
436
|
+
/**
|
|
437
|
+
* Get available profile versions for a decision
|
|
438
|
+
*/
|
|
439
|
+
private getProfileVersions;
|
|
308
440
|
private generateDocsHtml;
|
|
309
441
|
/**
|
|
310
442
|
* Get the Hono app instance (for custom middleware)
|
|
@@ -324,4 +456,4 @@ declare class CriterionServer {
|
|
|
324
456
|
*/
|
|
325
457
|
declare function createServer(options: ServerOptions): CriterionServer;
|
|
326
458
|
|
|
327
|
-
export { type AfterEvaluateHook, type BeforeEvaluateHook, CriterionServer, type DecisionInfo, type ErrorCode, type ErrorResponse, type EvaluateRequest, type HealthResponse, type HealthStatus, type HookContext, type Hooks, METRIC_EVALUATIONS_TOTAL, METRIC_EVALUATION_DURATION_SECONDS, METRIC_RULE_MATCHES_TOTAL, MetricsCollector, type MetricsOptions$1 as MetricsOptions, type OnErrorHook, type OpenAPIInfo, type OpenAPIOptions, type OpenAPISpec, type ServerOptions, createServer, generateEndpointSchema, generateOpenAPISpec, generateSwaggerUIHtml };
|
|
459
|
+
export { type AfterEvaluateHook, type BeforeEvaluateHook, CriterionServer, type DecisionInfo, type ErrorCode, type ErrorResponse, type EvaluateRequest, type HealthResponse, type HealthStatus, type HookContext, type Hooks, InMemoryRateLimitStore, type LogEntry, type LoggerFn, type LoggingOptions, METRIC_EVALUATIONS_TOTAL, METRIC_EVALUATION_DURATION_SECONDS, METRIC_RULE_MATCHES_TOTAL, MetricsCollector, type MetricsOptions$1 as MetricsOptions, type OnErrorHook, type OpenAPIInfo, type OpenAPIOptions, type OpenAPISpec, type ProfileListResponse, type ProfileVersionInfo, type RateLimitInfo, type RateLimitOptions, type RateLimitStore, type ServerOptions, createRateLimitMiddleware, createServer, generateEndpointSchema, generateOpenAPISpec, generateSwaggerUIHtml };
|
package/dist/index.js
CHANGED
|
@@ -452,6 +452,84 @@ function generateSwaggerUIHtml(specUrl) {
|
|
|
452
452
|
</html>`;
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
+
// src/rate-limit.ts
|
|
456
|
+
var InMemoryRateLimitStore = class {
|
|
457
|
+
hits = /* @__PURE__ */ new Map();
|
|
458
|
+
windowMs;
|
|
459
|
+
constructor(windowMs) {
|
|
460
|
+
this.windowMs = windowMs;
|
|
461
|
+
}
|
|
462
|
+
async increment(key) {
|
|
463
|
+
const now = Date.now();
|
|
464
|
+
const record = this.hits.get(key);
|
|
465
|
+
if (!record || now >= record.resetTime) {
|
|
466
|
+
const resetTime = now + this.windowMs;
|
|
467
|
+
this.hits.set(key, { count: 1, resetTime });
|
|
468
|
+
return { count: 1, resetTime };
|
|
469
|
+
}
|
|
470
|
+
record.count++;
|
|
471
|
+
return { count: record.count, resetTime: record.resetTime };
|
|
472
|
+
}
|
|
473
|
+
async reset(key) {
|
|
474
|
+
this.hits.delete(key);
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Get current info for a key (for testing/debugging)
|
|
478
|
+
*/
|
|
479
|
+
getInfo(key) {
|
|
480
|
+
return this.hits.get(key);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Clear all entries (for testing)
|
|
484
|
+
*/
|
|
485
|
+
clear() {
|
|
486
|
+
this.hits.clear();
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
function defaultKeyGenerator(c) {
|
|
490
|
+
return c.req.header("x-forwarded-for")?.split(",")[0]?.trim() ?? c.req.header("x-real-ip") ?? "unknown";
|
|
491
|
+
}
|
|
492
|
+
function defaultSkip(c) {
|
|
493
|
+
const path = c.req.path;
|
|
494
|
+
return path === "/health" || path === "/metrics";
|
|
495
|
+
}
|
|
496
|
+
function createRateLimitMiddleware(options) {
|
|
497
|
+
const windowMs = options.windowMs ?? 6e4;
|
|
498
|
+
const max = options.max ?? 100;
|
|
499
|
+
const store = options.store ?? new InMemoryRateLimitStore(windowMs);
|
|
500
|
+
const keyGenerator = options.keyGenerator ?? defaultKeyGenerator;
|
|
501
|
+
const skip = options.skip ?? defaultSkip;
|
|
502
|
+
return async (c, next) => {
|
|
503
|
+
if (skip(c)) {
|
|
504
|
+
return next();
|
|
505
|
+
}
|
|
506
|
+
const key = keyGenerator(c);
|
|
507
|
+
const info = await store.increment(key);
|
|
508
|
+
c.header("X-RateLimit-Limit", String(max));
|
|
509
|
+
c.header("X-RateLimit-Remaining", String(Math.max(0, max - info.count)));
|
|
510
|
+
c.header("X-RateLimit-Reset", String(Math.floor(info.resetTime / 1e3)));
|
|
511
|
+
if (info.count > max) {
|
|
512
|
+
const retryAfter = Math.max(1, Math.ceil((info.resetTime - Date.now()) / 1e3));
|
|
513
|
+
c.header("Retry-After", String(retryAfter));
|
|
514
|
+
if (options.handler) {
|
|
515
|
+
return options.handler(c);
|
|
516
|
+
}
|
|
517
|
+
return c.json(
|
|
518
|
+
{
|
|
519
|
+
error: {
|
|
520
|
+
code: "RATE_LIMIT_EXCEEDED",
|
|
521
|
+
message: "Too many requests, please try again later"
|
|
522
|
+
},
|
|
523
|
+
retryAfter,
|
|
524
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
525
|
+
},
|
|
526
|
+
429
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
return next();
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
455
533
|
// src/server.ts
|
|
456
534
|
import { Hono } from "hono";
|
|
457
535
|
import { cors } from "hono/cors";
|
|
@@ -483,6 +561,7 @@ var CriterionServer = class {
|
|
|
483
561
|
metricsOptions;
|
|
484
562
|
openApiOptions;
|
|
485
563
|
openApiSpec = null;
|
|
564
|
+
loggingOptions = null;
|
|
486
565
|
startTime;
|
|
487
566
|
constructor(options) {
|
|
488
567
|
this.app = new Hono();
|
|
@@ -496,6 +575,9 @@ var CriterionServer = class {
|
|
|
496
575
|
if (this.metricsOptions.enabled) {
|
|
497
576
|
this.metricsCollector = new MetricsCollector(this.metricsOptions);
|
|
498
577
|
}
|
|
578
|
+
if (options.logging?.enabled) {
|
|
579
|
+
this.loggingOptions = options.logging;
|
|
580
|
+
}
|
|
499
581
|
for (const decision of options.decisions) {
|
|
500
582
|
this.decisions.set(decision.id, decision);
|
|
501
583
|
}
|
|
@@ -513,6 +595,9 @@ var CriterionServer = class {
|
|
|
513
595
|
if (options.cors !== false) {
|
|
514
596
|
this.app.use("*", cors());
|
|
515
597
|
}
|
|
598
|
+
if (options.rateLimit?.enabled) {
|
|
599
|
+
this.app.use("*", createRateLimitMiddleware(options.rateLimit));
|
|
600
|
+
}
|
|
516
601
|
this.setupRoutes();
|
|
517
602
|
}
|
|
518
603
|
setupRoutes() {
|
|
@@ -628,16 +713,31 @@ var CriterionServer = class {
|
|
|
628
713
|
}
|
|
629
714
|
let profile = body.profile;
|
|
630
715
|
if (!profile) {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
716
|
+
if (body.profileVersion) {
|
|
717
|
+
const versionedKey = `${id}:${body.profileVersion}`;
|
|
718
|
+
profile = this.profiles.get(versionedKey);
|
|
719
|
+
if (!profile) {
|
|
720
|
+
return c.json(
|
|
721
|
+
createErrorResponse(
|
|
722
|
+
"MISSING_PROFILE",
|
|
723
|
+
`Profile version not found: ${body.profileVersion} for decision: ${id}`,
|
|
724
|
+
requestId
|
|
725
|
+
),
|
|
726
|
+
400
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
} else {
|
|
730
|
+
profile = this.profiles.get(id);
|
|
731
|
+
if (!profile) {
|
|
732
|
+
return c.json(
|
|
733
|
+
createErrorResponse(
|
|
734
|
+
"MISSING_PROFILE",
|
|
735
|
+
`No profile provided and no default profile for decision: ${id}`,
|
|
736
|
+
requestId
|
|
737
|
+
),
|
|
738
|
+
400
|
|
739
|
+
);
|
|
740
|
+
}
|
|
641
741
|
}
|
|
642
742
|
}
|
|
643
743
|
let ctx = {
|
|
@@ -679,6 +779,16 @@ var CriterionServer = class {
|
|
|
679
779
|
if (this.hooks.afterEvaluate) {
|
|
680
780
|
await this.hooks.afterEvaluate(ctx, result);
|
|
681
781
|
}
|
|
782
|
+
if (this.loggingOptions) {
|
|
783
|
+
const entry = {
|
|
784
|
+
requestId,
|
|
785
|
+
decisionId: id,
|
|
786
|
+
status: result.status,
|
|
787
|
+
durationMs: performance.now() - startTime,
|
|
788
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
789
|
+
};
|
|
790
|
+
this.loggingOptions.logger(entry);
|
|
791
|
+
}
|
|
682
792
|
const statusCode = result.status === "OK" ? 200 : 400;
|
|
683
793
|
return c.json(result, statusCode);
|
|
684
794
|
} catch (error) {
|
|
@@ -692,6 +802,16 @@ var CriterionServer = class {
|
|
|
692
802
|
if (this.hooks.onError) {
|
|
693
803
|
await this.hooks.onError(ctx, err);
|
|
694
804
|
}
|
|
805
|
+
if (this.loggingOptions) {
|
|
806
|
+
const entry = {
|
|
807
|
+
requestId,
|
|
808
|
+
decisionId: id,
|
|
809
|
+
status: "ERROR",
|
|
810
|
+
durationMs: performance.now() - startTime,
|
|
811
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
812
|
+
};
|
|
813
|
+
this.loggingOptions.logger(entry);
|
|
814
|
+
}
|
|
695
815
|
return c.json(
|
|
696
816
|
createErrorResponse(
|
|
697
817
|
"EVALUATION_ERROR",
|
|
@@ -703,6 +823,18 @@ var CriterionServer = class {
|
|
|
703
823
|
);
|
|
704
824
|
}
|
|
705
825
|
});
|
|
826
|
+
this.app.get("/decisions/:id/profiles", (c) => {
|
|
827
|
+
const id = c.req.param("id");
|
|
828
|
+
const decision = this.decisions.get(id);
|
|
829
|
+
if (!decision) {
|
|
830
|
+
return c.json(
|
|
831
|
+
createErrorResponse("DECISION_NOT_FOUND", `Decision not found: ${id}`),
|
|
832
|
+
404
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
const versions = this.getProfileVersions(id);
|
|
836
|
+
return c.json({ decisionId: id, versions });
|
|
837
|
+
});
|
|
706
838
|
this.app.get("/docs", (c) => {
|
|
707
839
|
const decisions = Array.from(this.decisions.values()).map((d) => ({
|
|
708
840
|
id: d.id,
|
|
@@ -713,6 +845,23 @@ var CriterionServer = class {
|
|
|
713
845
|
return c.html(html);
|
|
714
846
|
});
|
|
715
847
|
}
|
|
848
|
+
/**
|
|
849
|
+
* Get available profile versions for a decision
|
|
850
|
+
*/
|
|
851
|
+
getProfileVersions(decisionId) {
|
|
852
|
+
const versions = [];
|
|
853
|
+
const prefix = `${decisionId}:`;
|
|
854
|
+
if (this.profiles.has(decisionId)) {
|
|
855
|
+
versions.push({ version: null, isDefault: true });
|
|
856
|
+
}
|
|
857
|
+
for (const key of this.profiles.keys()) {
|
|
858
|
+
if (key.startsWith(prefix)) {
|
|
859
|
+
const version = key.slice(prefix.length);
|
|
860
|
+
versions.push({ version, isDefault: false });
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return versions;
|
|
864
|
+
}
|
|
716
865
|
generateDocsHtml(decisions) {
|
|
717
866
|
return `<!DOCTYPE html>
|
|
718
867
|
<html lang="en">
|
|
@@ -956,10 +1105,12 @@ function createServer(options) {
|
|
|
956
1105
|
}
|
|
957
1106
|
export {
|
|
958
1107
|
CriterionServer,
|
|
1108
|
+
InMemoryRateLimitStore,
|
|
959
1109
|
METRIC_EVALUATIONS_TOTAL,
|
|
960
1110
|
METRIC_EVALUATION_DURATION_SECONDS,
|
|
961
1111
|
METRIC_RULE_MATCHES_TOTAL,
|
|
962
1112
|
MetricsCollector,
|
|
1113
|
+
createRateLimitMiddleware,
|
|
963
1114
|
createServer,
|
|
964
1115
|
extractDecisionSchema,
|
|
965
1116
|
generateEndpointSchema,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@criterionx/server",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "HTTP server for Criterion decisions with auto-generated docs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@hono/node-server": "^1.13.0",
|
|
43
43
|
"hono": "^4.6.0",
|
|
44
44
|
"zod-to-json-schema": "^3.24.0",
|
|
45
|
-
"@criterionx/core": "0.3.
|
|
45
|
+
"@criterionx/core": "0.3.4"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"zod": "^3.22.0"
|