@eventsdk/sdk 0.0.0-stage → 0.3.1

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 (159) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +9 -0
  3. package/dist/compatibility-http/bridge/receive.d.ts +3 -0
  4. package/dist/compatibility-http/bridge/types.d.ts +44 -0
  5. package/dist/compatibility-http/index.d.ts +4 -0
  6. package/dist/compatibility-http/index.js +1 -0
  7. package/dist/compatibility-http/policy/validate-policy.d.ts +3 -0
  8. package/dist/conformance/admission/harness.d.ts +20 -0
  9. package/dist/conformance/admission/run-suite.d.ts +2 -0
  10. package/dist/conformance/consumer/harness.d.ts +24 -0
  11. package/dist/conformance/consumer/run-apply-suite.d.ts +2 -0
  12. package/dist/conformance/consumer/run-worker-suite.d.ts +2 -0
  13. package/dist/conformance/fanout/harness.d.ts +39 -0
  14. package/dist/conformance/fanout/run-suite.d.ts +2 -0
  15. package/dist/conformance/gap/harness.d.ts +19 -0
  16. package/dist/conformance/gap/run-suite.d.ts +2 -0
  17. package/dist/conformance/index.d.ts +20 -0
  18. package/dist/conformance/index.js +1 -0
  19. package/dist/conformance/producer/harness.d.ts +21 -0
  20. package/dist/conformance/producer/run-suite.d.ts +2 -0
  21. package/dist/conformance/receipt/harness.d.ts +12 -0
  22. package/dist/conformance/receipt/projection-harness.d.ts +9 -0
  23. package/dist/conformance/receipt/run-projection-suite.d.ts +2 -0
  24. package/dist/conformance/receipt/run-suite.d.ts +2 -0
  25. package/dist/conformance/reliability/checks.d.ts +4 -0
  26. package/dist/conformance/result/harness.d.ts +15 -0
  27. package/dist/conformance/result/run-suite.d.ts +2 -0
  28. package/dist/conformance/version/compatibility.d.ts +13 -0
  29. package/dist/contracts/canonical/canonical-json.d.ts +1 -0
  30. package/dist/contracts/contract-types.d.ts +34 -0
  31. package/dist/contracts/generated/business-receipt.d.ts +21 -0
  32. package/dist/contracts/generated/cloud-event-context.d.ts +32 -0
  33. package/dist/contracts/generated/cloud-event-fanout.d.ts +33 -0
  34. package/dist/contracts/generated/cloud-event.d.ts +24 -0
  35. package/dist/contracts/generated/event-origin-claims.d.ts +13 -0
  36. package/dist/contracts/generated/exchange-grant.d.ts +11 -0
  37. package/dist/contracts/generated/exchange-registration.d.ts +24 -0
  38. package/dist/contracts/generated/fanout-event-draft.d.ts +28 -0
  39. package/dist/contracts/generated/gap-baseline.d.ts +18 -0
  40. package/dist/contracts/generated/gap-recovery-command.d.ts +7 -0
  41. package/dist/contracts/generated/identity-context.d.ts +15 -0
  42. package/dist/contracts/generated/namespaced-reference.d.ts +4 -0
  43. package/dist/contracts/generated/native-event-catalog.d.ts +19 -0
  44. package/dist/contracts/generated/node-connection-profile.d.ts +217 -0
  45. package/dist/contracts/generated/node-worker-observation.d.ts +7 -0
  46. package/dist/contracts/generated/outbox-recovery-command.d.ts +8 -0
  47. package/dist/contracts/generated/portable-exchange-registration.d.ts +109 -0
  48. package/dist/contracts/generated/portable-gap-baseline.d.ts +93 -0
  49. package/dist/contracts/generated/portable-gap-recovery-command.d.ts +35 -0
  50. package/dist/contracts/generated/portable-outbox-recovery-command.d.ts +37 -0
  51. package/dist/contracts/generated/public-jwks-snapshot.d.ts +141 -0
  52. package/dist/contracts/generated/result-ref.d.ts +8 -0
  53. package/dist/contracts/generated/supported-profiles.d.ts +3 -0
  54. package/dist/contracts/generated/tenant-connection.d.ts +13 -0
  55. package/dist/contracts/generated/tenant-context.d.ts +4 -0
  56. package/dist/contracts/index.d.ts +26 -0
  57. package/dist/contracts/index.js +1 -0
  58. package/dist/contracts/validation/contract-error.d.ts +16 -0
  59. package/dist/contracts/validation/contracts.d.ts +316 -0
  60. package/dist/contracts/validation/decode-cloud-event.d.ts +2 -0
  61. package/dist/contracts/validation/identity-context.d.ts +3 -0
  62. package/dist/contracts/validation/origin-proof-wire.d.ts +3 -0
  63. package/dist/contracts/validation/strict-json.d.ts +1 -0
  64. package/dist/core/inbox/accept-delivery.d.ts +26 -0
  65. package/dist/core/inbox/apply-delivery.d.ts +29 -0
  66. package/dist/core/inbox/domain-apply-result.d.ts +13 -0
  67. package/dist/core/inbox/inbox-worker.d.ts +2 -0
  68. package/dist/core/inbox/read-result.d.ts +12 -0
  69. package/dist/core/inbox/worker-types.d.ts +21 -0
  70. package/dist/core/index.d.ts +50 -0
  71. package/dist/core/index.js +1 -0
  72. package/dist/core/outbox/authorize-recovery.d.ts +75 -0
  73. package/dist/core/outbox/outbox-relay.d.ts +26 -0
  74. package/dist/core/outbox/record-event.d.ts +10 -0
  75. package/dist/core/outbox/record-fanout.d.ts +16 -0
  76. package/dist/core/ports/dead-letter.d.ts +29 -0
  77. package/dist/core/ports/inbox.d.ts +43 -0
  78. package/dist/core/ports/observability.d.ts +21 -0
  79. package/dist/core/ports/origin-refresh.d.ts +5 -0
  80. package/dist/core/ports/outbox.d.ts +46 -0
  81. package/dist/core/ports/result-reader.d.ts +14 -0
  82. package/dist/core/ports/transport.d.ts +13 -0
  83. package/dist/core/receipt/receipt-state.d.ts +3 -0
  84. package/dist/core/recovery/event-log.d.ts +28 -0
  85. package/dist/core/recovery/gap-baseline-binding.d.ts +2 -0
  86. package/dist/core/recovery/gap-recovery.d.ts +15 -0
  87. package/dist/core/recovery/receipt-identity-recovery.d.ts +8 -0
  88. package/dist/core/recovery/receipt-recovery.d.ts +14 -0
  89. package/dist/core/recovery/replay-dead-letter.d.ts +4 -0
  90. package/dist/core/runtime/dependencies.d.ts +19 -0
  91. package/dist/core/runtime/errors.d.ts +6 -0
  92. package/dist/core/runtime/event-identity.d.ts +14 -0
  93. package/dist/core/runtime/exchange-connection.d.ts +7 -0
  94. package/dist/core/runtime/fanout-identity.d.ts +6 -0
  95. package/dist/core/runtime/lease.d.ts +6 -0
  96. package/dist/core/runtime/observe-health.d.ts +3 -0
  97. package/dist/core/runtime/operation.d.ts +2 -0
  98. package/dist/core/runtime/retry.d.ts +14 -0
  99. package/dist/identity-contracts/authorization/authorization-port.d.ts +35 -0
  100. package/dist/identity-contracts/index.d.ts +2 -0
  101. package/dist/identity-contracts/index.js +0 -0
  102. package/dist/identity-contracts/origin/verified-origin.d.ts +28 -0
  103. package/dist/identity-verifier/index.d.ts +6 -0
  104. package/dist/identity-verifier/index.js +1 -0
  105. package/dist/identity-verifier/jwt/service-token-verifier.d.ts +15 -0
  106. package/dist/identity-verifier/origin/event-origin-policy.d.ts +36 -0
  107. package/dist/identity-verifier/origin/event-origin-signer.d.ts +6 -0
  108. package/dist/identity-verifier/origin/event-origin-verifier.d.ts +6 -0
  109. package/dist/index.d.ts +22 -0
  110. package/dist/index.js +20 -0
  111. package/dist/node-config/configuration-error.d.ts +4 -0
  112. package/dist/node-config/diagnostics/check-configuration.d.ts +40 -0
  113. package/dist/node-config/identity/load-identity.d.ts +14 -0
  114. package/dist/node-config/identity/local-recovery-origin.d.ts +3 -0
  115. package/dist/node-config/identity/public-identity.d.ts +87 -0
  116. package/dist/node-config/identity/public-snapshot.d.ts +2 -0
  117. package/dist/node-config/identity/signing-identity.d.ts +5 -0
  118. package/dist/node-config/index.d.ts +12 -0
  119. package/dist/node-config/index.js +1 -0
  120. package/dist/node-config/main.js +3 -0
  121. package/dist/node-config/operations/export-own-public.d.ts +8 -0
  122. package/dist/node-config/operations/refresh-trust.d.ts +10 -0
  123. package/dist/node-config/profile/load-profile.d.ts +6 -0
  124. package/dist/node-config/profile/validate-profile.d.ts +2 -0
  125. package/dist/node-worker/index.d.ts +3 -0
  126. package/dist/node-worker/index.js +1 -0
  127. package/dist/node-worker/lifecycle.d.ts +19 -0
  128. package/dist/node-worker/route-scheduler.d.ts +17 -0
  129. package/dist/node-worker/session.d.ts +15 -0
  130. package/dist/provisioner/application/apply.d.ts +11 -0
  131. package/dist/provisioner/application/ports.d.ts +44 -0
  132. package/dist/provisioner/application/verify.d.ts +7 -0
  133. package/dist/provisioner/cli/arguments.d.ts +9 -0
  134. package/dist/provisioner/cli/live-command.d.ts +7 -0
  135. package/dist/provisioner/cli/main.js +2 -0
  136. package/dist/provisioner/cli/run.d.ts +2 -0
  137. package/dist/provisioner/cli/session-types.d.ts +26 -0
  138. package/dist/provisioner/conformance/policy.d.ts +9 -0
  139. package/dist/provisioner/index.d.ts +17 -0
  140. package/dist/provisioner/index.js +1 -0
  141. package/dist/provisioner/manifest/types.d.ts +55 -0
  142. package/dist/provisioner/manifest/validate.d.ts +3 -0
  143. package/dist/provisioner/nats/inspector.d.ts +13 -0
  144. package/dist/provisioner/nats/types.d.ts +38 -0
  145. package/dist/provisioner/nats/writer-types.d.ts +11 -0
  146. package/dist/provisioner/nats/writer.d.ts +18 -0
  147. package/dist/provisioner/planning/digest.d.ts +1 -0
  148. package/dist/provisioner/planning/plan.d.ts +18 -0
  149. package/dist/transport-nats/connection/delivery-session.d.ts +10 -0
  150. package/dist/transport-nats/connection/verified-connection.d.ts +12 -0
  151. package/dist/transport-nats/consume/delivery-handler.d.ts +34 -0
  152. package/dist/transport-nats/consume/nats-consumer.d.ts +18 -0
  153. package/dist/transport-nats/index.d.ts +12 -0
  154. package/dist/transport-nats/index.js +3 -0
  155. package/dist/transport-nats/publish/nats-publisher.d.ts +24 -0
  156. package/dist/transport-nats/transport-error.d.ts +5 -0
  157. package/licenses/NATS-LICENSE +201 -0
  158. package/package.json +75 -3
  159. package/README.md +0 -3
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ var ProvisionerError=class extends Error{constructor(code,message){super(message);this.code=code;this.name="ProvisionerError"}code};var publicCodes=new Set(["CLI_USAGE","CLI_INPUT_ERROR","INPUT_TOO_LARGE","INVALID_MANIFEST","INVALID_PLAN","INVALID_SNAPSHOT","INVALID_STATUS","INVALID_ADAPTER_PATH","INVALID_ADAPTER_MODULE","INVALID_ADAPTER_SESSION","LIVE_ADAPTER_REQUIRED","READ_ONLY_SESSION","ROLE_DURABILITY_CONFLICT","NO_CAPABILITIES","CAPABILITY_CONFLICT","PLAN_DRIFT","UNAUTHORIZED_DEPLOYMENT","DOMAIN_MISMATCH","BROKER_READ_UNAVAILABLE","UNSUPPORTED_BROKER_CONFIG","INVALID_INSPECTION_OPTIONS","AMBIGUOUS_RESOURCE","APPLY_INVARIANT"]);function publicErrorCode(error){if(error instanceof ProvisionerError&&publicCodes.has(error.code))return error.code;if(error instanceof Error&&"code"in error&&["TIMEOUT","ABORTED"].includes(String(error.code))){return String(error.code)}return"CLI_OPERATION_FAILED"}import{isSupportedMessagingProfile}from"@eventsdk/sdk/contracts";function parseDurability(outbox,inbox){if(outbox!=="owner-domain-transaction"&&outbox!=="native-event-log"&&outbox!=="not-applicable"||inbox!=="owner-durable"&&inbox!=="not-applicable"){throw new ProvisionerError("INVALID_MANIFEST","Explicit supported durability values are required.")}return{outbox,inbox}}function checkRoleDurability(manifest){const produces=manifest.emits.length>0;const consumes=manifest.consumes.length>0;if(!produces&&!consumes){throw new ProvisionerError("NO_CAPABILITIES","At least one explicit producer or consumer capability is required.")}if(produces===(manifest.durability.outbox==="not-applicable")||consumes===(manifest.durability.inbox==="not-applicable")){throw new ProvisionerError("ROLE_DURABILITY_CONFLICT","Durability must match the explicitly declared roles.")}}function checkCapabilities(m){const emittedTypes=new Set;const emittedSubjects=new Set;for(const emit of m.emits){if(emittedTypes.has(emit.type)||emittedSubjects.has(emit.subject))invalid();emittedTypes.add(emit.type);emittedSubjects.add(emit.subject);const streams=m.resources.filter(r=>r.kind==="stream"&&r.subjects.includes(emit.subject));if(streams.length!==1)invalid()}const consumerRefs=new Set;for(const consume of m.consumes){if(consumerRefs.has(consume.consumerRef))invalid();consumerRefs.add(consume.consumerRef);const resources=m.resources.filter(r=>r.kind==="consumer"&&r.name===consume.consumerRef);if(resources.length!==1||resources[0]?.subjects.length!==1||resources[0]?.subjects[0]!==consume.subject)invalid()}for(const resource2 of m.resources){if(resource2.kind==="stream"&&resource2.subjects.some(s=>!emittedSubjects.has(s)))invalid();if(resource2.kind==="consumer"&&!consumerRefs.has(resource2.name))invalid()}}function invalid(){throw new ProvisionerError("CAPABILITY_CONFLICT","Capabilities require explicit, unique resource/subject mappings.")}var token=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/;var subject=/^[a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)*$/;function object(value,keys){if(!value||typeof value!=="object"||Array.isArray(value)){throw new ProvisionerError("INVALID_MANIFEST","Expected an object.")}const record2=value;if(Object.keys(record2).some(key=>!keys.includes(key))){throw new ProvisionerError("INVALID_MANIFEST","Unknown or sensitive fields are not allowed.")}return record2}function string(value,pattern=token){if(typeof value!=="string"||!pattern.test(value)){throw new ProvisionerError("INVALID_MANIFEST","Invalid identifier.")}return value}function integer(value,min,max=Number.MAX_SAFE_INTEGER){if(!Number.isSafeInteger(value)||typeof value!=="number"||value<min||value>max){throw new ProvisionerError("INVALID_MANIFEST","Invalid bounded integer.")}return value}function list(value){if(!Array.isArray(value)||value.length>1e3){throw new ProvisionerError("INVALID_MANIFEST","Expected a bounded array.")}return value}function parseResource(value){const r=object(value,["kind","name","owner","deploymentDomainRef","subjects","retentionSeconds","replicas","maxBytes","stream","ackWaitSeconds","maxAckPending"]);if(r.kind!=="stream"&&r.kind!=="consumer"){throw new ProvisionerError("INVALID_MANIFEST","Unsupported resource kind.")}const subjects=list(r.subjects).map(v=>string(v,subject));if(!subjects.length||new Set(subjects).size!==subjects.length){throw new ProvisionerError("INVALID_MANIFEST","Subjects must be nonempty and unique.")}const base={name:string(r.name),owner:string(r.owner),deploymentDomainRef:string(r.deploymentDomainRef),subjects};if(r.kind==="stream"){if(r.stream!==void 0||r.ackWaitSeconds!==void 0||r.maxAckPending!==void 0){throw new ProvisionerError("INVALID_MANIFEST","Consumer fields on stream.")}return{...base,kind:"stream",retentionSeconds:integer(r.retentionSeconds,1),replicas:integer(r.replicas,1,5),maxBytes:integer(r.maxBytes,1)}}if(["retentionSeconds","replicas","maxBytes"].some(key=>key in r))throw new ProvisionerError("INVALID_MANIFEST","Stream capacity fields on consumer.");return{...base,kind:"consumer",stream:string(r.stream),ackWaitSeconds:integer(r.ackWaitSeconds,1),maxAckPending:integer(r.maxAckPending,1)}}function parseManifest(value){const m=object(value,["messagingProfile","service","emits","consumes","transport","durability","resources"]);const t=object(m.transport,["kind","deploymentDomainRef"]);const d=object(m.durability,["outbox","inbox"]);if(t.kind!=="nats-jetstream"){throw new ProvisionerError("INVALID_MANIFEST","Unsupported transport.")}if(!isSupportedMessagingProfile(m.messagingProfile))throw new ProvisionerError("INVALID_MANIFEST","Unsupported messaging profile.");const manifest={messagingProfile:m.messagingProfile,service:string(m.service),emits:list(m.emits).map(v=>{const x=object(v,["type","subject"]);return{type:string(x.type,subject),subject:string(x.subject,subject)}}),consumes:list(m.consumes).map(parseConsume),transport:{kind:"nats-jetstream",deploymentDomainRef:string(t.deploymentDomainRef)},durability:parseDurability(d.outbox,d.inbox),resources:list(m.resources).map(parseResource)};checkOwnership(manifest);checkCapabilities(manifest);checkRoleDurability(manifest);return manifest}function parseConsume(value){const c=object(value,["fromService","type","consumerRef","subject"]);return{fromService:string(c.fromService),type:string(c.type,subject),consumerRef:string(c.consumerRef),subject:string(c.subject,subject)}}function checkOwnership(m){const ids=new Set;for(const resource2 of m.resources){const id=resource2.kind+":"+(resource2.kind==="consumer"?resource2.stream:"")+":"+resource2.name;if(ids.has(id)||resource2.owner!==m.service||resource2.deploymentDomainRef!==m.transport.deploymentDomainRef){throw new ProvisionerError("INVALID_MANIFEST","Duplicate resource or ownership mismatch.")}ids.add(id)}}import{createHash}from"node:crypto";function canonical(value){if(value===null||typeof value!=="object"){const encoded=JSON.stringify(value);if(encoded===void 0)throw new TypeError("Undefined is not canonical JSON.");return encoded}if(Array.isArray(value))return"["+value.map(canonical).join(",")+"]";const record2=value;return"{"+Object.keys(record2).sort().map(key=>JSON.stringify(key)+":"+canonical(record2[key])).join(",")+"}"}function digest(value){return createHash("sha256").update(canonical(value)).digest("hex")}function resourceKey(r){return r.kind+":"+(r.kind==="consumer"?r.stream:"")+":"+r.name}function configurationDigest(observed){return digest(observed.map(s=>({config:parseResource(s.config),nativeConfigDigest:s.nativeConfigDigest??null})).sort((a,b)=>resourceKey(a.config).localeCompare(resourceKey(b.config))))}function planProvisioning(input,observed){const manifest=parseManifest(input);const configs=new Map;for(const snapshot of observed){const config=parseResource(snapshot.config);const key=resourceKey(config);if(configs.has(key))throw new ProvisionerError("AMBIGUOUS_RESOURCE","Duplicate observed resource.");configs.set(key,config)}const operations=[...manifest.resources].sort((a,b)=>Number(a.kind==="consumer")-Number(b.kind==="consumer")).map(r=>classify(r,configs.get(resourceKey(r))));for(const operation2 of operations){const r=operation2.resource;if(r.kind!=="consumer"||operation2.action!=="CREATE")continue;const parent=configs.get("stream::"+r.stream)??operations.find(o=>o.resource.kind==="stream"&&o.resource.name===r.stream&&o.action==="CREATE")?.resource;if(!parent||parent.kind!=="stream"||parent.deploymentDomainRef!==r.deploymentDomainRef||r.subjects.some(subject2=>!parent.subjects.includes(subject2))){operations[operations.indexOf(operation2)]={...operation2,action:"BLOCKED",reason:"PARENT_DEPENDENCY_UNPROVEN"}}}const body={manifestDigest:digest(manifest),observedConfigDigest:configurationDigest(observed),operations};return{...body,planRef:digest(body)}}function classify(desired,current){const beforeDigest=current?digest(current):null;const base={resource:desired,beforeDigest};if(!current)return{...base,action:"CREATE",reason:"RESOURCE_ABSENT"};if(digest(desired)===beforeDigest)return{...base,action:"NOOP",reason:"MATCH"};const blocked=unsafeChange(current,desired);return blocked?{...base,action:"BLOCKED",reason:blocked}:{...base,action:"SAFE_UPDATE",reason:"MONOTONIC_CAPACITY_UPDATE"}}function unsafeChange(a,b){if(a.owner!==b.owner||a.deploymentDomainRef!==b.deploymentDomainRef)return"OWNER_CONFLICT";if(a.kind!==b.kind||digest(a.subjects)!==digest(b.subjects))return"ROUTING_CHANGE";if(a.kind==="stream"&&b.kind==="stream"){if(b.retentionSeconds<a.retentionSeconds||b.replicas<a.replicas||b.maxBytes<a.maxBytes)return"DURABILITY_REDUCTION"}if(a.kind==="consumer"&&b.kind==="consumer"){if(a.stream!==b.stream)return"ROUTING_CHANGE";if(a.ackWaitSeconds!==b.ackWaitSeconds)return"DELIVERY_POLICY_CHANGE";if(b.maxAckPending<a.maxAckPending)return"CAPACITY_REDUCTION"}return void 0}function rolePolicyCases(producer){const stream=producer.resources[0];if(!stream)throw new Error("Conformance fixture requires a producer stream.");const consumer=consumerFixture(producer,stream);return roleCases(producer,consumer)}function consumerFixture(producer,stream){return{...producer,emits:[],consumes:[{fromService:"upstream",type:"fixture.created.v1",subject:"fixture.created.v1",consumerRef:"worker"}],resources:[{owner:stream.owner,deploymentDomainRef:stream.deploymentDomainRef,subjects:stream.subjects,kind:"consumer",name:"worker",stream:"existing-upstream",ackWaitSeconds:30,maxAckPending:10}],durability:{outbox:"not-applicable",inbox:"owner-durable"}}}function roleCases(producer,consumer){return[{name:"producer-without-inbox",passed:parseManifest(producer).durability.inbox==="not-applicable"},{name:"consumer-without-outbox-or-stream-create",passed:planProvisioning(consumer,[]).operations.every(o=>o.resource.kind==="consumer")},{name:"reject-empty-roles",passed:rejects(()=>parseManifest({...producer,emits:[],resources:[],durability:{outbox:"not-applicable",inbox:"not-applicable"}}))},{name:"reject-role-durability-mismatch",passed:rejects(()=>parseManifest({...producer,durability:{outbox:"owner-domain-transaction",inbox:"owner-durable"}}))}]}function rejects(run){try{run();return false}catch{return true}}function provisionerConformance(){const resource2={kind:"stream",name:"fixture",owner:"fixture",deploymentDomainRef:"isolated",subjects:["fixture.created.v1"],retentionSeconds:60,replicas:1,maxBytes:1024};const manifest={messagingProfile:"eventsdk/1.0",service:"fixture",emits:[{type:"fixture.created.v1",subject:"fixture.created.v1"}],consumes:[],transport:{kind:"nats-jetstream",deploymentDomainRef:"isolated"},durability:{outbox:"owner-domain-transaction",inbox:"not-applicable"},resources:[resource2]};const tests=[...rolePolicyCases(parseManifest(manifest)),{name:"create-absent",passed:planProvisioning(manifest,[]).operations[0]?.action==="CREATE"},{name:"repeat-noop",passed:planProvisioning(manifest,[{config:resource2}]).operations[0]?.action==="NOOP"},{name:"block-downgrade",passed:planProvisioning(manifest,[{config:{...resource2,replicas:3}}]).operations[0]?.action==="BLOCKED"},{name:"reject-secret-fields",passed:rejects2(()=>parseManifest({...manifest,token:"fixture"}))}];return{suite:"provisioner-offline-policy/1.0",scope:"OFFLINE_POLICY_ONLY",passed:tests.every(t=>t.passed),tests}}function rejects2(run){try{run();return false}catch{return true}}function parseArguments(args){const command=args[0];if(!["validate","plan","apply","verify","status","conformance"].includes(command??""))usage();const options=new Map;const positional=[];for(let index=1;index<args.length;index++){const value=args[index];if(!value)usage();if(!value.startsWith("--")){positional.push(value);continue}if(!["--adapter","--plan","--timeout-ms"].includes(value)||options.has(value))usage();const argument=args[++index];if(!argument||argument.startsWith("--"))usage();options.set(value,argument)}const timeoutMs=Number(options.get("--timeout-ms")??"10000");if(!Number.isSafeInteger(timeoutMs)||timeoutMs<10||timeoutMs>12e4)usage();const adapterPath=options.get("--adapter");const planPath=options.get("--plan");if(planPath&&command!=="apply")usage();if(adapterPath&&["validate","conformance"].includes(command??""))usage();const expected=command==="conformance"||command==="status"&&adapterPath?0:command==="status"||command==="validate"||command==="apply"||adapterPath?1:2;if(positional.length!==expected)usage();if(command==="apply"&&adapterPath&&!planPath)usage();return{command,timeoutMs,...adapterPath===void 0?{}:{adapterPath},...planPath===void 0?{}:{planPath},...positional[0]===void 0?{}:command==="status"?{snapshotPath:positional[0]}:{manifestPath:positional[0]},...positional[1]===void 0?{}:{snapshotPath:positional[1]}}}function usage(){throw new ProvisionerError("CLI_USAGE","Invalid or ambiguous command arguments.")}import{open,stat}from"node:fs/promises";import{parseJsonStrict}from"@eventsdk/sdk/contracts";async function readCliJson(path){if(!path)throw new ProvisionerError("CLI_USAGE","A JSON file path is required.");if(!(await stat(path)).isFile()){throw new ProvisionerError("CLI_INPUT_ERROR","Expected a regular JSON file.")}const file=await open(path,"r");try{if(!(await file.stat()).isFile())throw new ProvisionerError("CLI_INPUT_ERROR","Expected a regular JSON file.");const limit=1048576;const bytes=new Uint8Array(limit+1);let used=0;while(used<=limit){const chunk=await file.read(bytes,used,bytes.length-used,null);if(chunk.bytesRead===0)break;used+=chunk.bytesRead;if(used>limit)throw new ProvisionerError("INPUT_TOO_LARGE","Input exceeds 1 MiB.")}return parseJsonStrict(bytes.subarray(0,used))}finally{await file.close()}}import{boundedOperation as boundedOperation2}from"@eventsdk/sdk/core";async function applyProvisioning(input){const manifest=parseManifest(structuredClone(input.manifest));const request={...input,manifest,plan:structuredClone(input.plan),authorization:structuredClone(input.authorization)};authorize(request,manifest.transport.deploymentDomainRef);request.signal.throwIfAborted();const lease=await request.broker.acquireLease(manifest.transport.deploymentDomainRef,request.signal);let result;let failure;let failed=false;try{const observed=await request.broker.read(request.signal);const actual=planProvisioning(manifest,observed);if(digest(actual)!==digest(request.plan)){throw new ProvisionerError("PLAN_DRIFT","Manifest, configuration or plan has changed.")}result=actual.operations.some(o=>o.action==="BLOCKED")?{planRef:actual.planRef,state:"BLOCKED",receipts:[]}:await execute({...request,plan:actual},lease,observed)}catch(error){failed=true;failure=error}let leaseReleased=true;try{await lease.release()}catch{leaseReleased=false}if(failed)throw failure;if(!result)throw new ProvisionerError("APPLY_INVARIANT","No apply result.");return{...result,leaseReleased}}function authorize(request,domain){const a=request.authorization;const manifest=parseManifest(request.manifest);if(a.role!=="deployment"||!a.subject||a.deploymentDomainRef!==domain||manifest.resources.some(r=>!a.allowedResourceKeys.includes(resourceKey(r))||r.subjects.some(s=>!a.allowedSubjects.includes(s)))){throw new ProvisionerError("UNAUTHORIZED_DEPLOYMENT","Explicit deployment authority is required.")}}async function execute(r,lease,observed){const receipts=[];let expected=configurationDigest(observed);for(const operation2 of r.plan.operations){let before;try{r.signal.throwIfAborted();before=await r.broker.read(r.signal)}catch{receipts.push({resourceKey:resourceKey(operation2.resource),action:operation2.action,outcome:"BLOCKED",reason:r.signal.aborted?"CANCELLED_BEFORE_WRITE":"READ_UNAVAILABLE"});return{planRef:r.plan.planRef,state:"PARTIAL",receipts}}if(configurationDigest(before)!==expected){receipts.push({resourceKey:resourceKey(operation2.resource),action:operation2.action,outcome:"BLOCKED",reason:"PLAN_DRIFT"});return{planRef:r.plan.planRef,state:"PARTIAL",receipts}}const receipt=await journaledExecute(r,lease,operation2);receipts.push(receipt);if(receipt.outcome!=="VERIFIED")return{planRef:r.plan.planRef,state:"PARTIAL",receipts};if(operation2.action==="NOOP")continue;const desired=before.filter(s=>resourceKey(s.config)!==resourceKey(operation2.resource));expected=configurationDigest([...desired,{config:operation2.resource,...receipt.nativeConfigDigest===void 0?{}:{nativeConfigDigest:receipt.nativeConfigDigest}}])}return verifyFinalConfiguration(r,expected,receipts)}async function verifyFinalConfiguration(r,expected,receipts){try{r.signal.throwIfAborted();if(configurationDigest(await r.broker.read(r.signal))!==expected){receipts.push({resourceKey:"plan",action:"VERIFY",outcome:"BLOCKED",reason:"PLAN_DRIFT"});return{planRef:r.plan.planRef,state:"PARTIAL",receipts}}return{planRef:r.plan.planRef,state:"VERIFIED",receipts}}catch{receipts.push({resourceKey:"plan",action:"VERIFY",outcome:"UNKNOWN",reason:"FINAL_READ_UNAVAILABLE"});return{planRef:r.plan.planRef,state:"PARTIAL",receipts}}}async function executeOne(r,lease,operation2){const base={resourceKey:resourceKey(operation2.resource),action:operation2.action};try{if(operation2.action==="CREATE")await r.broker.create(structuredClone(operation2.resource),lease,r.signal);if(operation2.action==="SAFE_UPDATE"){if(!operation2.beforeDigest)throw new ProvisionerError("INVALID_PLAN","Missing prior digest.");await r.broker.update(structuredClone(operation2.resource),operation2.beforeDigest,lease,r.signal)}const after=await r.broker.read(r.signal);const actual=after.find(s=>resourceKey(s.config)===base.resourceKey);if(actual&&digest(actual.config)===digest(operation2.resource)){return{...base,outcome:"VERIFIED",reason:"READBACK_MATCH",observedDigest:digest(actual.config),...actual.nativeConfigDigest===void 0?{}:{nativeConfigDigest:actual.nativeConfigDigest}}}return{...base,outcome:"UNKNOWN",reason:"READBACK_MISMATCH"}}catch{return{...base,outcome:"UNKNOWN",reason:"WRITE_OR_READBACK_UNCERTAIN"}}}async function journaledExecute(r,lease,operation2){const resource2=resourceKey(operation2.resource);if(!r.journal)return executeOne(r,lease,operation2);try{await r.journal.append({planRef:r.plan.planRef,resourceKey:resource2,phase:"INTENT"},r.signal)}catch{return{resourceKey:resource2,action:operation2.action,outcome:"BLOCKED",reason:"JOURNAL_INTENT_FAILED",journalPersisted:false}}const receipt=await executeOne(r,lease,operation2);try{await r.journal.append({planRef:r.plan.planRef,resourceKey:resource2,phase:"RESULT",receipt:structuredClone(receipt)},r.signal);return{...receipt,journalPersisted:true}}catch{return{...receipt,outcome:"UNKNOWN",reason:"JOURNAL_RESULT_FAILED",journalPersisted:false}}}async function verifyProvisioning(manifest,broker,signal){signal.throwIfAborted();const plan=planProvisioning(manifest,await broker.read(signal));return{verified:plan.operations.every(o=>o.action==="NOOP"),plan}}async function readReviewedPlan(path){const value=await readCliJson(path);const p=record(value,["manifestDigest","observedConfigDigest","planRef","operations"]);if(!Array.isArray(p.operations)||p.operations.length>1e3)invalid2();return{manifestDigest:hash(p.manifestDigest),observedConfigDigest:hash(p.observedConfigDigest),planRef:hash(p.planRef),operations:p.operations.map(operation)}}function operation(value){const o=record(value,["action","resource","beforeDigest","reason"]);if(!["NOOP","CREATE","SAFE_UPDATE","BLOCKED"].includes(String(o.action))||typeof o.reason!=="string"||!/^[A-Z0-9_]{1,128}$/.test(o.reason))invalid2();return{action:o.action,resource:parseResource(o.resource),beforeDigest:o.beforeDigest===null?null:hash(o.beforeDigest),reason:o.reason}}function record(value,keys){if(!value||typeof value!=="object"||Array.isArray(value)||Object.keys(value).some(key=>!keys.includes(key)))invalid2();return value}function hash(value){if(typeof value!=="string"||!/^[a-f0-9]{64}$/.test(value))invalid2();return value}function invalid2(){throw new ProvisionerError("INVALID_PLAN","Reviewed plan object required.")}import{realpath,stat as stat2}from"node:fs/promises";import{isAbsolute,extname}from"node:path";import{pathToFileURL}from"node:url";import{boundedOperation}from"@eventsdk/sdk/core";async function closeSession(session){try{await boundedOperation({timeoutMs:1e3},signal=>session.close(signal));return true}catch{return false}}async function loadSession(path,context,timeoutMs){if(!isAbsolute(path)||![".mjs",".js"].includes(extname(path))){throw new ProvisionerError("INVALID_ADAPTER_PATH","Explicit absolute local ESM path required.")}return boundedOperation({signal:context.signal,timeoutMs},async signal=>{const canonical2=await realpath(path);if(![".mjs",".js"].includes(extname(canonical2))||!(await stat2(canonical2)).isFile()){throw new ProvisionerError("INVALID_ADAPTER_PATH","Adapter must be a local regular ESM file.")}signal.throwIfAborted();const module=await import(pathToFileURL(canonical2).href);signal.throwIfAborted();const factory=field(module,"createProvisionerSession");if(typeof factory!=="function")throw new ProvisionerError("INVALID_ADAPTER_MODULE","Missing session factory.");const value=await factory({...context,signal,...context.manifest?{manifest:structuredClone(context.manifest)}:{}});if(signal.aborted){await cleanupUnvalidated(value);signal.throwIfAborted()}try{return validateSession(value)}catch(error){await cleanupUnvalidated(value);throw error}})}function field(value,key){return value&&typeof value==="object"?value[key]:void 0}function method(value,key){return typeof field(value,key)==="function"}function validateSession(value){const mode=field(value,"mode");const broker=field(value,"broker");if(field(value,"apiVersion")!=="eventsdk-provisioner-session/1"||!["read-only","deployment"].includes(String(mode))||!method(value,"close")||field(value,"status")!==void 0&&!method(value,"status")||!method(broker,"read")){throw new ProvisionerError("INVALID_ADAPTER_SESSION","Unsupported session contract.")}if(mode==="deployment"&&(!method(broker,"acquireLease")||!method(broker,"create")||!method(broker,"update")||!method(field(value,"journal"),"append")||!field(value,"authorization"))){throw new ProvisionerError("INVALID_ADAPTER_SESSION","Deployment requires authority, fencing port and journal.")}return value}async function cleanupUnvalidated(value){if(method(value,"close"))await closeSession(value)}function validateNatsStatus(value){const s=object2(value,["mode","natsDomain","accountIdentity","storageBytes","memoryBytes","maxStorageBytes","maxMemoryBytes","streamCount","consumerCount","resources"]);if(s.mode!=="LIVE_READ_ONLY"||s.accountIdentity!=="AUTHENTICATED_CONNECTION_NOT_EXPOSED_BY_JS_API"||!Array.isArray(s.resources)||s.resources.length>1e3)invalid3();return{mode:"LIVE_READ_ONLY",natsDomain:nullableName(s.natsDomain),accountIdentity:"AUTHENTICATED_CONNECTION_NOT_EXPOSED_BY_JS_API",storageBytes:integer2(s.storageBytes),memoryBytes:integer2(s.memoryBytes),maxStorageBytes:integer2(s.maxStorageBytes,-1),maxMemoryBytes:integer2(s.maxMemoryBytes,-1),streamCount:integer2(s.streamCount),consumerCount:integer2(s.consumerCount),resources:s.resources.map(resource)}}function resource(value){const r=object2(value,["kind","name","stream","owner","deploymentDomainRef","messages","bytes","firstSequence","lastSequence","pending","ackPending","ackFloor","nativeConfigDigest"]);if(r.kind!=="stream"&&r.kind!=="consumer"||typeof r.nativeConfigDigest!=="string"||!/^[a-f0-9]{64}$/.test(r.nativeConfigDigest))invalid3();return{kind:r.kind,name:name(r.name),stream:name(r.stream),owner:nullableName(r.owner),deploymentDomainRef:nullableName(r.deploymentDomainRef),messages:integer2(r.messages),bytes:integer2(r.bytes),firstSequence:integer2(r.firstSequence),lastSequence:integer2(r.lastSequence),pending:integer2(r.pending),ackPending:integer2(r.ackPending),ackFloor:r.ackFloor===null?null:integer2(r.ackFloor),nativeConfigDigest:r.nativeConfigDigest}}function object2(value,keys){if(!value||typeof value!=="object"||Array.isArray(value)||Object.keys(value).some(key=>!keys.includes(key)))invalid3();return value}function integer2(value,minimum=0){if(typeof value!=="number"||!Number.isSafeInteger(value)||value<minimum)invalid3();return value}function name(value){if(typeof value!=="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(value))invalid3();return value}function nullableName(value){return value===null?null:name(value)}function invalid3(){throw new ProvisionerError("INVALID_STATUS","Unsupported live status shape.")}var JournalProgress=class{constructor(delegate,plan){this.plan=plan;this.journal={append:async(entry,signal)=>{const operation2=plan.operations.find(o=>resourceKey(o.resource)===entry.resourceKey);if(!operation2||entry.planRef!==plan.planRef)throw new Error("Journal entry outside plan.");this.receipts.set(entry.resourceKey,{resourceKey:entry.resourceKey,action:operation2.action,outcome:"UNKNOWN",reason:"JOURNAL_OR_APPLY_UNCONFIRMED",journalPersisted:false});const receipt=entry.receipt===void 0?void 0:structuredClone(entry.receipt);await delegate.append(structuredClone(entry),signal);if(entry.phase==="RESULT"&&receipt){this.receipts.set(entry.resourceKey,{...receipt,journalPersisted:true})}}}}plan;receipts=new Map;journal;interrupted(){return{planRef:this.plan.planRef,state:"PARTIAL",receipts:[...this.receipts.values()]}}};async function runLiveCommand(args,manifest,signal){if(!args.adapterPath)throw new ProvisionerError("LIVE_ADAPTER_REQUIRED","Explicit adapter required.");const reviewedPlan=args.command==="apply"?await readReviewedPlan(args.planPath):void 0;const command=args.command;const session=await loadSession(args.adapterPath,{command,signal,...manifest===void 0?{}:{manifest}},args.timeoutMs);let result;try{result=await dispatch(args,manifest,session,signal,reviewedPlan)}catch(error){await closeSession(session);throw error}const closed=await closeSession(session);return{value:{mode:"LIVE_ADAPTER",result:result.value,sessionClosed:closed},exitCode:closed?result.exitCode:4}}async function dispatch(args,manifest,session,signal,reviewedPlan){if(args.command==="apply"){if(session.mode!=="deployment")throw new ProvisionerError("READ_ONLY_SESSION","Apply requires a deployment session.");if(!manifest)throw new ProvisionerError("CLI_USAGE","Manifest required.");if(!reviewedPlan)throw new ProvisionerError("INVALID_PLAN","Reviewed plan required.");return runApply(args,manifest,session,signal,reviewedPlan)}return boundedOperation2({signal,timeoutMs:args.timeoutMs},async bounded=>{if(args.command==="status"){if(session.status)return{value:{coverage:"NATS_TELEMETRY",status:validateNatsStatus(await session.status(bounded))},exitCode:0};return{value:{coverage:"RESOURCES_ONLY",resources:safeSnapshots(await session.broker.read(bounded))},exitCode:0}}if(!manifest)throw new ProvisionerError("CLI_USAGE","Manifest required.");if(args.command==="verify"){const result=await verifyProvisioning(manifest,session.broker,bounded);return{value:result,exitCode:result.verified?0:3}}const plan=planProvisioning(manifest,await session.broker.read(bounded));return{value:{plan},exitCode:plan.operations.some(o=>o.action==="BLOCKED")?3:0}})}async function runApply(args,manifest,session,signal,plan){const progress=new JournalProgress(session.journal,plan);try{const result=await boundedOperation2({signal,timeoutMs:args.timeoutMs},bounded=>applyProvisioning({manifest,plan,broker:session.broker,authorization:session.authorization,journal:progress.journal,signal:bounded}));return{value:result,exitCode:result.state==="VERIFIED"&&result.leaseReleased!==false?0:4}}catch(error){if(isInterrupted(error))return{value:{...progress.interrupted(),interruption:"CANCELLED_OR_TIMEOUT"},exitCode:4};throw error}}function isInterrupted(error){return error instanceof Error&&"code"in error&&["ABORTED","TIMEOUT"].includes(String(error.code))}function safeSnapshots(snapshots2){if(snapshots2.length>1e3)throw new ProvisionerError("INVALID_SNAPSHOT","Too many resources.");return snapshots2.map(s=>{const config=parseResource(s.config);const counters={};for(const key of["messages","bytes","consumerPending"]){if(s[key]!==void 0){if(!Number.isSafeInteger(s[key])||s[key]<0)throw new ProvisionerError("INVALID_SNAPSHOT","Invalid resource counters.");counters[key]=s[key]}}return{config,...counters}})}function snapshots(value){if(!Array.isArray(value))throw new ProvisionerError("INVALID_SNAPSHOT","Expected configuration array.");return value.map(config=>({config:parseResource(config)}))}async function runProvisionerCli(args,signal){const options=parseArguments(args);if(options.command==="conformance"){const result=provisionerConformance();return{value:result,exitCode:result.passed?0:3}}if(options.command==="apply"&&!options.adapterPath){throw new ProvisionerError("LIVE_ADAPTER_REQUIRED","Explicit trusted deployment adapter required.")}const manifest=options.command==="status"?void 0:parseManifest(await readCliJson(options.manifestPath));if(options.adapterPath)return runLiveCommand(options,manifest,signal);if(options.command==="status"){return{value:{mode:"OFFLINE_SNAPSHOT",resources:snapshots(await readCliJson(options.snapshotPath))},exitCode:0}}if(options.command==="validate")return{value:{valid:true,manifest},exitCode:0};const plan=planProvisioning(manifest,snapshots(await readCliJson(options.snapshotPath)));const verified=plan.operations.every(o=>o.action==="NOOP");return{value:{mode:"OFFLINE_SNAPSHOT",verified,plan},exitCode:options.command==="verify"&&!verified||plan.operations.some(o=>o.action==="BLOCKED")?3:0}}var controller=new AbortController;var cancel=()=>controller.abort();process.once("SIGINT",cancel);process.once("SIGTERM",cancel);try{const result=await runProvisionerCli(process.argv.slice(2),controller.signal);process.stdout.write(JSON.stringify(result.value,null,2)+"\n");process.exitCode=result.exitCode}catch(error){const code=publicErrorCode(error);process.stderr.write(JSON.stringify({code})+"\n");process.exitCode=1}finally{process.off("SIGINT",cancel);process.off("SIGTERM",cancel)}
@@ -0,0 +1,2 @@
1
+ import type { CliResult } from './live-command.js';
2
+ export declare function runProvisionerCli(args: readonly string[], signal: AbortSignal): Promise<CliResult>;
@@ -0,0 +1,26 @@
1
+ import type { NatsInspectionStatus } from '../nats/types.js';
2
+ import type { CapabilityManifest } from '../manifest/types.js';
3
+ import type { BrokerManagementPort, BrokerReadPort, DeploymentAuthorization, DeploymentJournal } from '../application/ports.js';
4
+ export interface SessionContext {
5
+ readonly command: 'plan' | 'apply' | 'verify' | 'status';
6
+ readonly signal: AbortSignal;
7
+ readonly manifest?: CapabilityManifest;
8
+ }
9
+ interface SessionBase {
10
+ readonly apiVersion: 'eventsdk-provisioner-session/1';
11
+ readonly close: (signal: AbortSignal) => Promise<void>;
12
+ readonly status?: (signal: AbortSignal) => Promise<NatsInspectionStatus>;
13
+ }
14
+ export interface ReadOnlyProvisionerSession extends SessionBase {
15
+ readonly mode: 'read-only';
16
+ readonly broker: BrokerReadPort;
17
+ }
18
+ export interface DeploymentProvisionerSession extends SessionBase {
19
+ readonly mode: 'deployment';
20
+ readonly broker: BrokerManagementPort;
21
+ readonly authorization: DeploymentAuthorization;
22
+ readonly journal: DeploymentJournal;
23
+ }
24
+ export type ProvisionerSession = ReadOnlyProvisionerSession | DeploymentProvisionerSession;
25
+ export type SessionFactory = (context: SessionContext) => Promise<ProvisionerSession>;
26
+ export {};
@@ -0,0 +1,9 @@
1
+ export declare function provisionerConformance(): {
2
+ suite: string;
3
+ scope: string;
4
+ passed: boolean;
5
+ tests: {
6
+ name: string;
7
+ passed: boolean;
8
+ }[];
9
+ };
@@ -0,0 +1,17 @@
1
+ export { ProvisionerError } from './manifest/types.js';
2
+ export type { CapabilityManifest, ResourceConfig, StreamResourceConfig, ConsumerResourceConfig, ResourceSnapshot, } from './manifest/types.js';
3
+ export { parseManifest, parseResource } from './manifest/validate.js';
4
+ export { digest } from './planning/digest.js';
5
+ export { planProvisioning, configurationDigest, resourceKey, desiredResources, } from './planning/plan.js';
6
+ export type { PlanOperation, ProvisioningPlan, OperationKind } from './planning/plan.js';
7
+ export { applyProvisioning } from './application/apply.js';
8
+ export type { ApplyRequest } from './application/apply.js';
9
+ export type { BrokerManagementPort, BrokerReadPort, DeploymentJournal, DeploymentAuthorization, ManagementLease, ApplyResult, OperationReceipt, } from './application/ports.js';
10
+ export { verifyProvisioning, provisioningStatus } from './application/verify.js';
11
+ export { provisionerConformance } from './conformance/policy.js';
12
+ export { NatsReadOnlyInspector } from './nats/inspector.js';
13
+ export type { NatsInspectionOptions, InspectResource, NatsInspectionStatus, NatsResourceStatus, } from './nats/types.js';
14
+ export type { ProvisionerSession, ReadOnlyProvisionerSession, DeploymentProvisionerSession, SessionContext, SessionFactory, } from './cli/session-types.js';
15
+ export { runProvisionerCli } from './cli/run.js';
16
+ export { NatsExclusiveWriter } from './nats/writer.js';
17
+ export type { NatsExclusiveAccess, NatsWriterOptions } from './nats/writer-types.js';
@@ -0,0 +1 @@
1
+ var ProvisionerError=class extends Error{constructor(code,message){super(message);this.code=code;this.name="ProvisionerError"}code};import{isSupportedMessagingProfile}from"@eventsdk/sdk/contracts";function parseDurability(outbox,inbox){if(outbox!=="owner-domain-transaction"&&outbox!=="native-event-log"&&outbox!=="not-applicable"||inbox!=="owner-durable"&&inbox!=="not-applicable"){throw new ProvisionerError("INVALID_MANIFEST","Explicit supported durability values are required.")}return{outbox,inbox}}function checkRoleDurability(manifest){const produces=manifest.emits.length>0;const consumes=manifest.consumes.length>0;if(!produces&&!consumes){throw new ProvisionerError("NO_CAPABILITIES","At least one explicit producer or consumer capability is required.")}if(produces===(manifest.durability.outbox==="not-applicable")||consumes===(manifest.durability.inbox==="not-applicable")){throw new ProvisionerError("ROLE_DURABILITY_CONFLICT","Durability must match the explicitly declared roles.")}}function checkCapabilities(m){const emittedTypes=new Set;const emittedSubjects=new Set;for(const emit of m.emits){if(emittedTypes.has(emit.type)||emittedSubjects.has(emit.subject))invalid();emittedTypes.add(emit.type);emittedSubjects.add(emit.subject);const streams=m.resources.filter(r=>r.kind==="stream"&&r.subjects.includes(emit.subject));if(streams.length!==1)invalid()}const consumerRefs=new Set;for(const consume of m.consumes){if(consumerRefs.has(consume.consumerRef))invalid();consumerRefs.add(consume.consumerRef);const resources=m.resources.filter(r=>r.kind==="consumer"&&r.name===consume.consumerRef);if(resources.length!==1||resources[0]?.subjects.length!==1||resources[0]?.subjects[0]!==consume.subject)invalid()}for(const resource2 of m.resources){if(resource2.kind==="stream"&&resource2.subjects.some(s=>!emittedSubjects.has(s)))invalid();if(resource2.kind==="consumer"&&!consumerRefs.has(resource2.name))invalid()}}function invalid(){throw new ProvisionerError("CAPABILITY_CONFLICT","Capabilities require explicit, unique resource/subject mappings.")}var token=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/;var subject=/^[a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)*$/;function object(value,keys){if(!value||typeof value!=="object"||Array.isArray(value)){throw new ProvisionerError("INVALID_MANIFEST","Expected an object.")}const record2=value;if(Object.keys(record2).some(key=>!keys.includes(key))){throw new ProvisionerError("INVALID_MANIFEST","Unknown or sensitive fields are not allowed.")}return record2}function string(value,pattern=token){if(typeof value!=="string"||!pattern.test(value)){throw new ProvisionerError("INVALID_MANIFEST","Invalid identifier.")}return value}function integer(value,min,max=Number.MAX_SAFE_INTEGER){if(!Number.isSafeInteger(value)||typeof value!=="number"||value<min||value>max){throw new ProvisionerError("INVALID_MANIFEST","Invalid bounded integer.")}return value}function list(value){if(!Array.isArray(value)||value.length>1e3){throw new ProvisionerError("INVALID_MANIFEST","Expected a bounded array.")}return value}function parseResource(value){const r=object(value,["kind","name","owner","deploymentDomainRef","subjects","retentionSeconds","replicas","maxBytes","stream","ackWaitSeconds","maxAckPending"]);if(r.kind!=="stream"&&r.kind!=="consumer"){throw new ProvisionerError("INVALID_MANIFEST","Unsupported resource kind.")}const subjects=list(r.subjects).map(v=>string(v,subject));if(!subjects.length||new Set(subjects).size!==subjects.length){throw new ProvisionerError("INVALID_MANIFEST","Subjects must be nonempty and unique.")}const base={name:string(r.name),owner:string(r.owner),deploymentDomainRef:string(r.deploymentDomainRef),subjects};if(r.kind==="stream"){if(r.stream!==void 0||r.ackWaitSeconds!==void 0||r.maxAckPending!==void 0){throw new ProvisionerError("INVALID_MANIFEST","Consumer fields on stream.")}return{...base,kind:"stream",retentionSeconds:integer(r.retentionSeconds,1),replicas:integer(r.replicas,1,5),maxBytes:integer(r.maxBytes,1)}}if(["retentionSeconds","replicas","maxBytes"].some(key=>key in r))throw new ProvisionerError("INVALID_MANIFEST","Stream capacity fields on consumer.");return{...base,kind:"consumer",stream:string(r.stream),ackWaitSeconds:integer(r.ackWaitSeconds,1),maxAckPending:integer(r.maxAckPending,1)}}function parseManifest(value){const m=object(value,["messagingProfile","service","emits","consumes","transport","durability","resources"]);const t=object(m.transport,["kind","deploymentDomainRef"]);const d=object(m.durability,["outbox","inbox"]);if(t.kind!=="nats-jetstream"){throw new ProvisionerError("INVALID_MANIFEST","Unsupported transport.")}if(!isSupportedMessagingProfile(m.messagingProfile))throw new ProvisionerError("INVALID_MANIFEST","Unsupported messaging profile.");const manifest={messagingProfile:m.messagingProfile,service:string(m.service),emits:list(m.emits).map(v=>{const x=object(v,["type","subject"]);return{type:string(x.type,subject),subject:string(x.subject,subject)}}),consumes:list(m.consumes).map(parseConsume),transport:{kind:"nats-jetstream",deploymentDomainRef:string(t.deploymentDomainRef)},durability:parseDurability(d.outbox,d.inbox),resources:list(m.resources).map(parseResource)};checkOwnership(manifest);checkCapabilities(manifest);checkRoleDurability(manifest);return manifest}function parseConsume(value){const c=object(value,["fromService","type","consumerRef","subject"]);return{fromService:string(c.fromService),type:string(c.type,subject),consumerRef:string(c.consumerRef),subject:string(c.subject,subject)}}function checkOwnership(m){const ids=new Set;for(const resource2 of m.resources){const id=resource2.kind+":"+(resource2.kind==="consumer"?resource2.stream:"")+":"+resource2.name;if(ids.has(id)||resource2.owner!==m.service||resource2.deploymentDomainRef!==m.transport.deploymentDomainRef){throw new ProvisionerError("INVALID_MANIFEST","Duplicate resource or ownership mismatch.")}ids.add(id)}}import{createHash}from"node:crypto";function canonical(value){if(value===null||typeof value!=="object"){const encoded=JSON.stringify(value);if(encoded===void 0)throw new TypeError("Undefined is not canonical JSON.");return encoded}if(Array.isArray(value))return"["+value.map(canonical).join(",")+"]";const record2=value;return"{"+Object.keys(record2).sort().map(key=>JSON.stringify(key)+":"+canonical(record2[key])).join(",")+"}"}function digest(value){return createHash("sha256").update(canonical(value)).digest("hex")}function resourceKey(r){return r.kind+":"+(r.kind==="consumer"?r.stream:"")+":"+r.name}function configurationDigest(observed){return digest(observed.map(s=>({config:parseResource(s.config),nativeConfigDigest:s.nativeConfigDigest??null})).sort((a,b)=>resourceKey(a.config).localeCompare(resourceKey(b.config))))}function planProvisioning(input,observed){const manifest=parseManifest(input);const configs=new Map;for(const snapshot of observed){const config=parseResource(snapshot.config);const key=resourceKey(config);if(configs.has(key))throw new ProvisionerError("AMBIGUOUS_RESOURCE","Duplicate observed resource.");configs.set(key,config)}const operations=[...manifest.resources].sort((a,b)=>Number(a.kind==="consumer")-Number(b.kind==="consumer")).map(r=>classify(r,configs.get(resourceKey(r))));for(const operation2 of operations){const r=operation2.resource;if(r.kind!=="consumer"||operation2.action!=="CREATE")continue;const parent=configs.get("stream::"+r.stream)??operations.find(o=>o.resource.kind==="stream"&&o.resource.name===r.stream&&o.action==="CREATE")?.resource;if(!parent||parent.kind!=="stream"||parent.deploymentDomainRef!==r.deploymentDomainRef||r.subjects.some(subject2=>!parent.subjects.includes(subject2))){operations[operations.indexOf(operation2)]={...operation2,action:"BLOCKED",reason:"PARENT_DEPENDENCY_UNPROVEN"}}}const body={manifestDigest:digest(manifest),observedConfigDigest:configurationDigest(observed),operations};return{...body,planRef:digest(body)}}function classify(desired,current){const beforeDigest=current?digest(current):null;const base={resource:desired,beforeDigest};if(!current)return{...base,action:"CREATE",reason:"RESOURCE_ABSENT"};if(digest(desired)===beforeDigest)return{...base,action:"NOOP",reason:"MATCH"};const blocked=unsafeChange(current,desired);return blocked?{...base,action:"BLOCKED",reason:blocked}:{...base,action:"SAFE_UPDATE",reason:"MONOTONIC_CAPACITY_UPDATE"}}function unsafeChange(a,b){if(a.owner!==b.owner||a.deploymentDomainRef!==b.deploymentDomainRef)return"OWNER_CONFLICT";if(a.kind!==b.kind||digest(a.subjects)!==digest(b.subjects))return"ROUTING_CHANGE";if(a.kind==="stream"&&b.kind==="stream"){if(b.retentionSeconds<a.retentionSeconds||b.replicas<a.replicas||b.maxBytes<a.maxBytes)return"DURABILITY_REDUCTION"}if(a.kind==="consumer"&&b.kind==="consumer"){if(a.stream!==b.stream)return"ROUTING_CHANGE";if(a.ackWaitSeconds!==b.ackWaitSeconds)return"DELIVERY_POLICY_CHANGE";if(b.maxAckPending<a.maxAckPending)return"CAPACITY_REDUCTION"}return void 0}function desiredResources(manifest){return manifest.resources}async function applyProvisioning(input){const manifest=parseManifest(structuredClone(input.manifest));const request={...input,manifest,plan:structuredClone(input.plan),authorization:structuredClone(input.authorization)};authorize(request,manifest.transport.deploymentDomainRef);request.signal.throwIfAborted();const lease=await request.broker.acquireLease(manifest.transport.deploymentDomainRef,request.signal);let result;let failure;let failed=false;try{const observed=await request.broker.read(request.signal);const actual=planProvisioning(manifest,observed);if(digest(actual)!==digest(request.plan)){throw new ProvisionerError("PLAN_DRIFT","Manifest, configuration or plan has changed.")}result=actual.operations.some(o=>o.action==="BLOCKED")?{planRef:actual.planRef,state:"BLOCKED",receipts:[]}:await execute({...request,plan:actual},lease,observed)}catch(error){failed=true;failure=error}let leaseReleased=true;try{await lease.release()}catch{leaseReleased=false}if(failed)throw failure;if(!result)throw new ProvisionerError("APPLY_INVARIANT","No apply result.");return{...result,leaseReleased}}function authorize(request,domain){const a=request.authorization;const manifest=parseManifest(request.manifest);if(a.role!=="deployment"||!a.subject||a.deploymentDomainRef!==domain||manifest.resources.some(r=>!a.allowedResourceKeys.includes(resourceKey(r))||r.subjects.some(s=>!a.allowedSubjects.includes(s)))){throw new ProvisionerError("UNAUTHORIZED_DEPLOYMENT","Explicit deployment authority is required.")}}async function execute(r,lease,observed){const receipts=[];let expected=configurationDigest(observed);for(const operation2 of r.plan.operations){let before;try{r.signal.throwIfAborted();before=await r.broker.read(r.signal)}catch{receipts.push({resourceKey:resourceKey(operation2.resource),action:operation2.action,outcome:"BLOCKED",reason:r.signal.aborted?"CANCELLED_BEFORE_WRITE":"READ_UNAVAILABLE"});return{planRef:r.plan.planRef,state:"PARTIAL",receipts}}if(configurationDigest(before)!==expected){receipts.push({resourceKey:resourceKey(operation2.resource),action:operation2.action,outcome:"BLOCKED",reason:"PLAN_DRIFT"});return{planRef:r.plan.planRef,state:"PARTIAL",receipts}}const receipt=await journaledExecute(r,lease,operation2);receipts.push(receipt);if(receipt.outcome!=="VERIFIED")return{planRef:r.plan.planRef,state:"PARTIAL",receipts};if(operation2.action==="NOOP")continue;const desired=before.filter(s=>resourceKey(s.config)!==resourceKey(operation2.resource));expected=configurationDigest([...desired,{config:operation2.resource,...receipt.nativeConfigDigest===void 0?{}:{nativeConfigDigest:receipt.nativeConfigDigest}}])}return verifyFinalConfiguration(r,expected,receipts)}async function verifyFinalConfiguration(r,expected,receipts){try{r.signal.throwIfAborted();if(configurationDigest(await r.broker.read(r.signal))!==expected){receipts.push({resourceKey:"plan",action:"VERIFY",outcome:"BLOCKED",reason:"PLAN_DRIFT"});return{planRef:r.plan.planRef,state:"PARTIAL",receipts}}return{planRef:r.plan.planRef,state:"VERIFIED",receipts}}catch{receipts.push({resourceKey:"plan",action:"VERIFY",outcome:"UNKNOWN",reason:"FINAL_READ_UNAVAILABLE"});return{planRef:r.plan.planRef,state:"PARTIAL",receipts}}}async function executeOne(r,lease,operation2){const base={resourceKey:resourceKey(operation2.resource),action:operation2.action};try{if(operation2.action==="CREATE")await r.broker.create(structuredClone(operation2.resource),lease,r.signal);if(operation2.action==="SAFE_UPDATE"){if(!operation2.beforeDigest)throw new ProvisionerError("INVALID_PLAN","Missing prior digest.");await r.broker.update(structuredClone(operation2.resource),operation2.beforeDigest,lease,r.signal)}const after=await r.broker.read(r.signal);const actual=after.find(s=>resourceKey(s.config)===base.resourceKey);if(actual&&digest(actual.config)===digest(operation2.resource)){return{...base,outcome:"VERIFIED",reason:"READBACK_MATCH",observedDigest:digest(actual.config),...actual.nativeConfigDigest===void 0?{}:{nativeConfigDigest:actual.nativeConfigDigest}}}return{...base,outcome:"UNKNOWN",reason:"READBACK_MISMATCH"}}catch{return{...base,outcome:"UNKNOWN",reason:"WRITE_OR_READBACK_UNCERTAIN"}}}async function journaledExecute(r,lease,operation2){const resource2=resourceKey(operation2.resource);if(!r.journal)return executeOne(r,lease,operation2);try{await r.journal.append({planRef:r.plan.planRef,resourceKey:resource2,phase:"INTENT"},r.signal)}catch{return{resourceKey:resource2,action:operation2.action,outcome:"BLOCKED",reason:"JOURNAL_INTENT_FAILED",journalPersisted:false}}const receipt=await executeOne(r,lease,operation2);try{await r.journal.append({planRef:r.plan.planRef,resourceKey:resource2,phase:"RESULT",receipt:structuredClone(receipt)},r.signal);return{...receipt,journalPersisted:true}}catch{return{...receipt,outcome:"UNKNOWN",reason:"JOURNAL_RESULT_FAILED",journalPersisted:false}}}async function verifyProvisioning(manifest,broker,signal){signal.throwIfAborted();const plan=planProvisioning(manifest,await broker.read(signal));return{verified:plan.operations.every(o=>o.action==="NOOP"),plan}}async function provisioningStatus(broker,signal){signal.throwIfAborted();return broker.read(signal)}function rolePolicyCases(producer){const stream=producer.resources[0];if(!stream)throw new Error("Conformance fixture requires a producer stream.");const consumer=consumerFixture(producer,stream);return roleCases(producer,consumer)}function consumerFixture(producer,stream){return{...producer,emits:[],consumes:[{fromService:"upstream",type:"fixture.created.v1",subject:"fixture.created.v1",consumerRef:"worker"}],resources:[{owner:stream.owner,deploymentDomainRef:stream.deploymentDomainRef,subjects:stream.subjects,kind:"consumer",name:"worker",stream:"existing-upstream",ackWaitSeconds:30,maxAckPending:10}],durability:{outbox:"not-applicable",inbox:"owner-durable"}}}function roleCases(producer,consumer){return[{name:"producer-without-inbox",passed:parseManifest(producer).durability.inbox==="not-applicable"},{name:"consumer-without-outbox-or-stream-create",passed:planProvisioning(consumer,[]).operations.every(o=>o.resource.kind==="consumer")},{name:"reject-empty-roles",passed:rejects(()=>parseManifest({...producer,emits:[],resources:[],durability:{outbox:"not-applicable",inbox:"not-applicable"}}))},{name:"reject-role-durability-mismatch",passed:rejects(()=>parseManifest({...producer,durability:{outbox:"owner-domain-transaction",inbox:"owner-durable"}}))}]}function rejects(run){try{run();return false}catch{return true}}function provisionerConformance(){const resource2={kind:"stream",name:"fixture",owner:"fixture",deploymentDomainRef:"isolated",subjects:["fixture.created.v1"],retentionSeconds:60,replicas:1,maxBytes:1024};const manifest={messagingProfile:"eventsdk/1.0",service:"fixture",emits:[{type:"fixture.created.v1",subject:"fixture.created.v1"}],consumes:[],transport:{kind:"nats-jetstream",deploymentDomainRef:"isolated"},durability:{outbox:"owner-domain-transaction",inbox:"not-applicable"},resources:[resource2]};const tests=[...rolePolicyCases(parseManifest(manifest)),{name:"create-absent",passed:planProvisioning(manifest,[]).operations[0]?.action==="CREATE"},{name:"repeat-noop",passed:planProvisioning(manifest,[{config:resource2}]).operations[0]?.action==="NOOP"},{name:"block-downgrade",passed:planProvisioning(manifest,[{config:{...resource2,replicas:3}}]).operations[0]?.action==="BLOCKED"},{name:"reject-secret-fields",passed:rejects2(()=>parseManifest({...manifest,token:"fixture"}))}];return{suite:"provisioner-offline-policy/1.0",scope:"OFFLINE_POLICY_ONLY",passed:tests.every(t=>t.passed),tests}}function rejects2(run){try{run();return false}catch{return true}}import{JetStreamApiError}from"@nats-io/jetstream";import{boundedOperation}from"@eventsdk/sdk/core";function streamSnapshot(info,domain){const c=info.config;if(c.mirror||c.sources?.length||c.subject_transform||c.republish||c.retention!=="limits"||c.storage!=="file"){throw new ProvisionerError("UNSUPPORTED_BROKER_CONFIG","Unsupported replication or retention policy.")}const config=parseResource({kind:"stream",name:c.name,owner:c.metadata?.["eventsdk.owner"],deploymentDomainRef:c.metadata?.["eventsdk.deployment-domain"],subjects:c.subjects,retentionSeconds:c.max_age/1e9,replicas:c.num_replicas,maxBytes:c.max_bytes});assertDomain(config,domain);return{config,nativeConfigDigest:digest(c),messages:info.state.messages,bytes:info.state.bytes}}function consumerSnapshot(info,stream,domain){const c=info.config;if(c.ack_policy!=="explicit"||c.deliver_subject||!c.durable_name||c.deliver_policy!=="all"||c.num_replicas!==void 0&&c.num_replicas!==0||c.mem_storage===true||Boolean(c.backoff?.length))throw new ProvisionerError("UNSUPPORTED_BROKER_CONFIG","Unsupported consumer policy.");streamSnapshot(stream,domain);const config=parseResource({kind:"consumer",name:info.name,stream:info.stream_name,owner:c.metadata?.["eventsdk.owner"],deploymentDomainRef:c.metadata?.["eventsdk.deployment-domain"],subjects:c.filter_subjects??(c.filter_subject?[c.filter_subject]:[]),ackWaitSeconds:(c.ack_wait??0)/1e9,maxAckPending:c.max_ack_pending});assertDomain(config,domain);return{config,nativeConfigDigest:digest(c),messages:stream.state.messages,bytes:stream.state.bytes,consumerPending:info.num_pending}}function assertDomain(config,domain){if(config.deploymentDomainRef!==domain)throw new ProvisionerError("DOMAIN_MISMATCH","Unexpected deployment domain metadata.")}function resourceStatus(stream,consumer){const c=consumer?.config??stream.config;return{kind:consumer?"consumer":"stream",name:consumer?.name??stream.config.name,stream:stream.config.name,owner:c.metadata?.["eventsdk.owner"]??null,deploymentDomainRef:c.metadata?.["eventsdk.deployment-domain"]??null,messages:stream.state.messages,bytes:stream.state.bytes,firstSequence:stream.state.first_seq,lastSequence:stream.state.last_seq,pending:consumer?.num_pending??0,ackPending:consumer?.num_ack_pending??0,ackFloor:consumer?.ack_floor.stream_seq??null,nativeConfigDigest:digest(c)}}var NatsReadOnlyInspector=class{constructor(manager,options){this.manager=manager;this.options=structuredClone(options);if(!options.deploymentDomainRef||new Set(options.resources.map(r=>r.kind+":"+(r.stream??"")+":"+r.name)).size!==options.resources.length||options.resources.length>1e3||options.resources.some(r=>!["stream","consumer"].includes(r.kind)||r.kind==="stream"&&r.stream!==void 0||!/^[A-Za-z0-9_-]+$/.test(r.name)||r.kind==="consumer"&&(!r.stream||!/^[A-Za-z0-9_-]+$/.test(r.stream)))){throw new ProvisionerError("INVALID_INSPECTION_OPTIONS","Invalid explicit resource selection.")}}manager;options;async read(signal){return boundedOperation({signal,timeoutMs:this.options.timeoutMs},async bounded=>{await this.account(bounded);const snapshots2=new Map;for(const ref of this.options.resources){bounded.throwIfAborted();const info=await this.resource(ref);if(!info)continue;const parent=streamSnapshot(info.stream,this.options.deploymentDomainRef);const key="stream::"+parent.config.name;const previous=snapshots2.get(key);if(previous&&previous.nativeConfigDigest!==parent.nativeConfigDigest)throw new ProvisionerError("PLAN_DRIFT","Parent changed during inspection.");snapshots2.set(key,parent);if(info.consumer)snapshots2.set("consumer:"+info.consumer.stream_name+":"+info.consumer.name,consumerSnapshot(info.consumer,info.stream,this.options.deploymentDomainRef))}return[...snapshots2.values()]})}async status(signal){return boundedOperation({signal,timeoutMs:this.options.timeoutMs},async bounded=>{const account=await this.account(bounded);const resources=[];for(const ref of this.options.resources){bounded.throwIfAborted();const info=await this.resource(ref);if(info&&(ref.kind==="stream"||info.consumer))resources.push(resourceStatus(info.stream,info.consumer))}return{mode:"LIVE_READ_ONLY",natsDomain:account.domain??null,accountIdentity:"AUTHENTICATED_CONNECTION_NOT_EXPOSED_BY_JS_API",storageBytes:account.storage,memoryBytes:account.memory,maxStorageBytes:account.limits.max_storage,maxMemoryBytes:account.limits.max_memory,streamCount:account.streams,consumerCount:account.consumers,resources}})}async account(signal){signal.throwIfAborted();const result=await this.manager.getAccountInfo().catch(()=>{throw new ProvisionerError("BROKER_READ_UNAVAILABLE","Authenticated account read failed.")});if((result.domain??null)!==this.options.expectedNatsDomain){throw new ProvisionerError("DOMAIN_MISMATCH","Unexpected authenticated NATS domain.")}return result}async resource(ref){try{const stream=await this.manager.streams.info(ref.stream??ref.name);if(ref.kind==="stream")return{stream};const consumer=await this.manager.consumers.info(ref.stream??"",ref.name).catch(error=>{if(error instanceof JetStreamApiError&&error.status===404&&error.code===10014)return void 0;throw error});return consumer?{stream,consumer}:{stream}}catch(error){if(error instanceof JetStreamApiError&&error.status===404&&(error.code===10059||error.code===10014))return null;throw new ProvisionerError("BROKER_READ_UNAVAILABLE","Authoritative broker read failed.")}}};function parseArguments(args){const command=args[0];if(!["validate","plan","apply","verify","status","conformance"].includes(command??""))usage();const options=new Map;const positional=[];for(let index=1;index<args.length;index++){const value=args[index];if(!value)usage();if(!value.startsWith("--")){positional.push(value);continue}if(!["--adapter","--plan","--timeout-ms"].includes(value)||options.has(value))usage();const argument=args[++index];if(!argument||argument.startsWith("--"))usage();options.set(value,argument)}const timeoutMs=Number(options.get("--timeout-ms")??"10000");if(!Number.isSafeInteger(timeoutMs)||timeoutMs<10||timeoutMs>12e4)usage();const adapterPath=options.get("--adapter");const planPath=options.get("--plan");if(planPath&&command!=="apply")usage();if(adapterPath&&["validate","conformance"].includes(command??""))usage();const expected=command==="conformance"||command==="status"&&adapterPath?0:command==="status"||command==="validate"||command==="apply"||adapterPath?1:2;if(positional.length!==expected)usage();if(command==="apply"&&adapterPath&&!planPath)usage();return{command,timeoutMs,...adapterPath===void 0?{}:{adapterPath},...planPath===void 0?{}:{planPath},...positional[0]===void 0?{}:command==="status"?{snapshotPath:positional[0]}:{manifestPath:positional[0]},...positional[1]===void 0?{}:{snapshotPath:positional[1]}}}function usage(){throw new ProvisionerError("CLI_USAGE","Invalid or ambiguous command arguments.")}import{open,stat}from"node:fs/promises";import{parseJsonStrict}from"@eventsdk/sdk/contracts";async function readCliJson(path){if(!path)throw new ProvisionerError("CLI_USAGE","A JSON file path is required.");if(!(await stat(path)).isFile()){throw new ProvisionerError("CLI_INPUT_ERROR","Expected a regular JSON file.")}const file=await open(path,"r");try{if(!(await file.stat()).isFile())throw new ProvisionerError("CLI_INPUT_ERROR","Expected a regular JSON file.");const limit=1048576;const bytes=new Uint8Array(limit+1);let used=0;while(used<=limit){const chunk=await file.read(bytes,used,bytes.length-used,null);if(chunk.bytesRead===0)break;used+=chunk.bytesRead;if(used>limit)throw new ProvisionerError("INPUT_TOO_LARGE","Input exceeds 1 MiB.")}return parseJsonStrict(bytes.subarray(0,used))}finally{await file.close()}}import{boundedOperation as boundedOperation3}from"@eventsdk/sdk/core";async function readReviewedPlan(path){const value=await readCliJson(path);const p=record(value,["manifestDigest","observedConfigDigest","planRef","operations"]);if(!Array.isArray(p.operations)||p.operations.length>1e3)invalid2();return{manifestDigest:hash(p.manifestDigest),observedConfigDigest:hash(p.observedConfigDigest),planRef:hash(p.planRef),operations:p.operations.map(operation)}}function operation(value){const o=record(value,["action","resource","beforeDigest","reason"]);if(!["NOOP","CREATE","SAFE_UPDATE","BLOCKED"].includes(String(o.action))||typeof o.reason!=="string"||!/^[A-Z0-9_]{1,128}$/.test(o.reason))invalid2();return{action:o.action,resource:parseResource(o.resource),beforeDigest:o.beforeDigest===null?null:hash(o.beforeDigest),reason:o.reason}}function record(value,keys){if(!value||typeof value!=="object"||Array.isArray(value)||Object.keys(value).some(key=>!keys.includes(key)))invalid2();return value}function hash(value){if(typeof value!=="string"||!/^[a-f0-9]{64}$/.test(value))invalid2();return value}function invalid2(){throw new ProvisionerError("INVALID_PLAN","Reviewed plan object required.")}import{realpath,stat as stat2}from"node:fs/promises";import{isAbsolute,extname}from"node:path";import{pathToFileURL}from"node:url";import{boundedOperation as boundedOperation2}from"@eventsdk/sdk/core";async function closeSession(session){try{await boundedOperation2({timeoutMs:1e3},signal=>session.close(signal));return true}catch{return false}}async function loadSession(path,context,timeoutMs){if(!isAbsolute(path)||![".mjs",".js"].includes(extname(path))){throw new ProvisionerError("INVALID_ADAPTER_PATH","Explicit absolute local ESM path required.")}return boundedOperation2({signal:context.signal,timeoutMs},async signal=>{const canonical2=await realpath(path);if(![".mjs",".js"].includes(extname(canonical2))||!(await stat2(canonical2)).isFile()){throw new ProvisionerError("INVALID_ADAPTER_PATH","Adapter must be a local regular ESM file.")}signal.throwIfAborted();const module=await import(pathToFileURL(canonical2).href);signal.throwIfAborted();const factory=field(module,"createProvisionerSession");if(typeof factory!=="function")throw new ProvisionerError("INVALID_ADAPTER_MODULE","Missing session factory.");const value=await factory({...context,signal,...context.manifest?{manifest:structuredClone(context.manifest)}:{}});if(signal.aborted){await cleanupUnvalidated(value);signal.throwIfAborted()}try{return validateSession(value)}catch(error){await cleanupUnvalidated(value);throw error}})}function field(value,key){return value&&typeof value==="object"?value[key]:void 0}function method(value,key){return typeof field(value,key)==="function"}function validateSession(value){const mode=field(value,"mode");const broker=field(value,"broker");if(field(value,"apiVersion")!=="eventsdk-provisioner-session/1"||!["read-only","deployment"].includes(String(mode))||!method(value,"close")||field(value,"status")!==void 0&&!method(value,"status")||!method(broker,"read")){throw new ProvisionerError("INVALID_ADAPTER_SESSION","Unsupported session contract.")}if(mode==="deployment"&&(!method(broker,"acquireLease")||!method(broker,"create")||!method(broker,"update")||!method(field(value,"journal"),"append")||!field(value,"authorization"))){throw new ProvisionerError("INVALID_ADAPTER_SESSION","Deployment requires authority, fencing port and journal.")}return value}async function cleanupUnvalidated(value){if(method(value,"close"))await closeSession(value)}function validateNatsStatus(value){const s=object2(value,["mode","natsDomain","accountIdentity","storageBytes","memoryBytes","maxStorageBytes","maxMemoryBytes","streamCount","consumerCount","resources"]);if(s.mode!=="LIVE_READ_ONLY"||s.accountIdentity!=="AUTHENTICATED_CONNECTION_NOT_EXPOSED_BY_JS_API"||!Array.isArray(s.resources)||s.resources.length>1e3)invalid3();return{mode:"LIVE_READ_ONLY",natsDomain:nullableName(s.natsDomain),accountIdentity:"AUTHENTICATED_CONNECTION_NOT_EXPOSED_BY_JS_API",storageBytes:integer2(s.storageBytes),memoryBytes:integer2(s.memoryBytes),maxStorageBytes:integer2(s.maxStorageBytes,-1),maxMemoryBytes:integer2(s.maxMemoryBytes,-1),streamCount:integer2(s.streamCount),consumerCount:integer2(s.consumerCount),resources:s.resources.map(resource)}}function resource(value){const r=object2(value,["kind","name","stream","owner","deploymentDomainRef","messages","bytes","firstSequence","lastSequence","pending","ackPending","ackFloor","nativeConfigDigest"]);if(r.kind!=="stream"&&r.kind!=="consumer"||typeof r.nativeConfigDigest!=="string"||!/^[a-f0-9]{64}$/.test(r.nativeConfigDigest))invalid3();return{kind:r.kind,name:name(r.name),stream:name(r.stream),owner:nullableName(r.owner),deploymentDomainRef:nullableName(r.deploymentDomainRef),messages:integer2(r.messages),bytes:integer2(r.bytes),firstSequence:integer2(r.firstSequence),lastSequence:integer2(r.lastSequence),pending:integer2(r.pending),ackPending:integer2(r.ackPending),ackFloor:r.ackFloor===null?null:integer2(r.ackFloor),nativeConfigDigest:r.nativeConfigDigest}}function object2(value,keys){if(!value||typeof value!=="object"||Array.isArray(value)||Object.keys(value).some(key=>!keys.includes(key)))invalid3();return value}function integer2(value,minimum=0){if(typeof value!=="number"||!Number.isSafeInteger(value)||value<minimum)invalid3();return value}function name(value){if(typeof value!=="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(value))invalid3();return value}function nullableName(value){return value===null?null:name(value)}function invalid3(){throw new ProvisionerError("INVALID_STATUS","Unsupported live status shape.")}var JournalProgress=class{constructor(delegate,plan){this.plan=plan;this.journal={append:async(entry,signal)=>{const operation2=plan.operations.find(o=>resourceKey(o.resource)===entry.resourceKey);if(!operation2||entry.planRef!==plan.planRef)throw new Error("Journal entry outside plan.");this.receipts.set(entry.resourceKey,{resourceKey:entry.resourceKey,action:operation2.action,outcome:"UNKNOWN",reason:"JOURNAL_OR_APPLY_UNCONFIRMED",journalPersisted:false});const receipt=entry.receipt===void 0?void 0:structuredClone(entry.receipt);await delegate.append(structuredClone(entry),signal);if(entry.phase==="RESULT"&&receipt){this.receipts.set(entry.resourceKey,{...receipt,journalPersisted:true})}}}}plan;receipts=new Map;journal;interrupted(){return{planRef:this.plan.planRef,state:"PARTIAL",receipts:[...this.receipts.values()]}}};async function runLiveCommand(args,manifest,signal){if(!args.adapterPath)throw new ProvisionerError("LIVE_ADAPTER_REQUIRED","Explicit adapter required.");const reviewedPlan=args.command==="apply"?await readReviewedPlan(args.planPath):void 0;const command=args.command;const session=await loadSession(args.adapterPath,{command,signal,...manifest===void 0?{}:{manifest}},args.timeoutMs);let result;try{result=await dispatch(args,manifest,session,signal,reviewedPlan)}catch(error){await closeSession(session);throw error}const closed=await closeSession(session);return{value:{mode:"LIVE_ADAPTER",result:result.value,sessionClosed:closed},exitCode:closed?result.exitCode:4}}async function dispatch(args,manifest,session,signal,reviewedPlan){if(args.command==="apply"){if(session.mode!=="deployment")throw new ProvisionerError("READ_ONLY_SESSION","Apply requires a deployment session.");if(!manifest)throw new ProvisionerError("CLI_USAGE","Manifest required.");if(!reviewedPlan)throw new ProvisionerError("INVALID_PLAN","Reviewed plan required.");return runApply(args,manifest,session,signal,reviewedPlan)}return boundedOperation3({signal,timeoutMs:args.timeoutMs},async bounded=>{if(args.command==="status"){if(session.status)return{value:{coverage:"NATS_TELEMETRY",status:validateNatsStatus(await session.status(bounded))},exitCode:0};return{value:{coverage:"RESOURCES_ONLY",resources:safeSnapshots(await session.broker.read(bounded))},exitCode:0}}if(!manifest)throw new ProvisionerError("CLI_USAGE","Manifest required.");if(args.command==="verify"){const result=await verifyProvisioning(manifest,session.broker,bounded);return{value:result,exitCode:result.verified?0:3}}const plan=planProvisioning(manifest,await session.broker.read(bounded));return{value:{plan},exitCode:plan.operations.some(o=>o.action==="BLOCKED")?3:0}})}async function runApply(args,manifest,session,signal,plan){const progress=new JournalProgress(session.journal,plan);try{const result=await boundedOperation3({signal,timeoutMs:args.timeoutMs},bounded=>applyProvisioning({manifest,plan,broker:session.broker,authorization:session.authorization,journal:progress.journal,signal:bounded}));return{value:result,exitCode:result.state==="VERIFIED"&&result.leaseReleased!==false?0:4}}catch(error){if(isInterrupted(error))return{value:{...progress.interrupted(),interruption:"CANCELLED_OR_TIMEOUT"},exitCode:4};throw error}}function isInterrupted(error){return error instanceof Error&&"code"in error&&["ABORTED","TIMEOUT"].includes(String(error.code))}function safeSnapshots(snapshots2){if(snapshots2.length>1e3)throw new ProvisionerError("INVALID_SNAPSHOT","Too many resources.");return snapshots2.map(s=>{const config=parseResource(s.config);const counters={};for(const key of["messages","bytes","consumerPending"]){if(s[key]!==void 0){if(!Number.isSafeInteger(s[key])||s[key]<0)throw new ProvisionerError("INVALID_SNAPSHOT","Invalid resource counters.");counters[key]=s[key]}}return{config,...counters}})}function snapshots(value){if(!Array.isArray(value))throw new ProvisionerError("INVALID_SNAPSHOT","Expected configuration array.");return value.map(config=>({config:parseResource(config)}))}async function runProvisionerCli(args,signal){const options=parseArguments(args);if(options.command==="conformance"){const result=provisionerConformance();return{value:result,exitCode:result.passed?0:3}}if(options.command==="apply"&&!options.adapterPath){throw new ProvisionerError("LIVE_ADAPTER_REQUIRED","Explicit trusted deployment adapter required.")}const manifest=options.command==="status"?void 0:parseManifest(await readCliJson(options.manifestPath));if(options.adapterPath)return runLiveCommand(options,manifest,signal);if(options.command==="status"){return{value:{mode:"OFFLINE_SNAPSHOT",resources:snapshots(await readCliJson(options.snapshotPath))},exitCode:0}}if(options.command==="validate")return{value:{valid:true,manifest},exitCode:0};const plan=planProvisioning(manifest,snapshots(await readCliJson(options.snapshotPath)));const verified=plan.operations.every(o=>o.action==="NOOP");return{value:{mode:"OFFLINE_SNAPSHOT",verified,plan},exitCode:options.command==="verify"&&!verified||plan.operations.some(o=>o.action==="BLOCKED")?3:0}}import{boundedOperation as boundedOperation5}from"@eventsdk/sdk/core";import{AckPolicy,DeliverPolicy,RetentionPolicy,ReplayPolicy,StorageType}from"@nats-io/jetstream";function metadata(r){return{"eventsdk.owner":r.owner,"eventsdk.deployment-domain":r.deploymentDomainRef}}function nanos(seconds){const value=seconds*1e9;if(!Number.isSafeInteger(value))throw new ProvisionerError("INVALID_MANIFEST","Duration exceeds exact NATS nanoseconds.");return value}function streamCreate(r){return{name:r.name,subjects:[...r.subjects],max_age:nanos(r.retentionSeconds),max_bytes:r.maxBytes,num_replicas:r.replicas,retention:RetentionPolicy.Limits,storage:StorageType.File,metadata:metadata(r)}}function consumerCreate(r){return{durable_name:r.name,name:r.name,filter_subjects:[...r.subjects],ack_policy:AckPolicy.Explicit,deliver_policy:DeliverPolicy.All,replay_policy:ReplayPolicy.Instant,ack_wait:nanos(r.ackWaitSeconds),max_ack_pending:r.maxAckPending,metadata:metadata(r)}}function assertSafeUpdate(current,next){if(current.kind!==next.kind||current.name!==next.name||current.owner!==next.owner||current.deploymentDomainRef!==next.deploymentDomainRef||digest(current.subjects)!==digest(next.subjects))throw new ProvisionerError("UNSAFE_UPDATE","Resource identity and routing are immutable.");if(current.kind==="stream"&&next.kind==="stream"){if(next.retentionSeconds<current.retentionSeconds||next.maxBytes<current.maxBytes||next.replicas<current.replicas)throw new ProvisionerError("UNSAFE_UPDATE","Durability reduction is forbidden.")}if(current.kind==="consumer"&&next.kind==="consumer"){if(next.stream!==current.stream||next.ackWaitSeconds!==current.ackWaitSeconds||next.maxAckPending<current.maxAckPending)throw new ProvisionerError("UNSAFE_UPDATE","Consumer position and policy are immutable.")}}function streamUpdate(current,next){return{...structuredClone(current),max_age:nanos(next.retentionSeconds),max_bytes:next.maxBytes,num_replicas:next.replicas}}async function createResource(manager,resource2,issue){if(resource2.kind==="stream"){const config=streamCreate(resource2);issue();await manager.streams.add(config)}else{const config=consumerCreate(resource2);issue();await manager.consumers.add(resource2.stream,config)}}async function updateResource(manager,resource2,observed,issue){if(resource2.kind==="stream"){const info=await manager.streams.info(resource2.name);assertNativeDigest(info.config,observed);const config=streamUpdate(info.config,resource2);issue();await manager.streams.update(resource2.name,config)}else{const info=await manager.consumers.info(resource2.stream,resource2.name);assertNativeDigest(info.config,observed);const config={...structuredClone(info.config),max_ack_pending:resource2.maxAckPending};issue();await manager.consumers.update(resource2.stream,resource2.name,config)}}function assertNativeDigest(config,observed){if(!observed.nativeConfigDigest||digest(config)!==observed.nativeConfigDigest)throw new ProvisionerError("PLAN_DRIFT","Native configuration changed before write.")}import{boundedOperation as boundedOperation4}from"@eventsdk/sdk/core";var NatsWriteSession=class{constructor(timeoutMs){this.timeoutMs=timeoutMs}timeoutMs;#active;#generation=0;#busy=false;#poisoned=false;acquire(){if(this.#poisoned)throw new ProvisionerError("WRITE_SESSION_UNCERTAIN","Reconcile first.");if(this.#active)throw new ProvisionerError("MANAGEMENT_BUSY","Session already leased.");const lease=Object.freeze({generation:String(++this.#generation),release:async()=>{this.assert(lease);this.#active=void 0;if(this.#busy||this.#poisoned){this.#poisoned=true;throw new ProvisionerError("WRITE_SESSION_UNCERTAIN","Outstanding write may exist.")}}});this.#active=lease;return lease}assert(lease){if(this.#poisoned)throw new ProvisionerError("WRITE_SESSION_UNCERTAIN","Reconcile first.");if(this.#active!==lease)throw new ProvisionerError("STALE_MANAGEMENT_LEASE","Lease does not own this session.")}assertReadable(){if(this.#busy)throw new ProvisionerError("MANAGEMENT_BUSY","Mutation in progress.")}async run(lease,signal,operation2){this.assert(lease);if(this.#busy)throw new ProvisionerError("MANAGEMENT_BUSY","Mutation in progress.");this.#busy=true;let issued=false;try{await boundedOperation4({signal,timeoutMs:this.timeoutMs},async bounded=>{await operation2(bounded,()=>{bounded.throwIfAborted();this.assert(lease);issued=true});bounded.throwIfAborted();this.assert(lease)})}catch(error){if(issued)this.#poisoned=true;throw error}finally{this.#busy=false}}};var NatsExclusiveWriter=class{constructor(manager,options){this.manager=manager;const{exclusiveAccess,...data}=options;if(exclusiveAccess?.mode!=="exclusive-session"||!/^[A-Za-z0-9][A-Za-z0-9_.:/-]{0,255}$/.test(exclusiveAccess.policyRef)||typeof exclusiveAccess.assertHeld!=="function")throw new ProvisionerError("EXCLUSIVE_ACCESS_REQUIRED","Explicit exclusive policy required.");this.#options=structuredClone(data);this.#exclusive=Object.freeze({mode:exclusiveAccess.mode,policyRef:exclusiveAccess.policyRef,assertHeld:exclusiveAccess.assertHeld.bind(exclusiveAccess)});this.#inspector=new NatsReadOnlyInspector(manager,data);this.#session=new NatsWriteSession(data.timeoutMs)}manager;#inspector;#session;#options;#exclusive;#observed;async read(signal){this.#session.assertReadable();const values=await this.#inspector.read(signal);this.#observed=structuredClone(values);return values}status(signal){return this.#inspector.status(signal)}async acquireLease(domain,signal){signal.throwIfAborted();if(domain!==this.#options.deploymentDomainRef)throw new ProvisionerError("DOMAIN_MISMATCH","Wrong management domain.");await boundedOperation5({signal,timeoutMs:this.#options.timeoutMs},bounded=>this.#exclusive.assertHeld(bounded));signal.throwIfAborted();return this.#session.acquire()}async create(config,lease,signal){await this.mutate(config,void 0,lease,signal)}async update(config,expectedDigest,lease,signal){await this.mutate(config,expectedDigest,lease,signal)}async mutate(input,expectedDigest,lease,signal){const config=parseResource(structuredClone(input));this.authorize(config);const observed=structuredClone(this.#observed);await this.#session.run(lease,signal,async(bounded,issue)=>{await this.#exclusive.assertHeld(bounded);const values=await this.#inspector.read(bounded);const current=this.validateObservation(values,observed,config,expectedDigest);await this.#exclusive.assertHeld(bounded);if(current)await updateResource(this.manager,config,current,issue);else await createResource(this.manager,config,issue);const after=await this.#inspector.read(bounded);const actual=after.find(s=>resourceKey(s.config)===resourceKey(config));if(!actual||digest(actual.config)!==digest(config))throw new ProvisionerError("WRITE_UNCONFIRMED","Written configuration did not verify.");this.#observed=structuredClone(after)})}validateObservation(values,observed,config,expectedDigest){if(!observed)throw new ProvisionerError("OBSERVATION_REQUIRED","Read authoritative configuration first.");if(configurationDigest(values)!==configurationDigest(observed))throw new ProvisionerError("PLAN_DRIFT","Configuration changed before mutation.");const current=values.find(s=>resourceKey(s.config)===resourceKey(config));if(expectedDigest===void 0){if(current)throw new ProvisionerError("RESOURCE_EXISTS","CREATE cannot replace resources.");assertParent(values,config);return void 0}if(!current||digest(current.config)!==expectedDigest)throw new ProvisionerError("PLAN_DRIFT","Expected resource configuration changed.");assertSafeUpdate(current.config,config);return current}authorize(config){const a=this.#options.authorization;const selected=this.#options.resources.some(r=>r.kind+":"+(r.stream??"")+":"+r.name===resourceKey(config));if(a.role!=="deployment"||!a.subject||!selected||config.deploymentDomainRef!==this.#options.deploymentDomainRef||a.deploymentDomainRef!==config.deploymentDomainRef||!a.allowedResourceKeys.includes(resourceKey(config))||config.subjects.some(s=>!a.allowedSubjects.includes(s)))throw new ProvisionerError("UNAUTHORIZED_DEPLOYMENT","Resource outside deployment grant.")}};function assertParent(values,config){if(config.kind!=="consumer")return;const parent=values.find(s=>s.config.kind==="stream"&&s.config.name===config.stream);if(!parent||config.subjects.some(s=>!parent.config.subjects.includes(s)))throw new ProvisionerError("PARENT_DEPENDENCY_UNPROVEN","Consumer parent is not verified.")}export{NatsExclusiveWriter,NatsReadOnlyInspector,ProvisionerError,applyProvisioning,configurationDigest,desiredResources,digest,parseManifest,parseResource,planProvisioning,provisionerConformance,provisioningStatus,resourceKey,runProvisionerCli,verifyProvisioning};
@@ -0,0 +1,55 @@
1
+ import type { SupportedMessagingProfile } from '@eventsdk/sdk/contracts';
2
+ interface ResourceBase {
3
+ readonly name: string;
4
+ readonly owner: string;
5
+ readonly deploymentDomainRef: string;
6
+ readonly subjects: readonly string[];
7
+ }
8
+ export interface StreamResourceConfig extends ResourceBase {
9
+ readonly kind: 'stream';
10
+ readonly retentionSeconds: number;
11
+ readonly replicas: number;
12
+ readonly maxBytes: number;
13
+ }
14
+ export interface ConsumerResourceConfig extends ResourceBase {
15
+ readonly kind: 'consumer';
16
+ readonly stream: string;
17
+ readonly ackWaitSeconds: number;
18
+ readonly maxAckPending: number;
19
+ }
20
+ export type ResourceConfig = StreamResourceConfig | ConsumerResourceConfig;
21
+ export interface CapabilityManifest {
22
+ readonly messagingProfile: SupportedMessagingProfile;
23
+ readonly service: string;
24
+ readonly emits: readonly {
25
+ readonly type: string;
26
+ readonly subject: string;
27
+ }[];
28
+ readonly consumes: readonly {
29
+ readonly fromService: string;
30
+ readonly type: string;
31
+ readonly consumerRef: string;
32
+ readonly subject: string;
33
+ }[];
34
+ readonly transport: {
35
+ readonly kind: 'nats-jetstream';
36
+ readonly deploymentDomainRef: string;
37
+ };
38
+ readonly durability: {
39
+ readonly outbox: 'owner-domain-transaction' | 'native-event-log' | 'not-applicable';
40
+ readonly inbox: 'owner-durable' | 'not-applicable';
41
+ };
42
+ readonly resources: readonly ResourceConfig[];
43
+ }
44
+ export interface ResourceSnapshot {
45
+ readonly config: ResourceConfig;
46
+ readonly nativeConfigDigest?: string;
47
+ readonly messages?: number;
48
+ readonly bytes?: number;
49
+ readonly consumerPending?: number;
50
+ }
51
+ export declare class ProvisionerError extends Error {
52
+ readonly code: string;
53
+ constructor(code: string, message: string);
54
+ }
55
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { CapabilityManifest, ResourceConfig } from './types.js';
2
+ export declare function parseResource(value: unknown): ResourceConfig;
3
+ export declare function parseManifest(value: unknown): CapabilityManifest;
@@ -0,0 +1,13 @@
1
+ import type { JetStreamManager } from '@nats-io/jetstream';
2
+ import type { BrokerReadPort } from '../application/ports.js';
3
+ import type { ResourceSnapshot } from '../manifest/types.js';
4
+ import type { NatsInspectionOptions, NatsInspectionStatus } from './types.js';
5
+ export declare class NatsReadOnlyInspector implements BrokerReadPort {
6
+ private readonly manager;
7
+ private readonly options;
8
+ constructor(manager: JetStreamManager, options: NatsInspectionOptions);
9
+ read(signal: AbortSignal): Promise<readonly ResourceSnapshot[]>;
10
+ status(signal: AbortSignal): Promise<NatsInspectionStatus>;
11
+ private account;
12
+ private resource;
13
+ }
@@ -0,0 +1,38 @@
1
+ export interface InspectResource {
2
+ readonly kind: 'stream' | 'consumer';
3
+ readonly name: string;
4
+ readonly stream?: string;
5
+ }
6
+ export interface NatsInspectionOptions {
7
+ readonly deploymentDomainRef: string;
8
+ readonly expectedNatsDomain: string | null;
9
+ readonly resources: readonly InspectResource[];
10
+ readonly timeoutMs: number;
11
+ }
12
+ export interface NatsResourceStatus {
13
+ readonly kind: 'stream' | 'consumer';
14
+ readonly name: string;
15
+ readonly stream: string;
16
+ readonly owner: string | null;
17
+ readonly deploymentDomainRef: string | null;
18
+ readonly messages: number;
19
+ readonly bytes: number;
20
+ readonly firstSequence: number;
21
+ readonly lastSequence: number;
22
+ readonly pending: number;
23
+ readonly ackPending: number;
24
+ readonly ackFloor: number | null;
25
+ readonly nativeConfigDigest: string;
26
+ }
27
+ export interface NatsInspectionStatus {
28
+ readonly mode: 'LIVE_READ_ONLY';
29
+ readonly natsDomain: string | null;
30
+ readonly accountIdentity: 'AUTHENTICATED_CONNECTION_NOT_EXPOSED_BY_JS_API';
31
+ readonly storageBytes: number;
32
+ readonly memoryBytes: number;
33
+ readonly maxStorageBytes: number;
34
+ readonly maxMemoryBytes: number;
35
+ readonly streamCount: number;
36
+ readonly consumerCount: number;
37
+ readonly resources: readonly NatsResourceStatus[];
38
+ }
@@ -0,0 +1,11 @@
1
+ import type { DeploymentAuthorization } from '../application/ports.js';
2
+ import type { NatsInspectionOptions } from './types.js';
3
+ export interface NatsExclusiveAccess {
4
+ readonly mode: 'exclusive-session';
5
+ readonly policyRef: string;
6
+ assertHeld(signal: AbortSignal): Promise<void>;
7
+ }
8
+ export interface NatsWriterOptions extends NatsInspectionOptions {
9
+ readonly authorization: DeploymentAuthorization;
10
+ readonly exclusiveAccess: NatsExclusiveAccess;
11
+ }
@@ -0,0 +1,18 @@
1
+ import type { JetStreamManager } from '@nats-io/jetstream';
2
+ import type { BrokerManagementPort, ManagementLease } from '../application/ports.js';
3
+ import type { ResourceConfig, ResourceSnapshot } from '../manifest/types.js';
4
+ import type { NatsWriterOptions } from './writer-types.js';
5
+ import type { NatsInspectionStatus } from './types.js';
6
+ export declare class NatsExclusiveWriter implements BrokerManagementPort {
7
+ #private;
8
+ private readonly manager;
9
+ constructor(manager: JetStreamManager, options: NatsWriterOptions);
10
+ read(signal: AbortSignal): Promise<readonly ResourceSnapshot[]>;
11
+ status(signal: AbortSignal): Promise<NatsInspectionStatus>;
12
+ acquireLease(domain: string, signal: AbortSignal): Promise<ManagementLease>;
13
+ create(config: ResourceConfig, lease: ManagementLease, signal: AbortSignal): Promise<void>;
14
+ update(config: ResourceConfig, expectedDigest: string, lease: ManagementLease, signal: AbortSignal): Promise<void>;
15
+ private mutate;
16
+ private validateObservation;
17
+ private authorize;
18
+ }
@@ -0,0 +1 @@
1
+ export declare function digest(value: unknown): string;
@@ -0,0 +1,18 @@
1
+ import type { CapabilityManifest, ResourceConfig, ResourceSnapshot } from '../manifest/types.js';
2
+ export type OperationKind = 'NOOP' | 'CREATE' | 'SAFE_UPDATE' | 'BLOCKED';
3
+ export interface PlanOperation {
4
+ readonly action: OperationKind;
5
+ readonly resource: ResourceConfig;
6
+ readonly beforeDigest: string | null;
7
+ readonly reason: string;
8
+ }
9
+ export interface ProvisioningPlan {
10
+ readonly manifestDigest: string;
11
+ readonly observedConfigDigest: string;
12
+ readonly planRef: string;
13
+ readonly operations: readonly PlanOperation[];
14
+ }
15
+ export declare function resourceKey(r: ResourceConfig): string;
16
+ export declare function configurationDigest(observed: readonly ResourceSnapshot[]): string;
17
+ export declare function planProvisioning(input: unknown, observed: readonly ResourceSnapshot[]): ProvisioningPlan;
18
+ export declare function desiredResources(manifest: CapabilityManifest): readonly ResourceConfig[];
@@ -0,0 +1,10 @@
1
+ import { type VerifiedNatsOptions } from './verified-connection.js';
2
+ import { NatsPublisher, type NatsPublisherOptions } from '../publish/nats-publisher.js';
3
+ import { NatsConsumer, type NatsConsumerOptions } from '../consume/nats-consumer.js';
4
+ export declare function openNatsDeliverySession(options: VerifiedNatsOptions): Promise<{
5
+ isClosed: () => boolean;
6
+ close: () => Promise<void>;
7
+ publisher: (config: NatsPublisherOptions) => NatsPublisher;
8
+ consumer(stream: string, config: NatsConsumerOptions, signal: AbortSignal): Promise<NatsConsumer>;
9
+ }>;
10
+ export type NatsDeliverySession = Awaited<ReturnType<typeof openNatsDeliverySession>>;
@@ -0,0 +1,12 @@
1
+ import type { Authenticator, NatsConnection } from '@nats-io/nats-core';
2
+ export interface VerifiedNatsOptions {
3
+ serverUrl: string;
4
+ expectedDnsName: string;
5
+ timeoutMs: number;
6
+ signal: AbortSignal;
7
+ caPem?: string;
8
+ authenticator?: Authenticator;
9
+ }
10
+ export declare function validateVerifiedNatsOptions(options: VerifiedNatsOptions): void;
11
+ export declare function connectVerifiedNats(input: VerifiedNatsOptions): Promise<NatsConnection>;
12
+ export declare function requireTransportPatch(capability: unknown): void;
@@ -0,0 +1,34 @@
1
+ import type { JsMsg } from '@nats-io/jetstream';
2
+ export interface NatsDelivery {
3
+ event: unknown;
4
+ deliveryRef: string;
5
+ subject: string;
6
+ stream: string;
7
+ streamSequence: number;
8
+ redelivered: boolean;
9
+ contentType: string;
10
+ originProof?: string;
11
+ }
12
+ export interface DurableAcceptance {
13
+ kind: 'RECEIVED' | 'DUPLICATE' | 'REJECTED' | 'QUARANTINED';
14
+ }
15
+ export type AcceptDelivery = (delivery: NatsDelivery, signal: AbortSignal) => Promise<DurableAcceptance>;
16
+ export interface InvalidWireMetadata {
17
+ deliveryRef: string;
18
+ stream: string;
19
+ streamSequence: number;
20
+ reason: 'INVALID_WIRE_MESSAGE';
21
+ }
22
+ export type QuarantineInvalidWire = (metadata: InvalidWireMetadata, signal: AbortSignal) => Promise<{
23
+ kind: 'QUARANTINED';
24
+ }>;
25
+ export interface HandlerOptions {
26
+ deploymentDomainRef: string;
27
+ timeoutMs: number;
28
+ maxPayloadBytes: number;
29
+ retryDelayMs: number;
30
+ quarantineInvalidWire?: QuarantineInvalidWire;
31
+ }
32
+ export declare function decodeDelivery(message: JsMsg, maximum: number, deploymentDomainRef: string): NatsDelivery;
33
+ export declare function persistThenAck(message: JsMsg, accept: AcceptDelivery, options: HandlerOptions, signal: AbortSignal): Promise<void>;
34
+ export declare function validateDeploymentDomainRef(value: string): void;
@@ -0,0 +1,18 @@
1
+ import { type Consumer } from '@nats-io/jetstream';
2
+ import { type AcceptDelivery, type HandlerOptions } from './delivery-handler.js';
3
+ export interface NatsConsumerOptions extends HandlerOptions {
4
+ batchSize: number;
5
+ fetchExpiresMs: number;
6
+ consumerName: string;
7
+ expectedStream?: string;
8
+ expectedSubject?: string;
9
+ }
10
+ export declare class NatsConsumer {
11
+ private readonly consumer;
12
+ private readonly options;
13
+ private consuming;
14
+ constructor(consumer: Consumer, options: NatsConsumerOptions);
15
+ verify(signal: AbortSignal): Promise<void>;
16
+ consumeBatch(accept: AcceptDelivery, signal: AbortSignal): Promise<number>;
17
+ private processBatch;
18
+ }
@@ -0,0 +1,12 @@
1
+ export { NatsPublisher, natsMessageId, validatePublishSubject } from './publish/nats-publisher.js';
2
+ export type { NatsPublisherOptions } from './publish/nats-publisher.js';
3
+ export { NatsConsumer } from './consume/nats-consumer.js';
4
+ export type { NatsConsumerOptions } from './consume/nats-consumer.js';
5
+ export { decodeDelivery, persistThenAck } from './consume/delivery-handler.js';
6
+ export type { NatsDelivery, DurableAcceptance, AcceptDelivery, HandlerOptions, InvalidWireMetadata, QuarantineInvalidWire, } from './consume/delivery-handler.js';
7
+ export { NatsTransportError } from './transport-error.js';
8
+ export type { NatsTransportCode } from './transport-error.js';
9
+ export { connectVerifiedNats, validateVerifiedNatsOptions, } from './connection/verified-connection.js';
10
+ export type { VerifiedNatsOptions } from './connection/verified-connection.js';
11
+ export { credsAuthenticator, nkeyAuthenticator } from '@nats-io/nats-core';
12
+ export { openNatsDeliverySession, type NatsDeliverySession, } from './connection/delivery-session.js';
@@ -0,0 +1,3 @@
1
+ /*! Includes NATS transport modified for bounded initial socket cleanup; see NOTICE and licenses/NATS-LICENSE. */
2
+ import { createRequire as eventSdkCreateRequire } from "node:module"; const require = eventSdkCreateRequire(import.meta.url);
3
+ var J=Object.create;var A=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var Q=Object.getOwnPropertyNames;var K=Object.getPrototypeOf,Y=Object.prototype.hasOwnProperty;var a=(r,e)=>A(r,"name",{value:e,configurable:!0}),N=(r=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(r,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):r)(function(r){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+r+'" is not supported')});var E=(r,e)=>()=>{try{return e||r((e={exports:{}}).exports,e),e.exports}catch(t){throw e=0,t}};var X=(r,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of Q(e))!Y.call(r,o)&&o!==t&&A(r,o,{get:()=>e[o],enumerable:!(i=$(e,o))||i.enumerable});return r};var Z=(r,e,t)=>(t=r!=null?J(K(r)):{},X(e||!r||!r.__esModule?A(t,"default",{value:r,enumerable:!0}):t,r));var b=E(y=>{"use strict";var de=y&&y.__createBinding||(Object.create?(function(r,e,t,i){i===void 0&&(i=t);var o=Object.getOwnPropertyDescriptor(e,t);(!o||("get"in o?!e.__esModule:o.writable||o.configurable))&&(o={enumerable:!0,get:a(function(){return e[t]},"get")}),Object.defineProperty(r,i,o)}):(function(r,e,t,i){i===void 0&&(i=t),r[i]=e[t]})),ue=y&&y.__exportStar||function(r,e){for(var t in r)t!=="default"&&!Object.prototype.hasOwnProperty.call(e,t)&&de(e,r,t)};Object.defineProperty(y,"__esModule",{value:!0});ue(N("@nats-io/nats-core/internal"),y)});var j=E(I=>{"use strict";Object.defineProperty(I,"__esModule",{value:!0});I.version=void 0;I.version="3.4.0"});var U=E(p=>{"use strict";var he=p&&p.__importDefault||function(r){return r&&r.__esModule?r:{default:r}};Object.defineProperty(p,"__esModule",{value:!0});p.NodeTransport=p.VERSION=void 0;p.nodeResolveHost=Ee;var l=b(),fe=N("node:net"),O=N("node:tls"),pe=N("node:path"),L=N("node:fs"),me=he(N("node:dns")),ye=j();p.VERSION=ye.version;var Ne="nats.js",S=class{static{a(this,"NodeTransport")}socket;version;lang;yields=[];signal=(0,l.deferred)();closedNotification=(0,l.deferred)();options;connected=!1;tlsName="";done=!1;closeError;constructor(){this.lang=Ne,this.version=p.VERSION}async connect(e,t){if(this.done)throw this.closeError||new l.errors.ConnectionError("connection closed during initialization");this.tlsName=e.tlsName,this.options=t;let{tls:i}=this.options,{handshakeFirst:o}=i||{};try{if(o===!0?this.socket=await this.tlsFirst(e):this.socket=await this.dial(e),this.done)throw this.socket?.destroy(),this.closeError||new l.errors.ConnectionError("connection closed during initialization");let s=await this.peekInfo();(0,l.checkOptions)(s,t);let{tls_required:n,tls_available:d}=s,u=d===!0&&t.tls!==null;if(!o&&(n||u)&&(this.socket=await this.startTLS()),this.done)throw this.socket?.destroy(),this.closeError||new l.errors.ConnectionError("connection closed during initialization");if(n&&this.socket.encrypted!==!0)throw l.errors.InvalidArgumentError.format("tls","is not available on this server");return this.connected=!0,this.setupHandlers(),this.signal.resolve(),Promise.resolve()}catch(s){let n=s;n||(n=new l.errors.ConnectionError("error connecting - node provided an undefined error"));let{code:d}=n,u=d==="ECONNREFUSED"?new l.errors.ConnectionError("connection refused",{cause:n}):n;throw this.socket?.destroy(),u}}dial(e){let t=(0,l.deferred)(),i,o=(0,fe.createConnection)(e.port,e.hostname,()=>{t.resolve(o),o.removeAllListeners()});return this.socket=o,o.on("error",s=>{i=s}),o.on("close",()=>{o.removeAllListeners(),t.reject(i)}),o.setNoDelay(!0),t}get isClosed(){return this.done}close(e){return this._closed(e,!1)}peekInfo(){let e=(0,l.deferred)(),t;return this.socket.on("data",i=>{this.yields.push(i);let o=l.DataBuffer.concat(...this.yields),s=(0,l.extractProtocolMessage)(o);if(s!=="")try{let n=l.INFO.exec(s);if(!n)throw new Error("unexpected response from server");let d=JSON.parse(n[1]);e.resolve(d)}catch(n){e.reject(n)}finally{this.socket.removeAllListeners()}}),this.socket.on("error",i=>{t=i}),this.socket.on("close",()=>{this.socket.removeAllListeners(),e.reject(t)}),e}loadFile(e){if(!e)return Promise.resolve();let t=(0,l.deferred)();try{e=(0,pe.resolve)(e),(0,L.existsSync)(e)||t.reject(new Error(`${e} doesn't exist`)),(0,L.readFile)(e,(i,o)=>{if(i)return t.reject(i);t.resolve(o)})}catch(i){t.reject(i)}return t}async loadClientCerts(){let e={},{certFile:t,cert:i,caFile:o,ca:s,keyFile:n,key:d}=this.options.tls;try{if(t){let u=await this.loadFile(t);u&&(e.cert=u)}else i&&(e.cert=i);if(n){let u=await this.loadFile(n);u&&(e.key=u)}else d&&(e.key=d);if(o){let u=await this.loadFile(o);u&&(e.ca=[u])}else s&&(e.ca=s);return Promise.resolve(e)}catch(u){return Promise.reject(u)}}async tlsFirst(e){let t,i={servername:this.tlsName,rejectUnauthorized:!0};if(this.socket&&(i.socket=this.socket),typeof this.options.tls=="object")try{let s=await this.loadClientCerts()||{};i=(0,l.extend)(i,this.options.tls,s)}catch(s){return Promise.reject(new l.errors.ConnectionError(s.message,{cause:s}))}let o=(0,l.deferred)();try{if(this.done)throw this.closeError||new l.errors.ConnectionError("connection closed during initialization");let s=(0,O.connect)(e.port,e.hostname,i,()=>{s.removeAllListeners(),o.resolve(s)});this.socket=s,s.on("error",n=>{t=n}),s.on("secureConnect",()=>{if(i.rejectUnauthorized!==!1&&!s.authorized)throw s.authorizationError}),s.on("close",()=>{o.reject(t),s.removeAllListeners()}),s.setNoDelay(!0)}catch(s){o.reject(new l.errors.ConnectionError(s.message,{cause:s}))}return o}async startTLS(){let e,t={socket:this.socket,servername:this.tlsName,rejectUnauthorized:!0};if(typeof this.options.tls=="object")try{let o=await this.loadClientCerts()||{};t=(0,l.extend)(t,this.options.tls,o)}catch(o){return Promise.reject(new l.errors.ConnectionError(o.message,{cause:o}))}let i=(0,l.deferred)();try{if(this.done)throw this.closeError||new l.errors.ConnectionError("connection closed during initialization");let o=(0,O.connect)(t,()=>{o.removeAllListeners(),i.resolve(o)});this.socket=o,o.on("error",s=>{e=s}),o.on("secureConnect",()=>{if(t.rejectUnauthorized!==!1&&!o.authorized)throw o.authorizationError}),o.on("close",()=>{i.reject(e),o.removeAllListeners()})}catch(o){i.reject(new l.errors.ConnectionError(o.message,{cause:o}))}return i}setupHandlers(){let e;this.socket.on("data",t=>(this.yields.push(t),this.signal.resolve())),this.socket.on("error",t=>{e=t}),this.socket.on("end",()=>{this.socket?.destroyed||this.socket?.write(new Uint8Array(0),()=>{this.socket?.end()})}),this.socket.on("close",()=>{this._closed(e,!1)})}[Symbol.asyncIterator](){return this.iterate()}async*iterate(){for(;;){this.yields.length===0&&await this.signal;let e=this.yields;this.yields=[];for(let t=0;t<e.length;t++)this.options.debug&&console.info(`> ${(0,l.render)(e[t])}`),yield e[t];if(this.done)break;this.yields.length===0&&(e.length=0,this.yields=e,this.signal=(0,l.deferred)())}}discard(){if(this.done=!0,this.socket)try{this.socket.removeAllListeners(),this.socket.destroy()}catch{}}disconnect(){this._closed(void 0,!0).then().catch()}isEncrypted(){return this.socket instanceof O.TLSSocket}_send(e){if(this.isClosed||this.socket===void 0)return Promise.resolve();this.options.debug&&console.info(`< ${(0,l.render)(e)}`);let t=(0,l.deferred)();try{this.socket.write(e,i=>i?(this.options.debug&&console.error(`!!! ${(0,l.render)(e)}: ${i}`),t.reject(i)):t.resolve())}catch(i){this.options.debug&&console.error(`!!! ${(0,l.render)(e)}: ${i}`),t.reject(i)}return t}send(e){this._send(e).catch(i=>{})}async _closed(e,t=!0){if(!this.connected){this.done=!0,this.closeError=e,this.socket?.destroy(),this.closedNotification.resolve(e);return}if(this.done){this.socket?.destroy();return}if(this.closeError=e,!e&&this.socket&&t)try{await this._send(new TextEncoder().encode(""))}catch(i){this.options.debug&&console.log("transport close terminated with an error",i)}try{this.socket&&(this.socket.removeAllListeners(),this.socket?.destroy(),this.socket=void 0)}catch(i){console.log(i)}this.done=!0,this.closedNotification.resolve(this.closeError)}closed(){return this.closedNotification}};p.NodeTransport=S;function Ee(r){let e=(0,l.deferred)();return me.default.lookup(r,{all:!0},(t,i)=>{if(t){e.reject(t);return}let o=[];for(let s of i)o.push(s.address);o.length===0&&o.push(r),e.resolve(o)}),e}a(Ee,"nodeResolveHost")});var G=E(P=>{"use strict";Object.defineProperty(P,"__esModule",{value:!0});P.connect=ve;var F=U(),V=b(),q=b();function ve(r={}){return(0,q.hasWsProtocol)(r)?Promise.reject(q.errors.InvalidArgumentError.format("servers","node client doesn't support websockets, use the 'wsconnect' function instead")):((0,V.setTransportFactory)({factory:a(()=>new F.NodeTransport,"factory"),dnsResolveFn:F.nodeResolveHost}),V.NatsConnectionImpl.connect(r))}a(ve,"connect")});var B=E(m=>{"use strict";var _e=m&&m.__createBinding||(Object.create?(function(r,e,t,i){i===void 0&&(i=t);var o=Object.getOwnPropertyDescriptor(e,t);(!o||("get"in o?!e.__esModule:o.writable||o.configurable))&&(o={enumerable:!0,get:a(function(){return e[t]},"get")}),Object.defineProperty(r,i,o)}):(function(r,e,t,i){i===void 0&&(i=t),r[i]=e[t]})),be=m&&m.__exportStar||function(r,e){for(var t in r)t!=="default"&&!Object.prototype.hasOwnProperty.call(e,t)&&_e(e,r,t)};Object.defineProperty(m,"__esModule",{value:!0});m.eventSdkInitialClosePatch="eventsdk-initial-close-v1";m.connect=void 0;var we=G();Object.defineProperty(m,"connect",{enumerable:!0,get:a(function(){return we.connect},"get")});be(b(),m)});var z=E((at,W)=>{"use strict";W.exports=B()});import{createHash as R}from"node:crypto";import{headers as ee}from"@nats-io/nats-core";import{assertDeliveryIdentity as te}from"@eventsdk/sdk/core";import{canonicalJson as re,parseCloudEvent as oe,EVENTSDK_ORIGIN_PROOF_HEADER as ie,isEventOriginProofWireForm as se}from"@eventsdk/sdk/contracts";var c=class extends Error{constructor(t){super(t);this.code=t;this.name="NatsTransportError"}code;static{a(this,"NatsTransportError")}};function h(r,e=Number.MAX_SAFE_INTEGER){if(!Number.isSafeInteger(r)||r<1||r>e)throw new c("INVALID_TRANSPORT_CONFIG")}a(h,"positiveInteger");async function f(r,e,t){e.throwIfAborted(),h(t);let i,o,s=new Promise((n,d)=>{o=a(()=>d(new c("TRANSPORT_ABORTED")),"abort"),e.addEventListener("abort",o,{once:!0}),i=setTimeout(()=>d(new c("TRANSPORT_TIMEOUT")),t)});try{return await Promise.race([r,s])}finally{i!==void 0&&clearTimeout(i),o!==void 0&&e.removeEventListener("abort",o)}}a(f,"bounded");function D(r){let e=r.deliveryid===void 0?`${r.source}\0${r.id}`:re(["eventsdk-delivery-message/1",r.source,r.id,r.deliveryid]);return R("sha256").update(e).digest("hex")}a(D,"natsMessageId");function k(r){if(!/^[^\s.*>]+(?:\.[^\s.*>]+)*$/u.test(r)||[...r].some(e=>e.charCodeAt(0)<32))throw new c("INVALID_TRANSPORT_CONFIG")}a(k,"validatePublishSubject");var v=class{constructor(e,t){this.client=e;if(this.options={...t},t.replicationMode==="mirror"&&t.expectedStream!==void 0)throw new c("UNSUPPORTED_TOPOLOGY_COMBINATION");k(t.subject),h(t.timeoutMs),h(t.maxPayloadBytes)}client;static{a(this,"NatsPublisher")}options;async publish(e,t){e=structuredClone(e),t={...t},t.signal.throwIfAborted(),oe(e),await te(e,{sha256:a(async n=>R("sha256").update(n).digest("hex"),"sha256")}),t.signal.throwIfAborted();let i=D(e);if(t.messageId!==i)throw new c("INVALID_TRANSPORT_CONFIG");let o=new TextEncoder().encode(JSON.stringify(e));if(o.byteLength>this.options.maxPayloadBytes)throw new c("INVALID_WIRE_MESSAGE");let s=ee();if(s.set("Content-Type","application/cloudevents+json"),this.options.originProof){let n=AbortSignal.any([t.signal,AbortSignal.timeout(this.options.timeoutMs)]),d=await f(this.options.originProof(structuredClone(e),n),n,this.options.timeoutMs);if(!se(d))throw new c("INVALID_WIRE_MESSAGE");s.set(ie,d),t.signal.throwIfAborted()}try{let n=await f(this.client.publish(this.options.subject,o,{msgID:i,headers:s,timeout:this.options.timeoutMs,retries:0,...this.options.expectedStream?{expect:{streamName:this.options.expectedStream}}:{}}),t.signal,this.options.timeoutMs);if(this.options.expectedStream!==void 0&&n.stream!==this.options.expectedStream)throw new c("PUBLISH_UNKNOWN");return{stream:n.stream,sequence:n.seq,duplicate:n.duplicate}}catch{throw new c("PUBLISH_UNKNOWN")}}};import{AckPolicy as le}from"@nats-io/jetstream";import{decodeCloudEvent as ne,EVENTSDK_ORIGIN_PROOF_HEADER as x,isEventOriginProofWireForm as ae}from"@eventsdk/sdk/contracts";function M(r,e,t){w(t);let i=r.headers?.get("Content-Type")??"",o=r.headers?.get(x);if((r.headers?.values(x).length??0)>1)throw new c("INVALID_WIRE_MESSAGE");if(o&&!ae(o))throw new c("INVALID_WIRE_MESSAGE");if(i!=="application/cloudevents+json"||r.data.byteLength>e)throw new c("INVALID_WIRE_MESSAGE");let s;try{s=ne(r.data)}catch{throw new c("INVALID_WIRE_MESSAGE")}return{event:s,contentType:i,subject:r.subject,stream:r.info.stream,streamSequence:r.info.streamSequence,deliveryRef:JSON.stringify([t,r.info.domain,r.info.stream,r.info.streamSequence]),redelivered:r.redelivered,...o?{originProof:o}:{}}}a(M,"decodeDelivery");async function g(r,e,t,i){i.throwIfAborted(),w(t.deploymentDomainRef);let o=AbortSignal.timeout(t.timeoutMs),s=AbortSignal.any([i,o]);try{let n=await f(ce(r,e,t,s),s,t.timeoutMs);if(!["RECEIVED","DUPLICATE","REJECTED","QUARANTINED"].includes(n.kind))throw new c("DURABLE_ACCEPTANCE_REQUIRED");s.throwIfAborted(),r.ack()}catch(n){throw r.nak(t.retryDelayMs),n}}a(g,"persistThenAck");async function ce(r,e,t,i){let o;try{o=M(r,t.maxPayloadBytes,t.deploymentDomainRef)}catch(s){if(!t.quarantineInvalidWire)throw s;return t.quarantineInvalidWire({deliveryRef:JSON.stringify([t.deploymentDomainRef,r.info.domain,r.info.stream,r.info.streamSequence]),stream:r.info.stream,streamSequence:r.info.streamSequence,reason:"INVALID_WIRE_MESSAGE"},i)}return e(o,i)}a(ce,"resolveAcceptance");function w(r){if(typeof r!="string"||r.trim()!==r||r.length<1||r.length>256)throw new c("INVALID_TRANSPORT_CONFIG")}a(w,"validateDeploymentDomainRef");var _=class{constructor(e,t){this.consumer=e;if(this.options={...t},w(t.deploymentDomainRef),h(t.batchSize,1e3),h(t.fetchExpiresMs),h(t.timeoutMs),h(t.maxPayloadBytes),h(t.retryDelayMs),!t.consumerName)throw new c("INVALID_CONSUMER_CONFIG")}consumer;static{a(this,"NatsConsumer")}options;consuming=!1;async verify(e){e.throwIfAborted();let t=await f(this.consumer.info(!1),e,this.options.timeoutMs);if(t.config.durable_name!==this.options.consumerName||t.config.ack_policy!==le.Explicit||t.config.deliver_subject!==void 0||this.options.expectedStream!==void 0&&t.stream_name!==this.options.expectedStream||this.options.expectedSubject!==void 0&&(t.config.filter_subject!==this.options.expectedSubject||(t.config.filter_subjects?.length??0)>0)||(t.config.ack_wait??0)<=this.options.timeoutMs*this.options.batchSize*1e6||(t.config.max_ack_pending??0)<this.options.batchSize)throw new c("INVALID_CONSUMER_CONFIG")}async consumeBatch(e,t){if(this.consuming)throw new c("INVALID_CONSUMER_CONFIG");this.consuming=!0;try{await this.verify(t),t.throwIfAborted();let i=this.consumer.fetch({max_messages:this.options.batchSize,expires:this.options.fetchExpiresMs}),o;try{o=await f(i,t,this.options.timeoutMs)}catch(s){throw i.then(n=>n.close(),()=>{}),s}return await this.processBatch(o,e,t)}finally{this.consuming=!1}}async processBatch(e,t,i){let o=a(()=>{e.close()},"stop");i.addEventListener("abort",o,{once:!0});let s=0;try{i.throwIfAborted();for await(let n of e)await g(n,t,this.options,i),s+=1;return i.throwIfAborted(),s}finally{i.removeEventListener("abort",o),await e.close()}}};var T=Z(z(),1);import{isIP as Ie}from"node:net";function H(r){h(r.timeoutMs,6e4);let e;try{e=new URL(r.serverUrl)}catch{throw new c("TLS_CONFIGURATION_REJECTED")}let t=r.expectedDnsName;if(typeof t!="string")throw new c("TLS_CONFIGURATION_REJECTED");let i=t.split(".");if(e.protocol!=="tls:"||e.username||e.password||e.search||e.hash||e.pathname!==""&&e.pathname!=="/"||Ie(e.hostname.replace(/^\[|\]$/gu,""))||t!==t.toLowerCase()||t!==e.hostname||t.length>253||i.some(o=>!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/u.test(o))||r.caPem!==void 0&&!r.caPem.includes("-----BEGIN CERTIFICATE-----"))throw new c("TLS_CONFIGURATION_REJECTED")}a(H,"validateVerifiedNatsOptions");async function C(r){let e={...r};H(e),e.signal.throwIfAborted(),Ae(T.eventSdkInitialClosePatch);let i=T.connect({servers:e.serverUrl,tls:{rejectUnauthorized:!0,...e.caPem?{ca:e.caPem}:{}},timeout:e.timeoutMs,ignoreClusterUpdates:!0,reconnect:!1,resolve:!1,...e.authenticator?{authenticator:e.authenticator}:{}});try{let o=await f(i,e.signal,e.timeoutMs);if(e.signal.aborted)throw await o.close(),new c("TRANSPORT_ABORTED");return o}catch(o){throw i.then(s=>s.close(),()=>{}),o instanceof c?o:new c("TLS_CONNECTION_FAILED")}}a(C,"connectVerifiedNats");function Ae(r){if(r!=="eventsdk-initial-close-v1")throw new c("TRANSPORT_PATCH_REQUIRED")}a(Ae,"requireTransportPatch");import{credsAuthenticator as St,nkeyAuthenticator as Pt}from"@nats-io/nats-core";import{jetstream as ge}from"@nats-io/jetstream";async function Oe(r){let e=await C(r),t=ge(e);return{isClosed:a(()=>e.isClosed(),"isClosed"),close:a(()=>e.close(),"close"),publisher:a(i=>new v(t,i),"publisher"),async consumer(i,o,s){let n=new _(await f(t.consumers.get(i,o.consumerName),s,o.timeoutMs),o);return await n.verify(s),n}}}a(Oe,"openNatsDeliverySession");export{_ as NatsConsumer,v as NatsPublisher,c as NatsTransportError,C as connectVerifiedNats,St as credsAuthenticator,M as decodeDelivery,D as natsMessageId,Pt as nkeyAuthenticator,Oe as openNatsDeliverySession,g as persistThenAck,k as validatePublishSubject,H as validateVerifiedNatsOptions};
@@ -0,0 +1,24 @@
1
+ import type { JetStreamClient } from '@nats-io/jetstream';
2
+ import { type CloudEvent } from '@eventsdk/sdk/contracts';
3
+ import type { PublishPort, PublishAck } from '@eventsdk/sdk/core';
4
+ export interface NatsPublisherOptions {
5
+ subject: string;
6
+ timeoutMs: number;
7
+ maxPayloadBytes: number;
8
+ expectedStream?: string;
9
+ replicationMode?: 'direct' | 'source' | 'mirror';
10
+ originProof?: (event: CloudEvent, signal: AbortSignal) => Promise<string>;
11
+ }
12
+ export declare function natsMessageId(event: Pick<CloudEvent, 'source' | 'id'> & {
13
+ readonly deliveryid?: string;
14
+ }): string;
15
+ export declare function validatePublishSubject(subject: string): void;
16
+ export declare class NatsPublisher implements PublishPort {
17
+ private readonly client;
18
+ private readonly options;
19
+ constructor(client: Pick<JetStreamClient, 'publish'>, options: NatsPublisherOptions);
20
+ publish(event: CloudEvent, context: {
21
+ messageId: string;
22
+ signal: AbortSignal;
23
+ }): Promise<PublishAck>;
24
+ }
@@ -0,0 +1,5 @@
1
+ export type NatsTransportCode = 'INVALID_TRANSPORT_CONFIG' | 'TRANSPORT_PATCH_REQUIRED' | 'TLS_CONFIGURATION_REJECTED' | 'TLS_CONNECTION_FAILED' | 'UNSUPPORTED_TOPOLOGY_COMBINATION' | 'TRANSPORT_ABORTED' | 'TRANSPORT_TIMEOUT' | 'PUBLISH_UNKNOWN' | 'INVALID_WIRE_MESSAGE' | 'DURABLE_ACCEPTANCE_REQUIRED' | 'INVALID_CONSUMER_CONFIG';
2
+ export declare class NatsTransportError extends Error {
3
+ readonly code: NatsTransportCode;
4
+ constructor(code: NatsTransportCode);
5
+ }