@axonflow/sdk 9.1.0 → 9.2.0

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 (46) hide show
  1. package/README.md +117 -22
  2. package/dist/cjs/authzen.d.ts +328 -0
  3. package/dist/cjs/authzen.d.ts.map +1 -0
  4. package/dist/cjs/authzen.js +875 -0
  5. package/dist/cjs/authzen.js.map +1 -0
  6. package/dist/cjs/client.d.ts +67 -0
  7. package/dist/cjs/client.d.ts.map +1 -1
  8. package/dist/cjs/client.js +84 -3
  9. package/dist/cjs/client.js.map +1 -1
  10. package/dist/cjs/index.d.ts +3 -0
  11. package/dist/cjs/index.d.ts.map +1 -1
  12. package/dist/cjs/index.js +45 -1
  13. package/dist/cjs/index.js.map +1 -1
  14. package/dist/cjs/telemetry.d.ts +68 -13
  15. package/dist/cjs/telemetry.d.ts.map +1 -1
  16. package/dist/cjs/telemetry.js +92 -9
  17. package/dist/cjs/telemetry.js.map +1 -1
  18. package/dist/cjs/types/authzen.gen.d.ts +405 -0
  19. package/dist/cjs/types/authzen.gen.d.ts.map +1 -0
  20. package/dist/cjs/types/authzen.gen.js +798 -0
  21. package/dist/cjs/types/authzen.gen.js.map +1 -0
  22. package/dist/cjs/version.d.ts +1 -1
  23. package/dist/cjs/version.js +1 -1
  24. package/dist/esm/authzen.d.ts +328 -0
  25. package/dist/esm/authzen.d.ts.map +1 -0
  26. package/dist/esm/authzen.js +862 -0
  27. package/dist/esm/authzen.js.map +1 -0
  28. package/dist/esm/client.d.ts +67 -0
  29. package/dist/esm/client.d.ts.map +1 -1
  30. package/dist/esm/client.js +84 -3
  31. package/dist/esm/client.js.map +1 -1
  32. package/dist/esm/index.d.ts +3 -0
  33. package/dist/esm/index.d.ts.map +1 -1
  34. package/dist/esm/index.js +12 -0
  35. package/dist/esm/index.js.map +1 -1
  36. package/dist/esm/telemetry.d.ts +68 -13
  37. package/dist/esm/telemetry.d.ts.map +1 -1
  38. package/dist/esm/telemetry.js +91 -9
  39. package/dist/esm/telemetry.js.map +1 -1
  40. package/dist/esm/types/authzen.gen.d.ts +405 -0
  41. package/dist/esm/types/authzen.gen.d.ts.map +1 -0
  42. package/dist/esm/types/authzen.gen.js +773 -0
  43. package/dist/esm/types/authzen.gen.js.map +1 -0
  44. package/dist/esm/version.d.ts +1 -1
  45. package/dist/esm/version.js +1 -1
  46. package/package.json +1 -1
package/README.md CHANGED
@@ -7,13 +7,13 @@
7
7
 
8
8
  > **Upgrade strongly recommended.** AxonFlow ships substantial monthly security and quality hardening; staying on the latest major is the security-supported release line. [Latest release](https://github.com/getaxonflow/axonflow-sdk-typescript/releases/latest) · [Security advisories](https://github.com/getaxonflow/axonflow-sdk-typescript/security/advisories)
9
9
 
10
- > **Evaluating AxonFlow for a real deployment?**
10
+ > **Taking a sponsored workflow to production?**
11
11
  >
12
12
  > Choose the path that fits:
13
13
  > - **Self-serve:** free 90-day [Evaluation License](https://getaxonflow.com/evaluation-license?utm_source=readme_sdk_typescript_eval)
14
- > - **Hands-on:** [Design Partner Program](https://getaxonflow.com/design-partner?utm_source=readme_sdk_typescript) Enterprise access for the scoped engagement, founder-led architecture and rollout support, and preferential pricing after successful rollout
14
+ > - **Paid production program:** [Design Partner or Confidential Pilot](https://getaxonflow.com/design-partner?utm_source=readme_sdk_typescript) - one scoped workflow over 60 or 75 days, founder-led rollout support, upfront conversion pricing, and a fixed decision date; public track from $2,000 or confidential track from $4,000
15
15
  >
16
- > Priority support, architecture review, incident-readiness review, and roadmap input are included for selected partners. We reply within 48 hours.
16
+ > The paid program requires a dated forcing event, written controls, an executive sponsor, and a technical owner. Prices are subject to eligibility and a signed agreement.
17
17
 
18
18
  > **Questions or feedback?**
19
19
  >
@@ -29,11 +29,11 @@ A deployed AxonFlow platform (self-hosted or cloud) is required for end-to-end A
29
29
 
30
30
  ### See AxonFlow in Action
31
31
 
32
- Three short videos covering different angles of the platform:
32
+ Videos covering different angles of the platform:
33
33
 
34
- - **[Community Quickstart Demo (Code + Terminal, 2.5 min)](https://youtu.be/BSqU1z0xxCo)** governed calls, PII block, Gateway Mode with LangChain/CrewAI, and MAP from YAML
35
- - **[Runtime Control Demo (Portal + Workflow, 2.5 min)](https://youtu.be/sRTv2uF0sxY)** approvals, retry safety, execution state, and the audit viewer
36
- - **[Architecture Deep Dive (12 min)](https://youtu.be/Q2CZ1qnquhg)** how the control plane works, policy enforcement flow, and multi-agent planning
34
+ - **[Product demos: Platform + Fraud & Risk](https://getaxonflow.com/demo/?utm_source=github&utm_medium=readme&utm_campaign=product_demo&utm_content=axonflow-sdk-typescript)** - runtime enforcement, HITL approvals, audit evidence, cost visibility, and agentic payment controls
35
+ - **[Community Quickstart walkthrough (2 min)](https://youtu.be/BSqU1z0xxCo)** - governed calls, PII blocking, Gateway Mode with LangChain/CrewAI, and MAP from YAML
36
+ - **[Architecture deep dive (12 min)](https://youtu.be/Q2CZ1qnquhg)** - how the control plane works, policy enforcement flow, and multi-agent planning
37
37
 
38
38
  ## Installation
39
39
 
@@ -67,7 +67,7 @@ Concurrent executions applies to MAP and WCP executions per tenant. Pending exec
67
67
 
68
68
  > **Note:** Evidence export and policy simulation are licensed AxonFlow platform capabilities available alongside the SDK on your deployed platform — not language-specific SDK helpers. Access them via the platform API or customer portal. The SDK row is included to show what your licensed deployment unlocks at each tier.
69
69
 
70
- [Get a free Evaluation license](https://getaxonflow.com/evaluation-license?utm_source=readme_sdk_typescript_eval) · [Apply for Design Partner](https://getaxonflow.com/design-partner?utm_source=readme_sdk_typescript_eval) · [Full feature matrix](https://docs.getaxonflow.com/docs/features/community-vs-enterprise?utm_source=readme_sdk_typescript_eval)
70
+ [Get a free Evaluation license](https://getaxonflow.com/evaluation-license?utm_source=readme_sdk_typescript_eval) · [Run a paid production program](https://getaxonflow.com/design-partner?utm_source=readme_sdk_typescript_eval) · [Full feature matrix](https://docs.getaxonflow.com/docs/features/community-vs-enterprise?utm_source=readme_sdk_typescript_eval)
71
71
 
72
72
  ## Try Without Installing
73
73
 
@@ -461,6 +461,85 @@ export default async function handler(req, res) {
461
461
  }
462
462
  ```
463
463
 
464
+ ## AuthZEN-Native Authorization
465
+
466
+ `axonflow.evaluate` asks the gateway an AuthZEN question - may this subject perform this action on this resource? - over `POST /api/v1/access/evaluation`. It is the surface to write **new** integrations against: at v11 the engine behind it becomes AxonFlow's new Policy Decision Point with no wire change, so an integration written here migrates once rather than twice. Nothing is deprecated by it today; `decide` and the gateway/proxy methods are wire-stable through all of v11.
467
+
468
+ ```typescript
469
+ import { AuthZENRefusal, AxonFlow } from '@axonflow/sdk';
470
+
471
+ const decision = await axonflow.evaluate({
472
+ subject: { type: 'gateway', id: 'llm-gateway-01' },
473
+ action: { name: 'llm.completion' },
474
+ resource: { type: 'llm', id: 'llm' },
475
+ context: { args: { query: userPrompt } },
476
+ });
477
+
478
+ if (!decision.allowed) {
479
+ throw new Error(`blocked: ${decision.state} (${decision.reason})`);
480
+ }
481
+ for (const obligation of decision.mandatoryObligations) {
482
+ // an allow you cannot discharge is not an allow
483
+ }
484
+ ```
485
+
486
+ Several preconditions of **one** operation go in a bulk envelope, which returns **one** decision - a denied entry denies the operation, so a caller cannot act on the entry it liked:
487
+
488
+ ```typescript
489
+ const decision = await axonflow.evaluateAll({
490
+ subject: { type: 'gateway', id: 'llm-gateway-01' },
491
+ action: { name: 'tool.call' },
492
+ context: { args: { query: userPrompt } },
493
+ evaluations: [
494
+ { resource: { type: 'tool', id: 'jira/move_issue' } },
495
+ { resource: { type: 'tool', id: 'jira/update_project' } },
496
+ ],
497
+ });
498
+ ```
499
+
500
+ ### Known gotchas
501
+
502
+ **A refusal is not a denial.** This surface refuses anything it cannot evaluate rather than evaluating around it - send a subject property or an unrecognised context member and you get an `AuthZENRefusal` naming the exact member, not a decision computed without it. Treating every error as a deny fails closed, which is safe, but blocks traffic that would be allowed once the request is corrected.
503
+
504
+ ```typescript
505
+ try {
506
+ const decision = await axonflow.evaluate(request);
507
+ } catch (err) {
508
+ if (err instanceof AuthZENRefusal) {
509
+ err.code; // e.g. 'unevaluable_attribute' - a closed, generated set
510
+ err.pointer; // '/evaluation/subject/properties' - the member to fix
511
+ err.refusedBy; // 'client' (this SDK) or 'gateway'
512
+ err.retryable; // only a gateway dependency failure is
513
+ }
514
+ }
515
+ ```
516
+
517
+ `AuthZENProtocolError` is separate and means something else: the gateway answered `200` with a body this build cannot safely act on - no profile context, a profile it cannot read, or a decision boolean that disagrees with its operational state. It is always fail-closed, and the fix is an upgrade or an operator, not a corrected request. A `401` surfaces as the SDK's ordinary `AuthenticationError`, because the gateway answers authentication before this route runs.
518
+
519
+ **`decision.allowed`, never `decision.decision`.** The bare boolean is AuthZEN 1.0's collapsed rendering; `allowed` additionally requires the operational state to be `ALLOW`, so a `CHALLENGE` or an `ERROR` can never be read as permission.
520
+
521
+ **Three states, not two.** `undefined` cannot express the difference between "the source established there is no value" and "the source could not be reached", and collapsing them is how an attribute nobody resolved gets recorded as one that was weighed. Attributes inside the `context` and `properties` bags may be explicit:
522
+
523
+ ```typescript
524
+ import { AUTHZEN_UNKNOWN_RESOLUTION_FAILED, AuthZENAttribute } from '@axonflow/sdk';
525
+
526
+ const context = {
527
+ args: { query: userPrompt },
528
+ correlation: {
529
+ session_id: AuthZENAttribute.absent(), // a fact: omitted, request sent
530
+ trace_id: AuthZENAttribute.unknown(AUTHZEN_UNKNOWN_RESOLUTION_FAILED), // refused locally
531
+ },
532
+ };
533
+ ```
534
+
535
+ The tri-state applies to attribute **data**, not to the structural members (`subject.id`, `action.name`, …): those are the identity of the question being asked, and an identity you cannot resolve is not an attribute whose absence a policy could evaluate - there is no request to make.
536
+
537
+ **Wire member names are `snake_case`, not `camelCase`.** These interfaces ARE the wire documents - they are serialised verbatim - so `source_policy`, `decision_id` and `schema_version` keep the names the server uses. That is deliberate: a server refusal's JSON Pointer names a member, and the pointer is this surface's whole diagnosis, so it has to name a member the caller can find in their own code.
538
+
539
+ **Today's mapping is deliberately narrow.** `subject.type` must be `'gateway'` (an end-user subject needs the identity plane, which activates at v11); an `llm` or `agent` resource id must be the stage name itself, not a provider/model pair, because nothing on the serving path reads a provider or a model; a `tool` resource id is `'server/tool'`, because both halves ARE read. Everything else is refused by name.
540
+
541
+ The wire types AND their runtime validators are **generated** from the platform's canonical contract artifact (`scripts/gen-authzen-types/generate.js`); CI fails if the committed module is not what the artifact produces. A TypeScript interface is erased at runtime, so the validators are what actually refuses a body this build cannot interpret. Runnable example: [`examples/authzen/index.ts`](examples/authzen/index.ts). Migration notes: [`docs/AUTHZEN_MIGRATION_DRAFT.md`](docs/AUTHZEN_MIGRATION_DRAFT.md).
542
+
464
543
  ## Configuration Options
465
544
 
466
545
  ```typescript
@@ -716,16 +795,16 @@ connectors.forEach(conn => {
716
795
 
717
796
  ```typescript
718
797
  await axonflow.installConnector({
719
- connector_id: 'amadeus-travel',
720
- name: 'amadeus-prod',
798
+ connector_id: 'redis-cache',
799
+ name: 'redis-cache',
721
800
  tenant_id: 'your-tenant-id',
722
801
  options: {
723
- environment: 'production'
802
+ // Host/port as seen from the platform (orchestrator), not from this
803
+ // process — 'redis' on the docker-compose stack.
804
+ host: 'redis',
805
+ port: 6379
724
806
  },
725
- credentials: {
726
- api_key: process.env.AMADEUS_API_KEY,
727
- api_secret: process.env.AMADEUS_API_SECRET
728
- }
807
+ credentials: {}
729
808
  });
730
809
 
731
810
  console.log('Connector installed successfully!');
@@ -734,19 +813,18 @@ console.log('Connector installed successfully!');
734
813
  ### Query a Connector
735
814
 
736
815
  ```typescript
737
- // Query the Amadeus connector for flight information
816
+ // Query the Redis connector. Redis connector queries are command
817
+ // statements (GET / EXISTS / TTL / KEYS) with the key in params.
738
818
  const resp = await axonflow.queryConnector(
739
- 'amadeus-prod',
740
- 'Find flights from Paris to Amsterdam on Dec 15',
819
+ 'redis-cache',
820
+ 'GET',
741
821
  {
742
- origin: 'CDG',
743
- destination: 'AMS',
744
- date: '2025-12-15'
822
+ key: 'user:123:preferences'
745
823
  }
746
824
  );
747
825
 
748
826
  if (resp.success) {
749
- console.log('Flight data:', resp.data);
827
+ console.log('Redis data:', resp.data);
750
828
  } else {
751
829
  console.error('Query failed:', resp.error);
752
830
  }
@@ -1028,6 +1106,23 @@ distinguishable from production heartbeat).
1028
1106
 
1029
1107
  `AXONFLOW_TELEMETRY=off` disables the anonymous SDK heartbeat (version, OS, architecture). On **self-hosted** and **in-VPC** deployments, that heartbeat is the only data the SDK sends to AxonFlow, so setting `=off` means we receive nothing. On **Community SaaS** (`try.getaxonflow.com`) the hosted service also processes operational data — registrations, audit logs, policy enforcement records, workflow state, plan data, and request-header metadata aggregated for usage analytics — as part of running the platform; that operational data flow is governed by the [Privacy Policy](https://getaxonflow.com/privacy/), not by `AXONFLOW_TELEMETRY`.
1030
1108
 
1109
+ ### Platform licence tier (`license_tier`)
1110
+
1111
+ Each heartbeat also reports the licence tier of the AxonFlow platform the SDK is configured to talk to — for example `community`, `evaluation`, `Enterprise`, or the transient `starting` while a platform is still booting. This lets us tell an enterprise-licensed deployment apart from an unlicensed community one in aggregate adoption figures, which the heartbeat previously could not distinguish.
1112
+
1113
+ What is and is not collected:
1114
+
1115
+ - **Collected:** the coarse tier string only.
1116
+ - **Not collected:** your licence key, its expiry, its seat or node count, your organisation's name, and any other licence detail. The SDK never reads your licence key.
1117
+
1118
+ The value is read from the `tier` field of the platform's own `/health` response — the same response the heartbeat already fetches to report the platform version, and an endpoint that returns this field to any caller without authentication. **No additional network request is made, and the SDK gains no access to anything `/health` does not already return.**
1119
+
1120
+ **This is an adoption-analytics signal, not an entitlement one.** The value is whatever the platform at your configured endpoint reported about itself, relayed unchanged: the SDK derives nothing and verifies nothing, and the receiver cannot verify the relay either. Whoever operates that endpoint controls the value completely, so it must never gate entitlement, unlock a feature, or enter any authorization or billing decision. It is used only for aggregate adoption figures.
1121
+
1122
+ The field is **omitted entirely** whenever the tier could not be determined — the platform is unreachable, returns an error, returns an unparseable body, or returns no `tier` field. It is never defaulted to a guessed value, so an absent field means "not known", never "community".
1123
+
1124
+ `AXONFLOW_TELEMETRY=off` suppresses this field along with the rest of the heartbeat.
1125
+
1031
1126
  `DO_NOT_TRACK` is **not** honored as an opt-out for AxonFlow telemetry. It is commonly inherited from host tools and developer environments, which makes it an unreliable expression of user intent.
1032
1127
 
1033
1128
  See [Telemetry Documentation](https://docs.getaxonflow.com/docs/telemetry) for full details.
@@ -0,0 +1,328 @@
1
+ /**
2
+ * AuthZEN-native authorization for the AxonFlow SDK.
3
+ *
4
+ * This is the surface the ADR-065 compatibility plan commits to in all five
5
+ * SDKs. It talks to `POST /api/v1/access/evaluation`, whose wire shape is
6
+ * generated from the platform's canonical contract (see
7
+ * `src/types/authzen.gen.ts`); nothing in this file re-states a field name or
8
+ * an enum value.
9
+ *
10
+ * ## What this replaces, and when
11
+ *
12
+ * Nothing yet. The existing decision surface (`decide`, `explainDecision` and
13
+ * the gateway/proxy methods) stays wire-stable through all of v11 and is not
14
+ * deprecated here. This is the surface to write NEW integrations against,
15
+ * because at v11 the engine behind it becomes the ADR-065 Policy Decision Point
16
+ * with no wire change — an integration written against it migrates once rather
17
+ * than twice. See `docs/AUTHZEN_MIGRATION_DRAFT.md`.
18
+ *
19
+ * ## The one thing worth knowing before you call it
20
+ *
21
+ * The server refuses anything it cannot evaluate rather than evaluating around
22
+ * it. Send a subject property, an unrecognised context member, or an argument
23
+ * beside the query, and you get an `AuthZENRefusal` naming the exact member —
24
+ * not a decision computed without it. That is deliberate: a decision that
25
+ * silently ignored an attribute would tell you the attribute was weighed when
26
+ * it was not, and every audit of that decision would inherit the claim.
27
+ *
28
+ * So treat an `AuthZENRefusal` as "fix the request", and retry only when
29
+ * `refusal.retryable` is true.
30
+ */
31
+ import { AxonFlowError } from './errors';
32
+ import { AuthZENApprovalRequirement, AuthZENBulk, AuthZENCategory, AuthZENEnvelope, AuthZENError, AuthZENErrorCode, AuthZENObligation, AuthZENOperationalState, AuthZENReasonCode, AuthZENRequest, AuthZENResponse, AuthZENResponseContext } from './types/authzen.gen';
33
+ /**
34
+ * The marker every tri-state attribute carries. See {@link AuthZENAttribute}.
35
+ *
36
+ * Exported so a caller building attributes across a serialisation boundary can
37
+ * reconstruct one the SDK will still recognise.
38
+ */
39
+ export declare const AUTHZEN_ATTRIBUTE_MARKER = "__axonflow_authzen_attribute__";
40
+ /** The AuthZEN evaluation endpoint. */
41
+ export declare const AUTHZEN_PATH = "/api/v1/access/evaluation";
42
+ /**
43
+ * How a Policy Enforcement Point negotiates the AxonFlow profile.
44
+ *
45
+ * The SDK always sends it. AuthZEN 1.0's response is a bare boolean, and the
46
+ * four-valued state, the obligations and the approval challenge ride in the
47
+ * response context, which the server returns only to a caller that asked for it
48
+ * by version. This SDK understands the profile, so there is no reason to ask
49
+ * for less than it can read — and a response WITHOUT the context is therefore a
50
+ * protocol failure here rather than a decision with no obligations.
51
+ */
52
+ export declare const AUTHZEN_PROFILE_HEADER = "X-Axonflow-AuthZEN-Profile";
53
+ export declare const AUTHZEN_UNKNOWN_NOT_SUPPLIED = "attribute_not_supplied";
54
+ export declare const AUTHZEN_UNKNOWN_RESOLUTION_FAILED = "resolution_failed";
55
+ export declare const AUTHZEN_UNKNOWN_STALE = "stale";
56
+ export declare const AUTHZEN_UNKNOWN_SCHEMA_MISMATCH = "schema_mismatch";
57
+ export declare const AUTHZEN_UNKNOWN_CLOSURE_UNAVAILABLE = "closure_unavailable";
58
+ export declare const AUTHZEN_UNKNOWN_CLOSURE_TRUNCATED = "closure_truncated";
59
+ export declare const AUTHZEN_UNKNOWN_MALFORMED_VALUE = "malformed_value";
60
+ export declare const AUTHZEN_UNKNOWN_REQUIRED_ABSENT = "required_attribute_absent";
61
+ /** Who declined to produce a decision. */
62
+ export type AuthZENRefusedBy = 'client' | 'gateway';
63
+ /**
64
+ * The request was NOT evaluated, and here is the typed reason why.
65
+ *
66
+ * A refusal is not a denial. `decision: false` says the request WAS evaluated
67
+ * and the answer was no; a refusal says no decision exists. Code that treats
68
+ * every error as a deny fails closed — which is safe — but will block traffic
69
+ * that should have been allowed once the request is corrected.
70
+ *
71
+ * `refusedBy` says who made the call. `'gateway'` is a refusal document the
72
+ * server sent; `'client'` is this SDK declining to send a request it can
73
+ * already see will not be evaluated — an attribute the caller could not
74
+ * resolve, or an evaluation with no subject. The code vocabulary is shared
75
+ * because the REASONS are shared: an incomplete evaluation is an incomplete
76
+ * evaluation whoever notices it first.
77
+ */
78
+ export declare class AuthZENRefusal extends AxonFlowError {
79
+ readonly code: AuthZENErrorCode;
80
+ readonly pointer?: string;
81
+ readonly supported?: string[];
82
+ readonly requestId?: string;
83
+ readonly refusedBy: AuthZENRefusedBy;
84
+ constructor(code: AuthZENErrorCode, message: string, options: {
85
+ refusedBy: AuthZENRefusedBy;
86
+ pointer?: string;
87
+ supported?: string[];
88
+ requestId?: string;
89
+ });
90
+ /** Build a refusal from the structured document the server sent. */
91
+ static fromBody(body: AuthZENError): AuthZENRefusal;
92
+ /**
93
+ * Whether sending the same request again could give a different answer.
94
+ *
95
+ * Only a dependency failure the GATEWAY reported is. Every other code names
96
+ * something about the request itself, which will not change on a retry — so
97
+ * a client that retries on any refusal burns its budget on requests that
98
+ * cannot succeed.
99
+ *
100
+ * A client-side refusal is never retryable, whatever its code: this SDK does
101
+ * not resolve the caller's attributes, so nothing it can do will change the
102
+ * answer. Reading retryability off the code alone would have told a caller to
103
+ * retry an attribute its own resolver failed to produce.
104
+ */
105
+ get retryable(): boolean;
106
+ }
107
+ /**
108
+ * A 200 whose body this build cannot safely interpret.
109
+ *
110
+ * Deliberately NOT an `AuthZENRefusal`. A refusal carries the server's own
111
+ * typed code from a closed vocabulary the server owns; a response this build
112
+ * cannot read is not something the server said, and dressing it in a server
113
+ * code would tell the caller the gateway refused when it did not. The two also
114
+ * demand different actions: a refusal means fix the request, a protocol error
115
+ * means upgrade the SDK or go and look at the deployment.
116
+ *
117
+ * It is always fail-closed: no decision is returned, so a caller that lets it
118
+ * propagate blocks the operation.
119
+ */
120
+ export declare class AuthZENProtocolError extends AxonFlowError {
121
+ constructor(message: string);
122
+ }
123
+ /** The three states a policy-visible attribute can be in. */
124
+ export type AuthZENAttributeState = 'known' | 'absent' | 'unknown';
125
+ /**
126
+ * One policy-visible attribute in exactly one of three states.
127
+ *
128
+ * `undefined` and `null` cannot express this. ADR-065's model has three:
129
+ *
130
+ * - `known` — the authoritative source returned a value. It is sent.
131
+ * - `absent` — the source successfully established that there is NO value.
132
+ * Absence is a FACT, not a failure, so the member is omitted and the request
133
+ * is sent: a policy that handles absence gets to handle it.
134
+ * - `unknown` — the value could not be established. The request is NOT sent.
135
+ * Sending it would have the gateway evaluate as though the attribute were
136
+ * absent, and the resulting decision — and every audit of it — would record
137
+ * that an attribute was weighed when nobody ever read it. That is the exact
138
+ * failure the whole surface refuses to commit, one hop earlier.
139
+ *
140
+ * Collapsing absent into unknown is the defect this type exists to prevent, and
141
+ * it is not hypothetical: on the platform side an ABSENT `subject.type` was
142
+ * read as the one supported value, so omitting the field bypassed the
143
+ * impersonation refusal that naming it correctly triggered.
144
+ *
145
+ * Where it may be used: inside the ATTRIBUTE bags — `context` on a request or a
146
+ * bulk envelope, and the `properties` bag on a subject, action or resource — at
147
+ * any depth. Not on the structural members (`subject.id`, `action.name`,
148
+ * `resource.type` …): those are the identity of the question being asked, not
149
+ * data about it, and an identity the caller cannot resolve is not an attribute
150
+ * whose absence a policy could evaluate — there is simply no request to make.
151
+ *
152
+ * @example
153
+ * ```typescript
154
+ * AuthZENAttribute.known('acme-corp');
155
+ * AuthZENAttribute.absent();
156
+ * AuthZENAttribute.unknown(AUTHZEN_UNKNOWN_RESOLUTION_FAILED);
157
+ * ```
158
+ */
159
+ export declare class AuthZENAttribute {
160
+ readonly state: AuthZENAttributeState;
161
+ readonly value: unknown;
162
+ readonly reason: string;
163
+ /**
164
+ * The marker, carried as an ordinary ENUMERABLE property.
165
+ *
166
+ * Recognising an attribute by `instanceof` alone has a silent failure in the
167
+ * dangerous direction: a bundler that duplicated this package gives two
168
+ * distinct classes. A non-enumerable Symbol brand fixes that and introduces
169
+ * the same failure by another route - every ordinary copy strips it, so
170
+ * `structuredClone`, a spread, a JSON round trip or a worker boundary turned
171
+ * an UNKNOWN attribute back into ordinary data and SENT it. Measured against
172
+ * a live gateway.
173
+ *
174
+ * Recognising it by SHAPE alone has the mirror failure: a caller's own bag
175
+ * carrying `state`/`value`/`reason` is read as an attribute, and a legitimate
176
+ * request is refused with a message asserting the caller could not establish
177
+ * a value it did establish.
178
+ *
179
+ * An enumerable marker closes both. It survives every ordinary copy, and no
180
+ * caller's data carries it by accident. The Python sibling uses the same key
181
+ * for the same reason.
182
+ */
183
+ readonly [AUTHZEN_ATTRIBUTE_MARKER] = true;
184
+ private constructor();
185
+ /** The source returned this value. */
186
+ static known(value: unknown): AuthZENAttribute;
187
+ /** The source established that there is no value. */
188
+ static absent(): AuthZENAttribute;
189
+ /**
190
+ * The value could not be established, for the named reason.
191
+ *
192
+ * The reason is mandatory. An unknown with no reason carries no more than
193
+ * `undefined` already did, and the whole point of the third state is that it
194
+ * says why.
195
+ */
196
+ static unknown(reason: string): AuthZENAttribute;
197
+ /**
198
+ * Whether `value` is a tri-state attribute, however it was copied.
199
+ *
200
+ * See the marker above for why this is neither a bare `instanceof` nor a
201
+ * structural shape check.
202
+ */
203
+ static is(value: unknown): value is AuthZENAttribute;
204
+ }
205
+ /**
206
+ * The decision, with the readings a Policy Enforcement Point acts on.
207
+ *
208
+ * It implements the generated wire type rather than replacing it, so `decision`
209
+ * and `context` remain exactly what the server sent while the readings below
210
+ * stay in hand-written code the generator never has to know about.
211
+ */
212
+ export declare class AuthZENDecision implements AuthZENResponse {
213
+ readonly decision: boolean;
214
+ readonly context?: AuthZENResponseContext;
215
+ constructor(response: AuthZENResponse);
216
+ /**
217
+ * Whether the enforcement point may proceed.
218
+ *
219
+ * Read this rather than `decision`. `decision` is AuthZEN 1.0's collapsed
220
+ * boolean; the operational STATE is what the policy engine actually produced,
221
+ * and exactly one state permits execution. Requiring both means a response
222
+ * whose boolean and state disagree can never be read as an allow — and such a
223
+ * response is refused before it gets here anyway, so this is the second of
224
+ * two locks rather than the only one.
225
+ *
226
+ * An allow with an undischarged MANDATORY obligation is not an allow. See
227
+ * `mandatoryObligations`.
228
+ */
229
+ get allowed(): boolean;
230
+ /**
231
+ * The four-valued operational state.
232
+ *
233
+ * `ERROR` when there is no context. Unreachable via `evaluate`, which refuses
234
+ * a context-less 200 before constructing this — but the type is public and a
235
+ * caller can build one by hand, and the safe reading of an outcome that
236
+ * carries no state is not ALLOW.
237
+ */
238
+ get state(): AuthZENOperationalState;
239
+ /**
240
+ * The id of the evaluation that DETERMINED this outcome.
241
+ *
242
+ * For a bulk envelope this is the entry that decided the meet, not the last
243
+ * one evaluated: it is the id an operator looks up to explain the answer.
244
+ */
245
+ get decisionId(): string | undefined;
246
+ /** The safe machine-readable reason code, when the server sent one. */
247
+ get reason(): AuthZENReasonCode | undefined;
248
+ /** The coarse outcome category, when the server sent one. */
249
+ get category(): AuthZENCategory | undefined;
250
+ /** Instructions the enforcement point must discharge before proceeding. */
251
+ get obligations(): AuthZENObligation[];
252
+ /**
253
+ * The obligations that are not optional.
254
+ *
255
+ * A mandatory obligation that cannot be discharged means the operation must
256
+ * NOT proceed, even though `allowed` is true. This SDK cannot make that call
257
+ * for you — whether your enforcement point can discharge a redaction is a
258
+ * fact about your seam, not about the decision — so it gives you the list and
259
+ * stays out of the way.
260
+ */
261
+ get mandatoryObligations(): AuthZENObligation[];
262
+ /** The approval challenge, when the state is CHALLENGE. */
263
+ get approval(): AuthZENApprovalRequirement | undefined;
264
+ }
265
+ /**
266
+ * Return the envelope with every tri-state attribute resolved to the wire.
267
+ *
268
+ * Throws `AuthZENRefusal` with `refusedBy: 'client'` and the JSON Pointer of
269
+ * the offending member when an attribute is UNKNOWN.
270
+ *
271
+ * The pointers match the server's own vocabulary — `/evaluation/...` for a
272
+ * singular envelope, `/evaluations/evaluations/<i>/...` for a plural entry — so
273
+ * a client-side refusal and a gateway refusal name the same member the same
274
+ * way, and a caller does not have to learn two pointer dialects.
275
+ */
276
+ export declare function resolveEnvelope(envelope: AuthZENEnvelope): AuthZENEnvelope;
277
+ /** Refuse an envelope that cannot produce a decision, before the round trip. */
278
+ export declare function checkEnvelopeComplete(envelope: AuthZENEnvelope): void;
279
+ /** What `evaluateEnvelope` needs from the SDK's own HTTP path. */
280
+ export type AuthZENTransport = (path: string, body: unknown, headers: Record<string, string>) => Promise<{
281
+ status: number;
282
+ body: string;
283
+ }>;
284
+ /**
285
+ * Build an envelope: RESOLVE, check completeness, then validate.
286
+ *
287
+ * The counterpart of the Python sibling's `build_envelope`, and it exists for
288
+ * the same reason: `evaluate` and `evaluateAll` must not each assemble an
289
+ * envelope inline, or the two entry points drift on the order these steps run
290
+ * in - and that order decides which of two problems a caller with two problems
291
+ * is told about.
292
+ */
293
+ export declare function buildEnvelope(evaluation?: AuthZENRequest, evaluations?: AuthZENBulk): AuthZENEnvelope;
294
+ /**
295
+ * Run one envelope through `send` and interpret the answer.
296
+ *
297
+ * `send` is the SDK's own transport — the same authenticated fetch wrapper,
298
+ * headers and heartbeat gate every other method uses. It is passed in rather
299
+ * than built here so this module owns the AuthZEN semantics and nothing else; a
300
+ * second transport would be a second place for credentials, timeouts and proxy
301
+ * configuration to drift out of step with the client the user configured.
302
+ */
303
+ export declare function evaluateEnvelope(send: AuthZENTransport, envelope: AuthZENEnvelope): Promise<AuthZENDecision>;
304
+ /**
305
+ * Guarantee no tri-state attribute reaches the wire.
306
+ *
307
+ * `resolveEnvelope` walks every bag the contract declares, so on today's
308
+ * contract this has nothing to catch. It exists for the case that does not
309
+ * announce itself: a container kind added to the artifact later, or an attribute
310
+ * reaching a member the resolver does not visit. Without it that attribute is
311
+ * not a crash - `JSON.stringify` turns the instance into an ordinary
312
+ * `{"state": …, "value": …, "reason": …}` object - so the request is SENT,
313
+ * carrying a resolver's internal shape where the gateway expects a value, and an
314
+ * UNKNOWN attribute reaches the network after all.
315
+ *
316
+ * The Python sibling carries the same control, and had exactly this defect:
317
+ * there the check ran after serialisation and could never fire.
318
+ */
319
+ export declare function assertFullyResolved(value: unknown, path?: string, depth?: number): void;
320
+ /**
321
+ * The exact document this SDK would send for `envelope`.
322
+ *
323
+ * Exported for tests and for support: "what did the SDK actually put on the
324
+ * wire" is the first question of every integration problem, and answering it by
325
+ * reading the client's source is how the answer ends up wrong.
326
+ */
327
+ export declare function toWire(envelope: AuthZENEnvelope): AuthZENEnvelope;
328
+ //# sourceMappingURL=authzen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authzen.d.ts","sourceRoot":"","sources":["../../src/authzen.ts"],"names":[],"mappings":"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"}