@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.
Files changed (136) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/audit/__tests__/audit.service.test.js +24 -1
  3. package/dist/audit/__tests__/audit.service.test.js.map +1 -1
  4. package/dist/audit/contract/index.d.ts +8 -8
  5. package/dist/audit/contract/index.js +2 -2
  6. package/dist/audit/contract/index.js.map +1 -1
  7. package/dist/audit/router/index.d.ts +4 -4
  8. package/dist/audit/service/audit.service.d.ts +2 -0
  9. package/dist/audit/service/audit.service.d.ts.map +1 -1
  10. package/dist/audit/service/audit.service.js +9 -1
  11. package/dist/audit/service/audit.service.js.map +1 -1
  12. package/dist/casino/lobby/__tests__/lobby.cache.test.js +1 -1
  13. package/dist/casino/lobby/__tests__/lobby.cache.test.js.map +1 -1
  14. package/dist/cms/__tests__/cms.reads.test.d.ts +2 -0
  15. package/dist/cms/__tests__/cms.reads.test.d.ts.map +1 -0
  16. package/dist/cms/__tests__/{cms.cache.test.js → cms.reads.test.js} +2 -2
  17. package/dist/cms/__tests__/cms.reads.test.js.map +1 -0
  18. package/dist/cms/__tests__/cms.router.test.js +25 -22
  19. package/dist/cms/__tests__/cms.router.test.js.map +1 -1
  20. package/dist/contracts/schemas/events.d.ts +1 -0
  21. package/dist/contracts/schemas/events.d.ts.map +1 -1
  22. package/dist/contracts/schemas/events.js +2 -1
  23. package/dist/contracts/schemas/events.js.map +1 -1
  24. package/dist/engagement/chat/__tests__/chat.service.test.js +1 -1
  25. package/dist/engagement/chat/__tests__/chat.service.test.js.map +1 -1
  26. package/dist/iam/__tests__/iam.service.test.js +4 -3
  27. package/dist/iam/__tests__/iam.service.test.js.map +1 -1
  28. package/dist/pam/identity/__tests__/identity.rate-limit.test.js +4 -3
  29. package/dist/pam/identity/__tests__/identity.rate-limit.test.js.map +1 -1
  30. package/dist/pam/identity/__tests__/identity.service.test.js +6 -1
  31. package/dist/pam/identity/__tests__/identity.service.test.js.map +1 -1
  32. package/dist/pam/identity/service/identity.service.d.ts.map +1 -1
  33. package/dist/pam/identity/service/identity.service.js +1 -0
  34. package/dist/pam/identity/service/identity.service.js.map +1 -1
  35. package/dist/server/kernel/__tests__/cache.test.js +11 -57
  36. package/dist/server/kernel/__tests__/cache.test.js.map +1 -1
  37. package/dist/server/kernel/__tests__/event-bus.test.js +14 -1
  38. package/dist/server/kernel/__tests__/event-bus.test.js.map +1 -1
  39. package/dist/server/kernel/__tests__/rate-limiter.test.js +3 -45
  40. package/dist/server/kernel/__tests__/rate-limiter.test.js.map +1 -1
  41. package/dist/server/kernel/__tests__/redis-streams-broker.test.d.ts +2 -0
  42. package/dist/server/kernel/__tests__/redis-streams-broker.test.d.ts.map +1 -0
  43. package/dist/server/kernel/__tests__/redis-streams-broker.test.js +293 -0
  44. package/dist/server/kernel/__tests__/redis-streams-broker.test.js.map +1 -0
  45. package/dist/server/kernel/cache.d.ts +0 -12
  46. package/dist/server/kernel/cache.d.ts.map +1 -1
  47. package/dist/server/kernel/cache.js +0 -55
  48. package/dist/server/kernel/cache.js.map +1 -1
  49. package/dist/server/kernel/event-bus.d.ts +1 -16
  50. package/dist/server/kernel/event-bus.d.ts.map +1 -1
  51. package/dist/server/kernel/event-bus.js +3 -32
  52. package/dist/server/kernel/event-bus.js.map +1 -1
  53. package/dist/server/kernel/index.d.ts +4 -6
  54. package/dist/server/kernel/index.d.ts.map +1 -1
  55. package/dist/server/kernel/index.js +4 -6
  56. package/dist/server/kernel/index.js.map +1 -1
  57. package/dist/server/kernel/rate-limiter.d.ts +1 -10
  58. package/dist/server/kernel/rate-limiter.d.ts.map +1 -1
  59. package/dist/server/kernel/rate-limiter.js +0 -38
  60. package/dist/server/kernel/rate-limiter.js.map +1 -1
  61. package/dist/server/kernel/redis-streams-broker.d.ts +65 -0
  62. package/dist/server/kernel/redis-streams-broker.d.ts.map +1 -0
  63. package/dist/server/kernel/redis-streams-broker.js +208 -0
  64. package/dist/server/kernel/redis-streams-broker.js.map +1 -0
  65. package/dist/server/runtime/__tests__/create-app.test.d.ts +2 -0
  66. package/dist/server/runtime/__tests__/create-app.test.d.ts.map +1 -0
  67. package/dist/server/runtime/__tests__/create-app.test.js +60 -0
  68. package/dist/server/runtime/__tests__/create-app.test.js.map +1 -0
  69. package/dist/server/runtime/assert-durable-seams.d.ts +18 -0
  70. package/dist/server/runtime/assert-durable-seams.d.ts.map +1 -0
  71. package/dist/server/runtime/assert-durable-seams.js +32 -0
  72. package/dist/server/runtime/assert-durable-seams.js.map +1 -0
  73. package/dist/server/runtime/create-app.d.ts +15 -9
  74. package/dist/server/runtime/create-app.d.ts.map +1 -1
  75. package/dist/server/runtime/create-app.js +61 -49
  76. package/dist/server/runtime/create-app.js.map +1 -1
  77. package/dist/testing/fakes/__tests__/cache.test.d.ts +2 -0
  78. package/dist/testing/fakes/__tests__/cache.test.d.ts.map +1 -0
  79. package/dist/testing/fakes/__tests__/cache.test.js +89 -0
  80. package/dist/testing/fakes/__tests__/cache.test.js.map +1 -0
  81. package/dist/testing/fakes/__tests__/job-queue.test.d.ts.map +1 -0
  82. package/dist/{server/kernel → testing/fakes}/__tests__/job-queue.test.js +1 -1
  83. package/dist/testing/fakes/__tests__/job-queue.test.js.map +1 -0
  84. package/dist/testing/fakes/__tests__/rate-limiter.test.d.ts +2 -0
  85. package/dist/testing/fakes/__tests__/rate-limiter.test.d.ts.map +1 -0
  86. package/dist/testing/fakes/__tests__/rate-limiter.test.js +83 -0
  87. package/dist/testing/fakes/__tests__/rate-limiter.test.js.map +1 -0
  88. package/dist/testing/fakes/__tests__/realtime-authorizer.test.d.ts.map +1 -0
  89. package/dist/{server/kernel → testing/fakes}/__tests__/realtime-authorizer.test.js.map +1 -1
  90. package/dist/testing/fakes/__tests__/realtime-transport.test.d.ts.map +1 -0
  91. package/dist/{server/kernel → testing/fakes}/__tests__/realtime-transport.test.js.map +1 -1
  92. package/dist/testing/fakes/cache.d.ts +14 -0
  93. package/dist/testing/fakes/cache.d.ts.map +1 -0
  94. package/dist/testing/fakes/cache.js +56 -0
  95. package/dist/testing/fakes/cache.js.map +1 -0
  96. package/dist/testing/fakes/event-bus.d.ts +17 -0
  97. package/dist/testing/fakes/event-bus.d.ts.map +1 -0
  98. package/dist/testing/fakes/event-bus.js +30 -0
  99. package/dist/testing/fakes/event-bus.js.map +1 -0
  100. package/dist/{server/kernel → testing/fakes}/job-queue.d.ts +9 -9
  101. package/dist/{server/kernel → testing/fakes}/job-queue.d.ts.map +1 -1
  102. package/dist/{server/kernel → testing/fakes}/job-queue.js +9 -9
  103. package/dist/{server/kernel → testing/fakes}/job-queue.js.map +1 -1
  104. package/dist/testing/fakes/rate-limiter.d.ts +11 -0
  105. package/dist/testing/fakes/rate-limiter.d.ts.map +1 -0
  106. package/dist/testing/fakes/rate-limiter.js +39 -0
  107. package/dist/testing/fakes/rate-limiter.js.map +1 -0
  108. package/dist/{server/kernel → testing/fakes}/realtime-authorizer.d.ts.map +1 -1
  109. package/dist/{server/kernel → testing/fakes}/realtime-authorizer.js.map +1 -1
  110. package/dist/{server/kernel → testing/fakes}/realtime-transport.d.ts.map +1 -1
  111. package/dist/{server/kernel → testing/fakes}/realtime-transport.js +3 -1
  112. package/dist/testing/fakes/realtime-transport.js.map +1 -0
  113. package/dist/testing/index.d.ts +7 -0
  114. package/dist/testing/index.d.ts.map +1 -0
  115. package/dist/testing/index.js +11 -0
  116. package/dist/testing/index.js.map +1 -0
  117. package/dist/wallet/__tests__/wallet.rate-limit.test.js +1 -1
  118. package/dist/wallet/__tests__/wallet.rate-limit.test.js.map +1 -1
  119. package/package.json +6 -1
  120. package/turbo-generators/src/templates/module/agents.hbs +16 -57
  121. package/dist/cms/__tests__/cms.cache.test.d.ts +0 -2
  122. package/dist/cms/__tests__/cms.cache.test.d.ts.map +0 -1
  123. package/dist/cms/__tests__/cms.cache.test.js.map +0 -1
  124. package/dist/server/kernel/__tests__/job-queue.test.d.ts.map +0 -1
  125. package/dist/server/kernel/__tests__/job-queue.test.js.map +0 -1
  126. package/dist/server/kernel/__tests__/realtime-authorizer.test.d.ts.map +0 -1
  127. package/dist/server/kernel/__tests__/realtime-transport.test.d.ts.map +0 -1
  128. package/dist/server/kernel/realtime-transport.js.map +0 -1
  129. /package/dist/{server/kernel → testing/fakes}/__tests__/job-queue.test.d.ts +0 -0
  130. /package/dist/{server/kernel → testing/fakes}/__tests__/realtime-authorizer.test.d.ts +0 -0
  131. /package/dist/{server/kernel → testing/fakes}/__tests__/realtime-authorizer.test.js +0 -0
  132. /package/dist/{server/kernel → testing/fakes}/__tests__/realtime-transport.test.d.ts +0 -0
  133. /package/dist/{server/kernel → testing/fakes}/__tests__/realtime-transport.test.js +0 -0
  134. /package/dist/{server/kernel → testing/fakes}/realtime-authorizer.d.ts +0 -0
  135. /package/dist/{server/kernel → testing/fakes}/realtime-authorizer.js +0 -0
  136. /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, InMemoryBroker, createEventBus } from './event-bus.js';
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 { InProcessRateLimiter, makeRateLimitError, assertRateLimit } from './rate-limiter.js';
8
- export { InProcessCache, cached, invalidate } from './cache.js';
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 { InProcessRealtimeTransport } from './realtime-transport.js';
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,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAChE,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,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,KAAK,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAEhG,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
+ {"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, InMemoryBroker, createEventBus } from './event-bus.js';
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 { InProcessRateLimiter, makeRateLimitError, assertRateLimit } from './rate-limiter.js';
6
- export { InProcessCache, cached, invalidate } from './cache.js';
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 { InProcessRealtimeTransport } from './realtime-transport.js';
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,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAChE,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,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAmC,MAAM,0BAA0B,CAAC;AAEhG,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
+ {"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, RateLimitResult } from '@openora/core/contracts';
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,EACV,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EAChB,MAAM,yBAAyB,CAAC;AAejC,qBAAa,oBAAqB,YAAW,kBAAkB;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;;IAOzD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAiBtE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjC,OAAO,CAAC,KAAK;IASb,KAAK,IAAI,IAAI;CAId;AAED,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
+ {"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;AAkBzC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAEjC,MAAM,OAAO,oBAAoB;IACd,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IACpC,OAAO,CAAiC;IAEzD;QACE,IAAI,CAAC,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,iBAAiB,CAAC,CAAC;QAClE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;IACzB,CAAC;IAED,OAAO,CAAC,GAAW,EAAE,IAAsB;QACzC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEvC,IAAI,CAAC,QAAQ,IAAI,GAAG,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACzC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YAClE,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7D,CAAC;QAED,IAAI,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YAChC,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;YACpB,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7D,CAAC;QAED,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,CAAC;IACnF,CAAC;IAED,KAAK,CAAC,GAAW;QACf,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACzB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAEO,KAAK;QACX,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACzC,IAAI,GAAG,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC1B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK;QACH,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5B,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;CACF;AAED,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"}
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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=create-app.test.d.ts.map
@@ -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":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACzF,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,mFAAmF;AACnF,iFAAiF;AACjF,mFAAmF;AACnF,4EAA4E;AAC5E,MAAM,kBAAkB,GAAG,kDAAkD,CAAC;AAE9E,QAAQ,CAAC,uDAAuD,EAAE,GAAG,EAAE;IACrE,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,MAAM,CACV,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,kBAAkB,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CACzF,CAAC,OAAO,CAAC,OAAO,CAAC,iDAAiD,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC;YAC9B,OAAO,EAAE,EAAE;YACX,WAAW,EAAE,kBAAkB;YAC/B,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;YAC3B,SAAS,CAAC,SAAS;gBACjB,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,cAAc,EAAE,CAAC,CAAC;gBAC/D,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,iBAAiB,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;gBACtF,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,cAAc,EAAE,CAAC,CAAC;gBACtD,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;YACrE,CAAC;SACF,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACjD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAEzD,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,+DAA+D,EAAE,GAAG,EAAE;IAC7E,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC;IAE7F,SAAS,CAAC,GAAG,EAAE;QACb,gFAAgF;QAChF,0DAA0D;QAC1D,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI;YACzB,CAAC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC;YAC1B,CAAC,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC;SAC5B,EAAE,CAAC;YACX,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC1B,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACnF,4EAA4E;QAC5E,gFAAgF;QAChF,uDAAuD;QACvD,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,qBAAqB,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,GAAG,YAAY,CAAC;QAC/C,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAEnC,MAAM,MAAM,CACV,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,kBAAkB,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CACzF,CAAC,OAAO,CAAC,OAAO,CAAC,iDAAiD,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -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. Seam
42
- * defaults auto-upgrade when env vars are set - `REDIS_URL` binds `JOB_QUEUE`/
43
- * `RATE_LIMITER`/`CACHE` to their Redis-backed drivers, `OUTBOX_ENABLED` (or
44
- * `AMQP_URL`/`RABBITMQ_URL`) enables the transactional outbox relay - otherwise
45
- * every seam stays in-process (zero deps for dev/test). `container.dispose()`
46
- * (via `close()`) runs disposers in REVERSE registration order, so DRIZZLE is
47
- * registered before JOB_QUEUE to guarantee workers drain before the DB closes.
48
- * A router namespace registered by more than one plugin throws at boot, not at
49
- * request time.
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,EAiBV,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAaL,KAAK,aAAa,EAEnB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAsBxE,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;;;;;;;;;;;GAWG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CA6R5E"}
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"}