@openora/core 0.4.1-canary.22 → 0.4.1-canary.26
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/.tsbuildinfo +1 -1
- package/dist/audit/__tests__/audit.service.test.js +24 -1
- package/dist/audit/__tests__/audit.service.test.js.map +1 -1
- package/dist/audit/contract/index.d.ts +8 -8
- package/dist/audit/contract/index.js +2 -2
- package/dist/audit/contract/index.js.map +1 -1
- package/dist/audit/router/index.d.ts +4 -4
- package/dist/audit/service/audit.service.d.ts +2 -0
- package/dist/audit/service/audit.service.d.ts.map +1 -1
- package/dist/audit/service/audit.service.js +9 -1
- package/dist/audit/service/audit.service.js.map +1 -1
- package/dist/casino/lobby/__tests__/lobby.cache.test.js +1 -1
- package/dist/casino/lobby/__tests__/lobby.cache.test.js.map +1 -1
- package/dist/cms/__tests__/cms.reads.test.d.ts +2 -0
- package/dist/cms/__tests__/cms.reads.test.d.ts.map +1 -0
- package/dist/cms/__tests__/{cms.cache.test.js → cms.reads.test.js} +2 -2
- package/dist/cms/__tests__/cms.reads.test.js.map +1 -0
- package/dist/cms/__tests__/cms.router.test.js +25 -22
- package/dist/cms/__tests__/cms.router.test.js.map +1 -1
- package/dist/contracts/schemas/events.d.ts +1 -0
- package/dist/contracts/schemas/events.d.ts.map +1 -1
- package/dist/contracts/schemas/events.js +2 -1
- package/dist/contracts/schemas/events.js.map +1 -1
- package/dist/engagement/chat/__tests__/chat.service.test.js +1 -1
- package/dist/engagement/chat/__tests__/chat.service.test.js.map +1 -1
- package/dist/iam/__tests__/iam.service.test.js +4 -3
- package/dist/iam/__tests__/iam.service.test.js.map +1 -1
- package/dist/pam/identity/__tests__/identity.rate-limit.test.js +4 -3
- package/dist/pam/identity/__tests__/identity.rate-limit.test.js.map +1 -1
- package/dist/pam/identity/__tests__/identity.service.test.js +6 -1
- package/dist/pam/identity/__tests__/identity.service.test.js.map +1 -1
- package/dist/pam/identity/service/identity.service.d.ts.map +1 -1
- package/dist/pam/identity/service/identity.service.js +1 -0
- package/dist/pam/identity/service/identity.service.js.map +1 -1
- package/dist/server/kernel/__tests__/cache.test.js +11 -57
- package/dist/server/kernel/__tests__/cache.test.js.map +1 -1
- package/dist/server/kernel/__tests__/event-bus.test.js +14 -1
- package/dist/server/kernel/__tests__/event-bus.test.js.map +1 -1
- package/dist/server/kernel/__tests__/rate-limiter.test.js +3 -45
- package/dist/server/kernel/__tests__/rate-limiter.test.js.map +1 -1
- package/dist/server/kernel/__tests__/redis-streams-broker.test.d.ts +2 -0
- package/dist/server/kernel/__tests__/redis-streams-broker.test.d.ts.map +1 -0
- package/dist/server/kernel/__tests__/redis-streams-broker.test.js +293 -0
- package/dist/server/kernel/__tests__/redis-streams-broker.test.js.map +1 -0
- package/dist/server/kernel/cache.d.ts +0 -12
- package/dist/server/kernel/cache.d.ts.map +1 -1
- package/dist/server/kernel/cache.js +0 -55
- package/dist/server/kernel/cache.js.map +1 -1
- package/dist/server/kernel/event-bus.d.ts +1 -16
- package/dist/server/kernel/event-bus.d.ts.map +1 -1
- package/dist/server/kernel/event-bus.js +3 -32
- package/dist/server/kernel/event-bus.js.map +1 -1
- package/dist/server/kernel/index.d.ts +4 -6
- package/dist/server/kernel/index.d.ts.map +1 -1
- package/dist/server/kernel/index.js +4 -6
- package/dist/server/kernel/index.js.map +1 -1
- package/dist/server/kernel/rate-limiter.d.ts +1 -10
- package/dist/server/kernel/rate-limiter.d.ts.map +1 -1
- package/dist/server/kernel/rate-limiter.js +0 -38
- package/dist/server/kernel/rate-limiter.js.map +1 -1
- package/dist/server/kernel/redis-streams-broker.d.ts +65 -0
- package/dist/server/kernel/redis-streams-broker.d.ts.map +1 -0
- package/dist/server/kernel/redis-streams-broker.js +208 -0
- package/dist/server/kernel/redis-streams-broker.js.map +1 -0
- package/dist/server/runtime/__tests__/create-app.test.d.ts +2 -0
- package/dist/server/runtime/__tests__/create-app.test.d.ts.map +1 -0
- package/dist/server/runtime/__tests__/create-app.test.js +60 -0
- package/dist/server/runtime/__tests__/create-app.test.js.map +1 -0
- package/dist/server/runtime/assert-durable-seams.d.ts +18 -0
- package/dist/server/runtime/assert-durable-seams.d.ts.map +1 -0
- package/dist/server/runtime/assert-durable-seams.js +32 -0
- package/dist/server/runtime/assert-durable-seams.js.map +1 -0
- package/dist/server/runtime/create-app.d.ts +15 -9
- package/dist/server/runtime/create-app.d.ts.map +1 -1
- package/dist/server/runtime/create-app.js +61 -49
- package/dist/server/runtime/create-app.js.map +1 -1
- package/dist/testing/fakes/__tests__/cache.test.d.ts +2 -0
- package/dist/testing/fakes/__tests__/cache.test.d.ts.map +1 -0
- package/dist/testing/fakes/__tests__/cache.test.js +89 -0
- package/dist/testing/fakes/__tests__/cache.test.js.map +1 -0
- package/dist/testing/fakes/__tests__/job-queue.test.d.ts.map +1 -0
- package/dist/{server/kernel → testing/fakes}/__tests__/job-queue.test.js +1 -1
- package/dist/testing/fakes/__tests__/job-queue.test.js.map +1 -0
- package/dist/testing/fakes/__tests__/rate-limiter.test.d.ts +2 -0
- package/dist/testing/fakes/__tests__/rate-limiter.test.d.ts.map +1 -0
- package/dist/testing/fakes/__tests__/rate-limiter.test.js +83 -0
- package/dist/testing/fakes/__tests__/rate-limiter.test.js.map +1 -0
- package/dist/testing/fakes/__tests__/realtime-authorizer.test.d.ts.map +1 -0
- package/dist/{server/kernel → testing/fakes}/__tests__/realtime-authorizer.test.js.map +1 -1
- package/dist/testing/fakes/__tests__/realtime-transport.test.d.ts.map +1 -0
- package/dist/{server/kernel → testing/fakes}/__tests__/realtime-transport.test.js.map +1 -1
- package/dist/testing/fakes/cache.d.ts +14 -0
- package/dist/testing/fakes/cache.d.ts.map +1 -0
- package/dist/testing/fakes/cache.js +56 -0
- package/dist/testing/fakes/cache.js.map +1 -0
- package/dist/testing/fakes/event-bus.d.ts +17 -0
- package/dist/testing/fakes/event-bus.d.ts.map +1 -0
- package/dist/testing/fakes/event-bus.js +30 -0
- package/dist/testing/fakes/event-bus.js.map +1 -0
- package/dist/{server/kernel → testing/fakes}/job-queue.d.ts +9 -9
- package/dist/{server/kernel → testing/fakes}/job-queue.d.ts.map +1 -1
- package/dist/{server/kernel → testing/fakes}/job-queue.js +9 -9
- package/dist/{server/kernel → testing/fakes}/job-queue.js.map +1 -1
- package/dist/testing/fakes/rate-limiter.d.ts +11 -0
- package/dist/testing/fakes/rate-limiter.d.ts.map +1 -0
- package/dist/testing/fakes/rate-limiter.js +39 -0
- package/dist/testing/fakes/rate-limiter.js.map +1 -0
- package/dist/{server/kernel → testing/fakes}/realtime-authorizer.d.ts.map +1 -1
- package/dist/{server/kernel → testing/fakes}/realtime-authorizer.js.map +1 -1
- package/dist/{server/kernel → testing/fakes}/realtime-transport.d.ts.map +1 -1
- package/dist/{server/kernel → testing/fakes}/realtime-transport.js +3 -1
- package/dist/testing/fakes/realtime-transport.js.map +1 -0
- package/dist/testing/index.d.ts +7 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +11 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/wallet/__tests__/wallet.rate-limit.test.js +1 -1
- package/dist/wallet/__tests__/wallet.rate-limit.test.js.map +1 -1
- package/package.json +6 -1
- package/turbo-generators/src/templates/module/agents.hbs +16 -57
- package/dist/cms/__tests__/cms.cache.test.d.ts +0 -2
- package/dist/cms/__tests__/cms.cache.test.d.ts.map +0 -1
- package/dist/cms/__tests__/cms.cache.test.js.map +0 -1
- package/dist/server/kernel/__tests__/job-queue.test.d.ts.map +0 -1
- package/dist/server/kernel/__tests__/job-queue.test.js.map +0 -1
- package/dist/server/kernel/__tests__/realtime-authorizer.test.d.ts.map +0 -1
- package/dist/server/kernel/__tests__/realtime-transport.test.d.ts.map +0 -1
- package/dist/server/kernel/realtime-transport.js.map +0 -1
- /package/dist/{server/kernel → testing/fakes}/__tests__/job-queue.test.d.ts +0 -0
- /package/dist/{server/kernel → testing/fakes}/__tests__/realtime-authorizer.test.d.ts +0 -0
- /package/dist/{server/kernel → testing/fakes}/__tests__/realtime-authorizer.test.js +0 -0
- /package/dist/{server/kernel → testing/fakes}/__tests__/realtime-transport.test.d.ts +0 -0
- /package/dist/{server/kernel → testing/fakes}/__tests__/realtime-transport.test.js +0 -0
- /package/dist/{server/kernel → testing/fakes}/realtime-authorizer.d.ts +0 -0
- /package/dist/{server/kernel → testing/fakes}/realtime-authorizer.js +0 -0
- /package/dist/{server/kernel → testing/fakes}/realtime-transport.d.ts +0 -0
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
export type { RequestContext, RequestStorage } from './request-context.js';
|
|
2
2
|
export { requestStorage, getCurrentRequestContext, getCurrentTraceId, withRequestContext, } from './request-context.js';
|
|
3
3
|
export type { EventBus, EventHandler } from './event-bus.js';
|
|
4
|
-
export { EVENT_BUS,
|
|
5
|
-
export { InProcessJobQueue } from './job-queue.js';
|
|
4
|
+
export { EVENT_BUS, createEventBus } from './event-bus.js';
|
|
6
5
|
export { BullMqJobQueue } from './bullmq-job-queue.js';
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
6
|
+
export { makeRateLimitError, assertRateLimit } from './rate-limiter.js';
|
|
7
|
+
export { cached, invalidate } from './cache.js';
|
|
9
8
|
export { createRedisClient, type RedisClient } from './redis-client.js';
|
|
10
9
|
export { RedisCache } from './redis-cache.js';
|
|
11
10
|
export { RedisRateLimiter } from './redis-rate-limiter.js';
|
|
12
|
-
export {
|
|
13
|
-
export { SseClientAuthorizer, type SseClientAuthorizerOptions } from './realtime-authorizer.js';
|
|
11
|
+
export { RedisStreamsBroker } from './redis-streams-broker.js';
|
|
14
12
|
export { Container } from './container.js';
|
|
15
13
|
export type { Factory } from './container.js';
|
|
16
14
|
export { createLogger } from './logger.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/kernel/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/kernel/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACpE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,OAAO,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
export { requestStorage, getCurrentRequestContext, getCurrentTraceId, withRequestContext, } from './request-context.js';
|
|
2
|
-
export { EVENT_BUS,
|
|
3
|
-
export { InProcessJobQueue } from './job-queue.js';
|
|
2
|
+
export { EVENT_BUS, createEventBus } from './event-bus.js';
|
|
4
3
|
export { BullMqJobQueue } from './bullmq-job-queue.js';
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
4
|
+
export { makeRateLimitError, assertRateLimit } from './rate-limiter.js';
|
|
5
|
+
export { cached, invalidate } from './cache.js';
|
|
7
6
|
export { createRedisClient } from './redis-client.js';
|
|
8
7
|
export { RedisCache } from './redis-cache.js';
|
|
9
8
|
export { RedisRateLimiter } from './redis-rate-limiter.js';
|
|
10
|
-
export {
|
|
11
|
-
export { SseClientAuthorizer } from './realtime-authorizer.js';
|
|
9
|
+
export { RedisStreamsBroker } from './redis-streams-broker.js';
|
|
12
10
|
export { Container } from './container.js';
|
|
13
11
|
export { createLogger } from './logger.js';
|
|
14
12
|
export { setErrorReporter, reportError } from './error-reporter.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/kernel/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/kernel/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAoB,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGpE,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAG/D,iDAAiD;AACjD,OAAO,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import { ORPCError } from '@orpc/server';
|
|
2
|
-
import type { RateLimiterAdapter, RateLimitOptions
|
|
3
|
-
export declare class InProcessRateLimiter implements RateLimiterAdapter {
|
|
4
|
-
private readonly windows;
|
|
5
|
-
private readonly sweeper;
|
|
6
|
-
constructor();
|
|
7
|
-
consume(key: string, opts: RateLimitOptions): Promise<RateLimitResult>;
|
|
8
|
-
reset(key: string): Promise<void>;
|
|
9
|
-
private sweep;
|
|
10
|
-
close(): void;
|
|
11
|
-
}
|
|
2
|
+
import type { RateLimiterAdapter, RateLimitOptions } from '@openora/core/contracts';
|
|
12
3
|
/** A 429 (TOO_MANY_REQUESTS) error carrying the client-facing back-off hint in its data. */
|
|
13
4
|
export declare function makeRateLimitError(retryAfterMs: number): ORPCError<'TOO_MANY_REQUESTS', {
|
|
14
5
|
retryAfterMs: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../../src/server/kernel/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../../src/server/kernel/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEpF,4FAA4F;AAC5F,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,MAAM,GACnB,SAAS,CAAC,mBAAmB,EAAE;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAK1D;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAC7D,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,SAAS,EAC1C,GAAG,EAAE,CAAC,EACN,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,IAAI,CAAC,CAQf"}
|
|
@@ -1,42 +1,4 @@
|
|
|
1
1
|
import { ORPCError } from '@orpc/server';
|
|
2
|
-
const SWEEP_INTERVAL_MS = 60_000;
|
|
3
|
-
export class InProcessRateLimiter {
|
|
4
|
-
windows = new Map();
|
|
5
|
-
sweeper;
|
|
6
|
-
constructor() {
|
|
7
|
-
this.sweeper = setInterval(() => this.sweep(), SWEEP_INTERVAL_MS);
|
|
8
|
-
this.sweeper.unref?.();
|
|
9
|
-
}
|
|
10
|
-
consume(key, opts) {
|
|
11
|
-
const now = Date.now();
|
|
12
|
-
const existing = this.windows.get(key);
|
|
13
|
-
if (!existing || now >= existing.resetAt) {
|
|
14
|
-
this.windows.set(key, { count: 1, resetAt: now + opts.windowMs });
|
|
15
|
-
return Promise.resolve({ allowed: true, retryAfterMs: 0 });
|
|
16
|
-
}
|
|
17
|
-
if (existing.count < opts.limit) {
|
|
18
|
-
existing.count += 1;
|
|
19
|
-
return Promise.resolve({ allowed: true, retryAfterMs: 0 });
|
|
20
|
-
}
|
|
21
|
-
return Promise.resolve({ allowed: false, retryAfterMs: existing.resetAt - now });
|
|
22
|
-
}
|
|
23
|
-
reset(key) {
|
|
24
|
-
this.windows.delete(key);
|
|
25
|
-
return Promise.resolve();
|
|
26
|
-
}
|
|
27
|
-
sweep() {
|
|
28
|
-
const now = Date.now();
|
|
29
|
-
for (const [key, window] of this.windows) {
|
|
30
|
-
if (now >= window.resetAt) {
|
|
31
|
-
this.windows.delete(key);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
close() {
|
|
36
|
-
clearInterval(this.sweeper);
|
|
37
|
-
this.windows.clear();
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
2
|
/** A 429 (TOO_MANY_REQUESTS) error carrying the client-facing back-off hint in its data. */
|
|
41
3
|
export function makeRateLimitError(retryAfterMs) {
|
|
42
4
|
return new ORPCError('TOO_MANY_REQUESTS', {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../../src/server/kernel/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../../src/server/kernel/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,4FAA4F;AAC5F,MAAM,UAAU,kBAAkB,CAChC,YAAoB;IAEpB,OAAO,IAAI,SAAS,CAAC,mBAAmB,EAAE;QACxC,OAAO,EAAE,4CAA4C;QACrD,IAAI,EAAE,EAAE,YAAY,EAAE;KACvB,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAA0C,EAC1C,GAAM,EACN,IAAsB;IAEtB,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;IACT,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACnE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,kBAAkB,CAAC,YAAY,CAAC,CAAC;IACzC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { MessageBrokerAdapter, EventEnvelope, BrokerHandler, SubscribeOptions } from '@openora/core/contracts';
|
|
2
|
+
import type { RedisClient } from './redis-client.js';
|
|
3
|
+
type RedisStreamsBrokerOptions = {
|
|
4
|
+
serviceName: string;
|
|
5
|
+
startId?: '$' | '0';
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Durable `MESSAGE_BROKER` reference driver on Redis Streams, auto-bound by
|
|
9
|
+
* `createApp` when `REDIS_URL` is set (ADR-0030 - production is distributed-only,
|
|
10
|
+
* core ships no in-process default).
|
|
11
|
+
*
|
|
12
|
+
* Every `subscribe(topic, handler)` call for the same (topic, group) - default
|
|
13
|
+
* group is the deployment's `serviceName` - coalesces into ONE Redis consumer-group
|
|
14
|
+
* loop that fans out to every registered local handler, then XACKs once: an event
|
|
15
|
+
* is delivered to exactly one replica of the service (competing consumers across
|
|
16
|
+
* replicas via the shared group), and that replica runs every local handler for
|
|
17
|
+
* the topic (matches in-process fan-out semantics, but once per cluster, not once
|
|
18
|
+
* per replica). A distinct `SERVICE_NAME` per split service gives each service its
|
|
19
|
+
* own copy of every event; `createApp` requires one as soon as `SERVICE_MANIFEST`
|
|
20
|
+
* is set, since the group name is a durable identity and can't be derived from a
|
|
21
|
+
* module list that reorders and grows.
|
|
22
|
+
*
|
|
23
|
+
* Blocking `XREADGROUP` runs on a `client.duplicate()`d connection per loop, never
|
|
24
|
+
* on the shared client - `publish` (`XADD`, non-blocking) uses the shared client,
|
|
25
|
+
* so a blocked read never stalls the cache/rate-limiter commands sharing it.
|
|
26
|
+
* `publish` awaits the XADD and lets errors propagate (the `OutboxRelay` marks a
|
|
27
|
+
* row published only after this resolves - it must be reliable, not a silent
|
|
28
|
+
* no-op). Handler errors are guarded + logged, then the entry is XACKed regardless
|
|
29
|
+
* (matches `EventBus.on` - no per-handler retry; must-not-lose events use the
|
|
30
|
+
* outbox). A crash before XACK leaves the entry pending; `XAUTOCLAIM` periodically
|
|
31
|
+
* reclaims idle-pending entries so at-least-once delivery survives a replica crash.
|
|
32
|
+
*
|
|
33
|
+
* At-least-once holds from group creation onward, NOT before it. A group is created
|
|
34
|
+
* at `startId` (default `$`) on first subscribe and persists in Redis from then on,
|
|
35
|
+
* so restarts and outages are covered: the group keeps its cursor, undelivered
|
|
36
|
+
* entries wait in the stream, and pending ones are reclaimed. The one uncovered
|
|
37
|
+
* window is a group's FIRST-EVER creation - a service extracted after events were
|
|
38
|
+
* already flowing starts from `$` and never sees the backlog. `startId: '0'` closes
|
|
39
|
+
* that window by replaying everything still retained (up to STREAM_MAXLEN), at the
|
|
40
|
+
* cost of re-running every handler for those events: safe on a fresh deploy (empty
|
|
41
|
+
* streams), a duplicate-side-effect hazard for a late-added service, since core has
|
|
42
|
+
* no dedup layer - `EventBus.on` invokes handlers directly, and `eventId` is a key
|
|
43
|
+
* consumers must dedup on themselves.
|
|
44
|
+
*
|
|
45
|
+
* `orderingKey` is NOT honoured - Redis Streams have no partitioning, so per-key
|
|
46
|
+
* ordering across concurrent consumers isn't guaranteed (same footnote as
|
|
47
|
+
* `BullMqJobQueue`'s `orderingKey`).
|
|
48
|
+
*/
|
|
49
|
+
export declare class RedisStreamsBroker implements MessageBrokerAdapter {
|
|
50
|
+
private readonly client;
|
|
51
|
+
private readonly logger;
|
|
52
|
+
private readonly serviceName;
|
|
53
|
+
private readonly startId;
|
|
54
|
+
private readonly consumerId;
|
|
55
|
+
private readonly loops;
|
|
56
|
+
constructor(client: RedisClient, opts: RedisStreamsBrokerOptions);
|
|
57
|
+
publish(envelope: EventEnvelope): Promise<void>;
|
|
58
|
+
subscribe(topic: string, handler: BrokerHandler, options?: SubscribeOptions): () => void;
|
|
59
|
+
close(): Promise<void>;
|
|
60
|
+
private startLoop;
|
|
61
|
+
private reclaimPending;
|
|
62
|
+
private dispatch;
|
|
63
|
+
}
|
|
64
|
+
export {};
|
|
65
|
+
//# sourceMappingURL=redis-streams-broker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-streams-broker.d.ts","sourceRoot":"","sources":["../../../src/server/kernel/redis-streams-broker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EACb,aAAa,EACb,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA4BrD,KAAK,yBAAyB,GAAG;IAC/B,WAAW,EAAE,MAAM,CAAC;IAOpB,OAAO,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;CACrB,CAAC;AAgBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,qBAAa,kBAAmB,YAAW,oBAAoB;IAQ3D,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkC;IACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAY;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;IAC3C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmC;gBAGtC,MAAM,EAAE,WAAW,EACpC,IAAI,EAAE,yBAAyB;IAM3B,OAAO,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IASrD,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,IAAI;IAwBlF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B,OAAO,CAAC,SAAS;YA0EH,cAAc;YAyBd,QAAQ;CA6BvB"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { createLogger } from './logger.js';
|
|
3
|
+
const STREAM_PREFIX = 'oss:evt:';
|
|
4
|
+
const STREAM_MAXLEN = 10_000;
|
|
5
|
+
const READ_COUNT = 10;
|
|
6
|
+
const BLOCK_MS = 5_000;
|
|
7
|
+
const CLAIM_INTERVAL_MS = 30_000;
|
|
8
|
+
const CLAIM_MIN_IDLE_MS = 60_000;
|
|
9
|
+
const RETRY_DELAY_MS = 2_000;
|
|
10
|
+
function delay(ms) {
|
|
11
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
12
|
+
}
|
|
13
|
+
function streamKey(topic) {
|
|
14
|
+
return STREAM_PREFIX + topic;
|
|
15
|
+
}
|
|
16
|
+
function isBusyGroupError(err) {
|
|
17
|
+
return err instanceof Error && err.message.includes('BUSYGROUP');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Durable `MESSAGE_BROKER` reference driver on Redis Streams, auto-bound by
|
|
21
|
+
* `createApp` when `REDIS_URL` is set (ADR-0030 - production is distributed-only,
|
|
22
|
+
* core ships no in-process default).
|
|
23
|
+
*
|
|
24
|
+
* Every `subscribe(topic, handler)` call for the same (topic, group) - default
|
|
25
|
+
* group is the deployment's `serviceName` - coalesces into ONE Redis consumer-group
|
|
26
|
+
* loop that fans out to every registered local handler, then XACKs once: an event
|
|
27
|
+
* is delivered to exactly one replica of the service (competing consumers across
|
|
28
|
+
* replicas via the shared group), and that replica runs every local handler for
|
|
29
|
+
* the topic (matches in-process fan-out semantics, but once per cluster, not once
|
|
30
|
+
* per replica). A distinct `SERVICE_NAME` per split service gives each service its
|
|
31
|
+
* own copy of every event; `createApp` requires one as soon as `SERVICE_MANIFEST`
|
|
32
|
+
* is set, since the group name is a durable identity and can't be derived from a
|
|
33
|
+
* module list that reorders and grows.
|
|
34
|
+
*
|
|
35
|
+
* Blocking `XREADGROUP` runs on a `client.duplicate()`d connection per loop, never
|
|
36
|
+
* on the shared client - `publish` (`XADD`, non-blocking) uses the shared client,
|
|
37
|
+
* so a blocked read never stalls the cache/rate-limiter commands sharing it.
|
|
38
|
+
* `publish` awaits the XADD and lets errors propagate (the `OutboxRelay` marks a
|
|
39
|
+
* row published only after this resolves - it must be reliable, not a silent
|
|
40
|
+
* no-op). Handler errors are guarded + logged, then the entry is XACKed regardless
|
|
41
|
+
* (matches `EventBus.on` - no per-handler retry; must-not-lose events use the
|
|
42
|
+
* outbox). A crash before XACK leaves the entry pending; `XAUTOCLAIM` periodically
|
|
43
|
+
* reclaims idle-pending entries so at-least-once delivery survives a replica crash.
|
|
44
|
+
*
|
|
45
|
+
* At-least-once holds from group creation onward, NOT before it. A group is created
|
|
46
|
+
* at `startId` (default `$`) on first subscribe and persists in Redis from then on,
|
|
47
|
+
* so restarts and outages are covered: the group keeps its cursor, undelivered
|
|
48
|
+
* entries wait in the stream, and pending ones are reclaimed. The one uncovered
|
|
49
|
+
* window is a group's FIRST-EVER creation - a service extracted after events were
|
|
50
|
+
* already flowing starts from `$` and never sees the backlog. `startId: '0'` closes
|
|
51
|
+
* that window by replaying everything still retained (up to STREAM_MAXLEN), at the
|
|
52
|
+
* cost of re-running every handler for those events: safe on a fresh deploy (empty
|
|
53
|
+
* streams), a duplicate-side-effect hazard for a late-added service, since core has
|
|
54
|
+
* no dedup layer - `EventBus.on` invokes handlers directly, and `eventId` is a key
|
|
55
|
+
* consumers must dedup on themselves.
|
|
56
|
+
*
|
|
57
|
+
* `orderingKey` is NOT honoured - Redis Streams have no partitioning, so per-key
|
|
58
|
+
* ordering across concurrent consumers isn't guaranteed (same footnote as
|
|
59
|
+
* `BullMqJobQueue`'s `orderingKey`).
|
|
60
|
+
*/
|
|
61
|
+
export class RedisStreamsBroker {
|
|
62
|
+
client;
|
|
63
|
+
logger = createLogger('message-broker');
|
|
64
|
+
serviceName;
|
|
65
|
+
startId;
|
|
66
|
+
consumerId = randomUUID();
|
|
67
|
+
loops = new Map();
|
|
68
|
+
constructor(client, opts) {
|
|
69
|
+
this.client = client;
|
|
70
|
+
this.serviceName = opts.serviceName;
|
|
71
|
+
this.startId = opts.startId ?? '$';
|
|
72
|
+
}
|
|
73
|
+
async publish(envelope) {
|
|
74
|
+
await this.client.xAdd(streamKey(envelope.topic), '*', { data: JSON.stringify(envelope) }, { TRIM: { strategy: 'MAXLEN', strategyModifier: '~', threshold: STREAM_MAXLEN } });
|
|
75
|
+
}
|
|
76
|
+
subscribe(topic, handler, options) {
|
|
77
|
+
const group = options?.consumerGroup ?? this.serviceName;
|
|
78
|
+
const key = streamKey(topic);
|
|
79
|
+
const loopKey = `${key}::${group}`;
|
|
80
|
+
let loop = this.loops.get(loopKey);
|
|
81
|
+
if (!loop) {
|
|
82
|
+
loop = this.startLoop(key, group);
|
|
83
|
+
this.loops.set(loopKey, loop);
|
|
84
|
+
}
|
|
85
|
+
loop.handlers.add(handler);
|
|
86
|
+
return () => {
|
|
87
|
+
const current = this.loops.get(loopKey);
|
|
88
|
+
if (!current || !current.handlers.delete(handler)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (current.handlers.size === 0) {
|
|
92
|
+
this.loops.delete(loopKey);
|
|
93
|
+
current.stop();
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async close() {
|
|
98
|
+
const loops = [...this.loops.values()];
|
|
99
|
+
this.loops.clear();
|
|
100
|
+
for (const loop of loops) {
|
|
101
|
+
loop.stop();
|
|
102
|
+
}
|
|
103
|
+
await Promise.allSettled(loops.map((loop) => loop.done));
|
|
104
|
+
}
|
|
105
|
+
startLoop(key, group) {
|
|
106
|
+
const handlers = new Set();
|
|
107
|
+
const reader = this.client.duplicate();
|
|
108
|
+
reader.on('error', (err) => this.logger.error({ err, key, group }, 'stream reader connection error'));
|
|
109
|
+
let stopped = false;
|
|
110
|
+
let lastClaimAt = 0;
|
|
111
|
+
const stop = () => {
|
|
112
|
+
stopped = true;
|
|
113
|
+
// Rejects the in-flight blocking XREADGROUP (if any) immediately, instead of
|
|
114
|
+
// waiting up to BLOCK_MS for it to time out on its own.
|
|
115
|
+
reader.destroy();
|
|
116
|
+
};
|
|
117
|
+
// `ready` gates connecting + group creation, and stays false until BOTH have
|
|
118
|
+
// succeeded - so a Redis outage at loop start retries the whole handshake with
|
|
119
|
+
// backoff rather than permanently abandoning this (topic, group)'s consumption.
|
|
120
|
+
// It is NOT reset on a later error: node-redis owns reconnection from there on
|
|
121
|
+
// (socket.reconnectStrategy re-establishes the connection and re-queues the
|
|
122
|
+
// blocked XREADGROUP), and the group already exists, so there is nothing to redo.
|
|
123
|
+
// Both steps are idempotent regardless - `connect()` throws 'Socket already
|
|
124
|
+
// opened' once the socket is open, hence the isOpen guard, and a re-created group
|
|
125
|
+
// comes back BUSYGROUP, which is tolerated below.
|
|
126
|
+
const done = (async () => {
|
|
127
|
+
let ready = false;
|
|
128
|
+
while (!stopped) {
|
|
129
|
+
try {
|
|
130
|
+
if (!ready) {
|
|
131
|
+
if (!reader.isOpen) {
|
|
132
|
+
await reader.connect();
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
await reader.xGroupCreate(key, group, this.startId, { MKSTREAM: true });
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
if (!isBusyGroupError(err)) {
|
|
139
|
+
throw err;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
ready = true;
|
|
143
|
+
}
|
|
144
|
+
if (Date.now() - lastClaimAt > CLAIM_INTERVAL_MS) {
|
|
145
|
+
lastClaimAt = Date.now();
|
|
146
|
+
await this.reclaimPending(reader, key, group, handlers);
|
|
147
|
+
}
|
|
148
|
+
const reply = (await reader.xReadGroup(group, this.consumerId, { key, id: '>' }, { COUNT: READ_COUNT, BLOCK: BLOCK_MS }));
|
|
149
|
+
if (!reply) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
for (const stream of reply) {
|
|
153
|
+
for (const message of stream.messages) {
|
|
154
|
+
await this.dispatch(reader, key, group, message, handlers);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
if (stopped) {
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
this.logger.error({ err, key, group }, 'stream loop error, retrying');
|
|
163
|
+
await delay(RETRY_DELAY_MS);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
})();
|
|
167
|
+
return { handlers, stop, done };
|
|
168
|
+
}
|
|
169
|
+
async reclaimPending(reader, key, group, handlers) {
|
|
170
|
+
try {
|
|
171
|
+
const claimed = (await reader.xAutoClaim(key, group, this.consumerId, CLAIM_MIN_IDLE_MS, '0-0', { COUNT: READ_COUNT }));
|
|
172
|
+
for (const message of claimed.messages) {
|
|
173
|
+
if (message) {
|
|
174
|
+
await this.dispatch(reader, key, group, message, handlers);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
this.logger.error({ err, key, group }, 'stream reclaim failed');
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async dispatch(reader, key, group, message, handlers) {
|
|
183
|
+
const raw = message.message['data'];
|
|
184
|
+
if (raw === undefined) {
|
|
185
|
+
await reader.xAck(key, group, message.id);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
let envelope;
|
|
189
|
+
try {
|
|
190
|
+
envelope = JSON.parse(raw);
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
this.logger.error({ err, key, group, id: message.id }, 'stream envelope parse failed');
|
|
194
|
+
await reader.xAck(key, group, message.id);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
for (const fn of handlers) {
|
|
198
|
+
try {
|
|
199
|
+
await fn(envelope);
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
this.logger.error({ err, topic: envelope.topic }, 'message broker handler threw');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
await reader.xAck(key, group, message.id);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=redis-streams-broker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-streams-broker.js","sourceRoot":"","sources":["../../../src/server/kernel/redis-streams-broker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-app.test.d.ts","sourceRoot":"","sources":["../../../../src/server/runtime/__tests__/create-app.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import { MESSAGE_BROKER, JOB_QUEUE, CACHE, RATE_LIMITER } from '@openora/core/contracts';
|
|
3
|
+
import { InMemoryBroker, InProcessJobQueue, InProcessCache, InProcessRateLimiter, } from '@openora/core/testing';
|
|
4
|
+
import { createLogger } from '../../kernel/index.js';
|
|
5
|
+
import { createApp } from '../create-app.js';
|
|
6
|
+
// A syntactically valid but unreachable DB url - fine here because nothing in this
|
|
7
|
+
// suite ever runs a query: DrizzleService's pg.Pool connects lazily, and neither
|
|
8
|
+
// health.ping nor better-auth's getSession() touch the DB when the request carries
|
|
9
|
+
// no session cookie (getSessionFromCtx short-circuits on a missing cookie).
|
|
10
|
+
const DUMMY_DATABASE_URL = 'postgres://test:test@127.0.0.1:1/create_app_test';
|
|
11
|
+
describe('createApp - distributed-only durable seams (ADR-0030)', () => {
|
|
12
|
+
it('throws a clear, actionable error when no durable seam is bound', async () => {
|
|
13
|
+
await expect(createApp({ plugins: [], databaseUrl: DUMMY_DATABASE_URL, openapi: { enabled: false } })).rejects.toThrow(/MESSAGE_BROKER.*JOB_QUEUE.*CACHE.*RATE_LIMITER/s);
|
|
14
|
+
});
|
|
15
|
+
it('boots and serves once the fakes are bound via configure', async () => {
|
|
16
|
+
const created = await createApp({
|
|
17
|
+
plugins: [],
|
|
18
|
+
databaseUrl: DUMMY_DATABASE_URL,
|
|
19
|
+
openapi: { enabled: false },
|
|
20
|
+
configure(container) {
|
|
21
|
+
container.register(MESSAGE_BROKER, () => new InMemoryBroker());
|
|
22
|
+
container.register(JOB_QUEUE, () => new InProcessJobQueue(createLogger('job-queue')));
|
|
23
|
+
container.register(CACHE, () => new InProcessCache());
|
|
24
|
+
container.register(RATE_LIMITER, () => new InProcessRateLimiter());
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
const res = await created.app.request('/health');
|
|
28
|
+
expect(res.status).toBe(200);
|
|
29
|
+
expect(await res.json()).toMatchObject({ status: 'ok' });
|
|
30
|
+
await created.close();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('createApp - service name for the Redis Streams consumer group', () => {
|
|
34
|
+
const saved = { redis: process.env['REDIS_URL'], manifest: process.env['SERVICE_MANIFEST'] };
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
// Restore rather than delete: a bare delete would strip a REDIS_URL the rest of
|
|
37
|
+
// the suite (or a CI service container) legitimately set.
|
|
38
|
+
for (const [key, value] of [
|
|
39
|
+
['REDIS_URL', saved.redis],
|
|
40
|
+
['SERVICE_MANIFEST', saved.manifest],
|
|
41
|
+
]) {
|
|
42
|
+
if (value === undefined) {
|
|
43
|
+
delete process.env[key];
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
process.env[key] = value;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
it('refuses to boot a split service that has no SERVICE_NAME of its own', async () => {
|
|
51
|
+
// The consumer group is a durable identity, so it can never be derived from
|
|
52
|
+
// SERVICE_MANIFEST - that is a list of module ids ('wallet,iam') which reorders
|
|
53
|
+
// and grows. Throws before any Redis client is opened.
|
|
54
|
+
process.env['REDIS_URL'] = 'redis://127.0.0.1:1';
|
|
55
|
+
process.env['SERVICE_MANIFEST'] = 'wallet,iam';
|
|
56
|
+
delete process.env['SERVICE_NAME'];
|
|
57
|
+
await expect(createApp({ plugins: [], databaseUrl: DUMMY_DATABASE_URL, openapi: { enabled: false } })).rejects.toThrow(/SERVICE_MANIFEST is set but SERVICE_NAME is not/);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=create-app.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-app.test.js","sourceRoot":"","sources":["../../../../src/server/runtime/__tests__/create-app.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type AnyToken } from '@openora/core/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* Production is distributed-only (ADR-0030, superseding ADR-0010/0014/0016/0028):
|
|
4
|
+
* the in-process seam impls no longer ship on the production path, so every
|
|
5
|
+
* always-needed seam must resolve to a durable binding before `createApp` finishes
|
|
6
|
+
* booting. Call this AFTER `loadPlugins` + `configure` so a `REDIS_URL` auto-bind or
|
|
7
|
+
* an overlay's own registration has already won (Container is last-registration-wins).
|
|
8
|
+
* Modeled on `assertSealedServicesBound` (`compliance/assert.ts`) - same `has(token)`
|
|
9
|
+
* shape, same "list every gap, don't stop at the first" style.
|
|
10
|
+
*
|
|
11
|
+
* `REALTIME_TRANSPORT` is deliberately excluded: it stays lazy and throws on first
|
|
12
|
+
* use, since not every deployment serves realtime traffic.
|
|
13
|
+
*/
|
|
14
|
+
export type DurableSeamContainerView = {
|
|
15
|
+
has(token: AnyToken<unknown>): boolean;
|
|
16
|
+
};
|
|
17
|
+
export declare function assertDurableSeamsBound(container: DurableSeamContainerView): void;
|
|
18
|
+
//# sourceMappingURL=assert-durable-seams.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assert-durable-seams.d.ts","sourceRoot":"","sources":["../../../src/server/runtime/assert-durable-seams.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,QAAQ,EACd,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CACxC,CAAC;AA2BF,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,wBAAwB,GAAG,IAAI,CASjF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { MESSAGE_BROKER, JOB_QUEUE, CACHE, RATE_LIMITER, } from '@openora/core/contracts';
|
|
2
|
+
const REQUIRED_DURABLE_SEAMS = [
|
|
3
|
+
{
|
|
4
|
+
token: MESSAGE_BROKER,
|
|
5
|
+
name: 'MESSAGE_BROKER',
|
|
6
|
+
fix: 'set REDIS_URL to auto-bind RedisStreamsBroker, or bind a MessageBrokerAdapter overlay (eg RabbitMQ/Kafka) via ctx.provide(MESSAGE_BROKER, ...)',
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
token: JOB_QUEUE,
|
|
10
|
+
name: 'JOB_QUEUE',
|
|
11
|
+
fix: 'set REDIS_URL to auto-bind BullMqJobQueue, or bind a JOB_QUEUE overlay directly',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
token: CACHE,
|
|
15
|
+
name: 'CACHE',
|
|
16
|
+
fix: 'set REDIS_URL to auto-bind RedisCache, or bind a CACHE overlay directly',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
token: RATE_LIMITER,
|
|
20
|
+
name: 'RATE_LIMITER',
|
|
21
|
+
fix: 'set REDIS_URL to auto-bind RedisRateLimiter, or bind a RATE_LIMITER overlay directly',
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
export function assertDurableSeamsBound(container) {
|
|
25
|
+
const missing = REQUIRED_DURABLE_SEAMS.filter((seam) => !container.has(seam.token));
|
|
26
|
+
if (missing.length === 0) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
throw new Error(`[create-app] production is distributed-only - the following seams have no durable binding:\n` +
|
|
30
|
+
missing.map((seam) => ` - ${seam.name}: ${seam.fix}`).join('\n'));
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=assert-durable-seams.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assert-durable-seams.js","sourceRoot":"","sources":["../../../src/server/runtime/assert-durable-seams.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,SAAS,EACT,KAAK,EACL,YAAY,GAEb,MAAM,yBAAyB,CAAC;AAoBjC,MAAM,sBAAsB,GAA+B;IACzD;QACE,KAAK,EAAE,cAAc;QACrB,IAAI,EAAE,gBAAgB;QACtB,GAAG,EAAE,gJAAgJ;KACtJ;IACD;QACE,KAAK,EAAE,SAAS;QAChB,IAAI,EAAE,WAAW;QACjB,GAAG,EAAE,iFAAiF;KACvF;IACD;QACE,KAAK,EAAE,KAAK;QACZ,IAAI,EAAE,OAAO;QACb,GAAG,EAAE,yEAAyE;KAC/E;IACD;QACE,KAAK,EAAE,YAAY;QACnB,IAAI,EAAE,cAAc;QACpB,GAAG,EAAE,sFAAsF;KAC5F;CACF,CAAC;AAEF,MAAM,UAAU,uBAAuB,CAAC,SAAmC;IACzE,MAAM,OAAO,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACpF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;IACT,CAAC;IACD,MAAM,IAAI,KAAK,CACb,8FAA8F;QAC5F,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CACpE,CAAC;AACJ,CAAC"}
|
|
@@ -38,15 +38,21 @@ export type CreatedApp = {
|
|
|
38
38
|
};
|
|
39
39
|
/**
|
|
40
40
|
* The single composition root: wires the DI container, boots every plugin in
|
|
41
|
-
* `config.plugins`, and mounts the resulting oRPC router on a Hono app.
|
|
42
|
-
*
|
|
43
|
-
* `RATE_LIMITER`/`CACHE` to their
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
41
|
+
* `config.plugins`, and mounts the resulting oRPC router on a Hono app.
|
|
42
|
+
* Production is distributed-only (ADR-0030, superseding ADR-0010/0014/0016/0028):
|
|
43
|
+
* `REDIS_URL` auto-binds `JOB_QUEUE`/`RATE_LIMITER`/`CACHE`/`MESSAGE_BROKER` to their
|
|
44
|
+
* Redis-backed drivers - the broker being the `RedisStreamsBroker` reference driver,
|
|
45
|
+
* which an overlay (RabbitMQ/Kafka) or a test's `configure` callback can replace.
|
|
46
|
+
* `assertDurableSeamsBound` runs right after
|
|
47
|
+
* plugins + `configure` and throws a clear, actionable error listing every
|
|
48
|
+
* always-needed seam that still has no binding - it never falls back to an
|
|
49
|
+
* in-process impl. `REALTIME_TRANSPORT` stays lazy (throws on first use) since not
|
|
50
|
+
* every deployment serves realtime traffic. `OUTBOX_ENABLED` (or
|
|
51
|
+
* `AMQP_URL`/`RABBITMQ_URL`) enables the transactional outbox relay.
|
|
52
|
+
* `container.dispose()` (via `close()`) runs disposers in REVERSE registration
|
|
53
|
+
* order, so DRIZZLE is registered before JOB_QUEUE to guarantee workers drain
|
|
54
|
+
* before the DB closes. A router namespace registered by more than one plugin
|
|
55
|
+
* throws at boot, not at request time.
|
|
50
56
|
*/
|
|
51
57
|
export declare function createApp(config: CreateAppConfig): Promise<CreatedApp>;
|
|
52
58
|
//# sourceMappingURL=create-app.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-app.d.ts","sourceRoot":"","sources":["../../../src/server/runtime/create-app.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAO5B,OAAO,EACL,SAAS,
|
|
1
|
+
{"version":3,"file":"create-app.d.ts","sourceRoot":"","sources":["../../../src/server/runtime/create-app.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAO5B,OAAO,EACL,SAAS,EAYV,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAWL,KAAK,aAAa,EAEnB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AA8CxE,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,WAAW,EAAE,CAAC;IAOvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAErC,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,IAAI,CAAC,EAAE,OAAO,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;KAAE,CAAC;IAEjD,WAAW,CAAC,EAAE,MAAM,CAAC;IAKrB,QAAQ,CAAC,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC;IAE/B,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,IAAI,CAAC,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IAEF,OAAO,CAAC,EAAE,aAAa,CAAC;IAIxB,SAAS,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,CAAC;IAEjE,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3D,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,IAAI,CAAC;IACV,SAAS,EAAE,SAAS,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,eAAe,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAqDF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAgR5E"}
|