cedar-mcp-server 1.0.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 (215) hide show
  1. package/.editorconfig +12 -0
  2. package/.github/workflows/ci.yml +31 -0
  3. package/.github/workflows/release.yml +42 -0
  4. package/.nvmrc +1 -0
  5. package/CHANGELOG.md +241 -0
  6. package/CONTRIBUTING.md +83 -0
  7. package/LICENSE +182 -0
  8. package/README.md +1635 -0
  9. package/SECURITY.md +37 -0
  10. package/dist/http-server.d.ts +61 -0
  11. package/dist/http-server.d.ts.map +1 -0
  12. package/dist/http-server.js +194 -0
  13. package/dist/http-server.js.map +1 -0
  14. package/dist/index.d.ts +32 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +270 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/parser/policy-ast.d.ts +49 -0
  19. package/dist/parser/policy-ast.d.ts.map +1 -0
  20. package/dist/parser/policy-ast.js +311 -0
  21. package/dist/parser/policy-ast.js.map +1 -0
  22. package/dist/prompts/index.d.ts +38 -0
  23. package/dist/prompts/index.d.ts.map +1 -0
  24. package/dist/prompts/index.js +172 -0
  25. package/dist/prompts/index.js.map +1 -0
  26. package/dist/resources/ref-resolver.d.ts +23 -0
  27. package/dist/resources/ref-resolver.d.ts.map +1 -0
  28. package/dist/resources/ref-resolver.js +128 -0
  29. package/dist/resources/ref-resolver.js.map +1 -0
  30. package/dist/resources/store-manager.d.ts +64 -0
  31. package/dist/resources/store-manager.d.ts.map +1 -0
  32. package/dist/resources/store-manager.js +221 -0
  33. package/dist/resources/store-manager.js.map +1 -0
  34. package/dist/server.d.ts +18 -0
  35. package/dist/server.d.ts.map +1 -0
  36. package/dist/server.js +539 -0
  37. package/dist/server.js.map +1 -0
  38. package/dist/tools/advise/avp-rules.d.ts +49 -0
  39. package/dist/tools/advise/avp-rules.d.ts.map +1 -0
  40. package/dist/tools/advise/avp-rules.js +59 -0
  41. package/dist/tools/advise/avp-rules.js.map +1 -0
  42. package/dist/tools/advise/cedar-patterns.d.ts +24 -0
  43. package/dist/tools/advise/cedar-patterns.d.ts.map +1 -0
  44. package/dist/tools/advise/cedar-patterns.js +57 -0
  45. package/dist/tools/advise/cedar-patterns.js.map +1 -0
  46. package/dist/tools/advise/context-builder.d.ts +28 -0
  47. package/dist/tools/advise/context-builder.d.ts.map +1 -0
  48. package/dist/tools/advise/context-builder.js +89 -0
  49. package/dist/tools/advise/context-builder.js.map +1 -0
  50. package/dist/tools/advise/gotchas.d.ts +15 -0
  51. package/dist/tools/advise/gotchas.d.ts.map +1 -0
  52. package/dist/tools/advise/gotchas.js +83 -0
  53. package/dist/tools/advise/gotchas.js.map +1 -0
  54. package/dist/tools/advise.d.ts +96 -0
  55. package/dist/tools/advise.d.ts.map +1 -0
  56. package/dist/tools/advise.js +258 -0
  57. package/dist/tools/advise.js.map +1 -0
  58. package/dist/tools/authorize-batch.d.ts +35 -0
  59. package/dist/tools/authorize-batch.d.ts.map +1 -0
  60. package/dist/tools/authorize-batch.js +262 -0
  61. package/dist/tools/authorize-batch.js.map +1 -0
  62. package/dist/tools/authorize.d.ts +115 -0
  63. package/dist/tools/authorize.d.ts.map +1 -0
  64. package/dist/tools/authorize.js +373 -0
  65. package/dist/tools/authorize.js.map +1 -0
  66. package/dist/tools/check-change.d.ts +19 -0
  67. package/dist/tools/check-change.d.ts.map +1 -0
  68. package/dist/tools/check-change.js +91 -0
  69. package/dist/tools/check-change.js.map +1 -0
  70. package/dist/tools/diff-schema.d.ts +103 -0
  71. package/dist/tools/diff-schema.d.ts.map +1 -0
  72. package/dist/tools/diff-schema.js +379 -0
  73. package/dist/tools/diff-schema.js.map +1 -0
  74. package/dist/tools/diff-stores.d.ts +45 -0
  75. package/dist/tools/diff-stores.d.ts.map +1 -0
  76. package/dist/tools/diff-stores.js +222 -0
  77. package/dist/tools/diff-stores.js.map +1 -0
  78. package/dist/tools/explain.d.ts +80 -0
  79. package/dist/tools/explain.d.ts.map +1 -0
  80. package/dist/tools/explain.js +187 -0
  81. package/dist/tools/explain.js.map +1 -0
  82. package/dist/tools/format.d.ts +11 -0
  83. package/dist/tools/format.d.ts.map +1 -0
  84. package/dist/tools/format.js +20 -0
  85. package/dist/tools/format.js.map +1 -0
  86. package/dist/tools/generate-sample.d.ts +28 -0
  87. package/dist/tools/generate-sample.d.ts.map +1 -0
  88. package/dist/tools/generate-sample.js +568 -0
  89. package/dist/tools/generate-sample.js.map +1 -0
  90. package/dist/tools/link-template.d.ts +17 -0
  91. package/dist/tools/link-template.d.ts.map +1 -0
  92. package/dist/tools/link-template.js +78 -0
  93. package/dist/tools/link-template.js.map +1 -0
  94. package/dist/tools/list-template-links.d.ts +16 -0
  95. package/dist/tools/list-template-links.d.ts.map +1 -0
  96. package/dist/tools/list-template-links.js +22 -0
  97. package/dist/tools/list-template-links.js.map +1 -0
  98. package/dist/tools/list-templates.d.ts +16 -0
  99. package/dist/tools/list-templates.d.ts.map +1 -0
  100. package/dist/tools/list-templates.js +36 -0
  101. package/dist/tools/list-templates.js.map +1 -0
  102. package/dist/tools/translate.d.ts +11 -0
  103. package/dist/tools/translate.d.ts.map +1 -0
  104. package/dist/tools/translate.js +53 -0
  105. package/dist/tools/translate.js.map +1 -0
  106. package/dist/tools/validate-entities.d.ts +19 -0
  107. package/dist/tools/validate-entities.d.ts.map +1 -0
  108. package/dist/tools/validate-entities.js +88 -0
  109. package/dist/tools/validate-entities.js.map +1 -0
  110. package/dist/tools/validate-schema.d.ts +22 -0
  111. package/dist/tools/validate-schema.d.ts.map +1 -0
  112. package/dist/tools/validate-schema.js +89 -0
  113. package/dist/tools/validate-schema.js.map +1 -0
  114. package/dist/tools/validate-template.d.ts +18 -0
  115. package/dist/tools/validate-template.d.ts.map +1 -0
  116. package/dist/tools/validate-template.js +59 -0
  117. package/dist/tools/validate-template.js.map +1 -0
  118. package/dist/tools/validate.d.ts +90 -0
  119. package/dist/tools/validate.d.ts.map +1 -0
  120. package/dist/tools/validate.js +351 -0
  121. package/dist/tools/validate.js.map +1 -0
  122. package/dist/utils/format-detector.d.ts +49 -0
  123. package/dist/utils/format-detector.d.ts.map +1 -0
  124. package/dist/utils/format-detector.js +298 -0
  125. package/dist/utils/format-detector.js.map +1 -0
  126. package/examples/README.md +36 -0
  127. package/examples/abac-multi-tenant/README.md +150 -0
  128. package/examples/abac-multi-tenant/entities/users-and-docs.json +33 -0
  129. package/examples/abac-multi-tenant/policies/member-read-internal.cedar +9 -0
  130. package/examples/abac-multi-tenant/policies/owner-full-access.cedar +9 -0
  131. package/examples/abac-multi-tenant/policies/premium-share-guard.cedar +9 -0
  132. package/examples/abac-multi-tenant/policies/private-doc-guard.cedar +13 -0
  133. package/examples/abac-multi-tenant/run.ts +92 -0
  134. package/examples/abac-multi-tenant/schema.json +60 -0
  135. package/examples/api-gateway-path-routing/README.md +154 -0
  136. package/examples/api-gateway-path-routing/entities/users-and-roles.json +20 -0
  137. package/examples/api-gateway-path-routing/policies/admin-full-access.cedar +6 -0
  138. package/examples/api-gateway-path-routing/policies/developer-projects.cedar +14 -0
  139. package/examples/api-gateway-path-routing/policies/viewer-readonly.cedar +10 -0
  140. package/examples/api-gateway-path-routing/run.ts +108 -0
  141. package/examples/api-gateway-path-routing/schema.json +54 -0
  142. package/examples/rbac-document-management/README.md +167 -0
  143. package/examples/rbac-document-management/entities/users-and-docs.json +43 -0
  144. package/examples/rbac-document-management/policies/admin.cedar +6 -0
  145. package/examples/rbac-document-management/policies/editor.cedar +6 -0
  146. package/examples/rbac-document-management/policies/top-secret-forbid.cedar +13 -0
  147. package/examples/rbac-document-management/policies/viewer.cedar +6 -0
  148. package/examples/rbac-document-management/run.ts +87 -0
  149. package/examples/rbac-document-management/schema.json +57 -0
  150. package/package.json +50 -0
  151. package/src/http-server.ts +239 -0
  152. package/src/index.ts +294 -0
  153. package/src/parser/policy-ast.ts +345 -0
  154. package/src/prompts/README.md +3 -0
  155. package/src/prompts/index.ts +217 -0
  156. package/src/resources/ref-resolver.ts +134 -0
  157. package/src/resources/store-manager.ts +248 -0
  158. package/src/server.ts +711 -0
  159. package/src/tools/advise/avp-rules.ts +70 -0
  160. package/src/tools/advise/cedar-patterns.ts +73 -0
  161. package/src/tools/advise/context-builder.ts +109 -0
  162. package/src/tools/advise/gotchas.ts +92 -0
  163. package/src/tools/advise.ts +366 -0
  164. package/src/tools/authorize-batch.ts +345 -0
  165. package/src/tools/authorize.ts +464 -0
  166. package/src/tools/check-change.ts +119 -0
  167. package/src/tools/diff-schema.ts +510 -0
  168. package/src/tools/diff-stores.ts +298 -0
  169. package/src/tools/explain.ts +278 -0
  170. package/src/tools/format.ts +33 -0
  171. package/src/tools/generate-sample.ts +665 -0
  172. package/src/tools/link-template.ts +109 -0
  173. package/src/tools/list-template-links.ts +41 -0
  174. package/src/tools/list-templates.ts +55 -0
  175. package/src/tools/translate.ts +66 -0
  176. package/src/tools/validate-entities.ts +125 -0
  177. package/src/tools/validate-schema.ts +128 -0
  178. package/src/tools/validate-template.ts +72 -0
  179. package/src/tools/validate.ts +459 -0
  180. package/src/utils/format-detector.ts +356 -0
  181. package/test/fixtures/docmgmt.ts +121 -0
  182. package/test/fixtures/multitenant.ts +163 -0
  183. package/test/index.test.ts +96 -0
  184. package/test/integration/e2e/behavior.test.ts +359 -0
  185. package/test/integration/e2e/edge-cases.test.ts +365 -0
  186. package/test/integration/e2e/failure-modes.test.ts +266 -0
  187. package/test/integration/e2e/protocol.test.ts +252 -0
  188. package/test/integration/http-smoke.test.ts +588 -0
  189. package/test/integration/smoke.test.ts +475 -0
  190. package/test/prompts/prompts.test.ts +173 -0
  191. package/test/property/properties.test.ts +234 -0
  192. package/test/resources/ref-resolver.test.ts +186 -0
  193. package/test/resources/store-manager.test.ts +344 -0
  194. package/test/setup.test.ts +7 -0
  195. package/test/tools/advise/avp-rules.test.ts +76 -0
  196. package/test/tools/advise.test.ts +339 -0
  197. package/test/tools/authorize-batch.test.ts +459 -0
  198. package/test/tools/authorize.test.ts +682 -0
  199. package/test/tools/check-change.test.ts +104 -0
  200. package/test/tools/cross-fixture.test.ts +170 -0
  201. package/test/tools/diff-schema.test.ts +355 -0
  202. package/test/tools/diff-stores.test.ts +291 -0
  203. package/test/tools/explain.test.ts +221 -0
  204. package/test/tools/format.test.ts +33 -0
  205. package/test/tools/generate-sample.test.ts +480 -0
  206. package/test/tools/link-template.test.ts +90 -0
  207. package/test/tools/list-templates.test.ts +151 -0
  208. package/test/tools/translate.test.ts +89 -0
  209. package/test/tools/validate-entities.test.ts +178 -0
  210. package/test/tools/validate-schema.test.ts +86 -0
  211. package/test/tools/validate-template.test.ts +89 -0
  212. package/test/tools/validate.test.ts +331 -0
  213. package/test/utils/format-detector.test.ts +518 -0
  214. package/tsconfig.json +17 -0
  215. package/vitest.config.ts +13 -0
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Cedar input format detection and normalization.
3
+ *
4
+ * Three AVP SDK variants exist in the wild — all need conversion to Cedar WASM format:
5
+ *
6
+ * Ruby SDK (snake_case): identifier.entity_type / entity_id, string/long/boolean
7
+ * Python/JS SDK (camelCase): identifier.entityType / entityId, string/long/boolean
8
+ * Official API/Console (PascalCase): Identifier.EntityType / EntityId, String/Long/Boolean
9
+ *
10
+ * Cedar WASM format:
11
+ * uid: { type, id }, attrs: { key: rawValue }, parents: [{ type, id }]
12
+ * Entity refs in attrs: { __entity: { type, id } }
13
+ * Extension types: { __extn: { fn, arg } }
14
+ *
15
+ * Detection strategy: case-insensitive key lookup handles all three casing variants
16
+ * in a single code path. One normalizer to rule them all.
17
+ *
18
+ * Cedar CLI format (uid.__entity wrapper): WASM accepts natively — no conversion needed.
19
+ *
20
+ * Attribute value wrapper detection rule:
21
+ * Single-key object whose key (lowercased) is a known AVP type name AND whose value
22
+ * is the matching primitive. Multi-key objects are Cedar Records — not touched.
23
+ *
24
+ * Limitation: a Cedar Record with exactly one field named "string"/"long"/"boolean"
25
+ * would be misidentified. Adding a second field removes the ambiguity.
26
+ *
27
+ * Sources confirmed by SDK docs (2026-05-20):
28
+ * Ruby: entity_type/entity_id/entity_identifier (snake_case)
29
+ * Python/JS: entityType/entityId/entityIdentifier (camelCase)
30
+ * Official API: EntityType/EntityId/EntityIdentifier (PascalCase)
31
+ */
32
+ // ─── Case-insensitive key access ──────────────────────────────────────────────
33
+ /** Find the value of a key case-insensitively. First match wins. */
34
+ function getCI(obj, key) {
35
+ const lower = key.toLowerCase();
36
+ const found = Object.keys(obj).find((k) => k.toLowerCase() === lower);
37
+ return found !== undefined ? obj[found] : undefined;
38
+ }
39
+ function hasKeyCI(obj, key) {
40
+ const lower = key.toLowerCase();
41
+ return Object.keys(obj).some((k) => k.toLowerCase() === lower);
42
+ }
43
+ // ─── Detection ────────────────────────────────────────────────────────────────
44
+ export function detectFormat(entities, principal, action, resource) {
45
+ // AVP principal/action/resource — any casing of entity_type/entityType/EntityType
46
+ if (isAvpRef(principal) || isAvpActionRef(action) || isAvpRef(resource)) {
47
+ return {
48
+ format: "avp",
49
+ confidence: "high",
50
+ note: "Principal, action, or resource is in AVP format (entity_type/entityType/EntityType keys). Automatically converted to Cedar format.",
51
+ };
52
+ }
53
+ // AVP entity list: `identifier` key (case-insensitive) is the clearest signal
54
+ if (entities.some(hasAvpIdentifierKey)) {
55
+ return {
56
+ format: "avp",
57
+ confidence: "high",
58
+ note: "Entities are in AVP format (identifier/Identifier key, typed attribute wrappers). Automatically converted to Cedar format.",
59
+ };
60
+ }
61
+ // AVP-typed attribute values without identifier key (partial AVP)
62
+ if (entities.some(hasAvpTypedAttributeValues)) {
63
+ return {
64
+ format: "avp",
65
+ confidence: "medium",
66
+ note: "Entity attributes appear to use AVP typed wrappers (string/long/boolean/set/record). Automatically unwrapped to raw Cedar values.",
67
+ };
68
+ }
69
+ // Cedar CLI uid.__entity wrapper — WASM accepts natively, no conversion needed
70
+ if (entities.some(hasCedarCliEntityWrapper)) {
71
+ return {
72
+ format: "cedar_cli",
73
+ confidence: "high",
74
+ note: "Entity UIDs use the __entity wrapper (Cedar CLI format). Compatible with Cedar WASM — no conversion needed.",
75
+ };
76
+ }
77
+ return {
78
+ format: "cedar",
79
+ confidence: "high",
80
+ note: "Input is in Cedar/WASM format.",
81
+ };
82
+ }
83
+ // ─── Normalization ────────────────────────────────────────────────────────────
84
+ export function normalizeEntities(entities, format) {
85
+ if (format === "cedar" || format === "cedar_cli")
86
+ return entities;
87
+ return entities.map(normalizeAvpEntity);
88
+ }
89
+ export function normalizePrincipalRef(ref) {
90
+ // Cedar string literal: 'Ns::Type::"id"'
91
+ if (typeof ref === "string") {
92
+ const match = ref.match(/^(.+)::"(.+)"$/);
93
+ if (!match)
94
+ return { error: `Invalid Cedar entity reference: "${ref}". Expected: Namespace::Type::"id"` };
95
+ return { type: match[1], id: match[2] };
96
+ }
97
+ if (typeof ref !== "object" || ref === null) {
98
+ return { error: `Unrecognized entity reference format: ${JSON.stringify(ref)}` };
99
+ }
100
+ const obj = ref;
101
+ // WASM native: { type, id }
102
+ if (typeof obj["type"] === "string" && typeof obj["id"] === "string") {
103
+ return { type: obj["type"], id: obj["id"] };
104
+ }
105
+ // WASM Cedar CLI: { __entity: { type, id } }
106
+ if (obj["__entity"] && typeof obj["__entity"] === "object") {
107
+ const inner = obj["__entity"];
108
+ if (typeof inner["type"] === "string" && typeof inner["id"] === "string") {
109
+ return { type: inner["type"], id: inner["id"] };
110
+ }
111
+ }
112
+ // AVP entity ref — all three casings (entity_type / entityType / EntityType)
113
+ const entityType = getCI(obj, "entity_type") ?? getCI(obj, "entityType") ?? getCI(obj, "EntityType");
114
+ const entityId = getCI(obj, "entity_id") ?? getCI(obj, "entityId") ?? getCI(obj, "EntityId");
115
+ if (typeof entityType === "string" && typeof entityId === "string") {
116
+ return { type: entityType, id: entityId };
117
+ }
118
+ // AVP action ref — all three casings (action_type / actionType / ActionType)
119
+ const actionType = getCI(obj, "action_type") ?? getCI(obj, "actionType") ?? getCI(obj, "ActionType");
120
+ const actionId = getCI(obj, "action_id") ?? getCI(obj, "actionId") ?? getCI(obj, "ActionId");
121
+ if (typeof actionType === "string" && typeof actionId === "string") {
122
+ return { type: actionType, id: actionId };
123
+ }
124
+ return { error: `Unrecognized entity reference format: ${JSON.stringify(ref)}` };
125
+ }
126
+ export function unwrapAvpAttributes(attrs) {
127
+ const result = {};
128
+ for (const [key, value] of Object.entries(attrs)) {
129
+ result[key] = unwrapAvpValue(value);
130
+ }
131
+ return result;
132
+ }
133
+ // ─── Private: detection helpers ───────────────────────────────────────────────
134
+ function isAvpRef(ref) {
135
+ if (typeof ref !== "object" || ref === null)
136
+ return false;
137
+ const obj = ref;
138
+ const hasType = typeof getCI(obj, "entity_type") === "string" ||
139
+ typeof getCI(obj, "entityType") === "string" ||
140
+ typeof getCI(obj, "EntityType") === "string";
141
+ const hasId = typeof getCI(obj, "entity_id") === "string" ||
142
+ typeof getCI(obj, "entityId") === "string" ||
143
+ typeof getCI(obj, "EntityId") === "string";
144
+ return hasType && hasId;
145
+ }
146
+ function isAvpActionRef(ref) {
147
+ if (typeof ref !== "object" || ref === null)
148
+ return false;
149
+ const obj = ref;
150
+ const hasType = typeof getCI(obj, "action_type") === "string" ||
151
+ typeof getCI(obj, "actionType") === "string" ||
152
+ typeof getCI(obj, "ActionType") === "string";
153
+ const hasId = typeof getCI(obj, "action_id") === "string" ||
154
+ typeof getCI(obj, "actionId") === "string" ||
155
+ typeof getCI(obj, "ActionId") === "string";
156
+ return hasType && hasId;
157
+ }
158
+ function hasAvpIdentifierKey(entity) {
159
+ if (typeof entity !== "object" || entity === null)
160
+ return false;
161
+ return hasKeyCI(entity, "identifier");
162
+ }
163
+ function hasAvpTypedAttributeValues(entity) {
164
+ if (typeof entity !== "object" || entity === null)
165
+ return false;
166
+ const e = entity;
167
+ const rawAttrs = getCI(e, "attributes") ??
168
+ getCI(e, "Attributes") ??
169
+ e["attrs"];
170
+ if (!rawAttrs || typeof rawAttrs !== "object")
171
+ return false;
172
+ return Object.values(rawAttrs).some(isAvpTypedValue);
173
+ }
174
+ function hasCedarCliEntityWrapper(entity) {
175
+ if (typeof entity !== "object" || entity === null)
176
+ return false;
177
+ const e = entity;
178
+ const uid = e["uid"];
179
+ if (typeof uid !== "object" || uid === null)
180
+ return false;
181
+ return "__entity" in uid;
182
+ }
183
+ /**
184
+ * Detects AVP typed value wrappers (case-insensitive type key).
185
+ * Rule: single-key object whose key lowercased is a known AVP type name with matching value type.
186
+ * Multi-key objects are Cedar Records.
187
+ */
188
+ function isAvpTypedValue(v) {
189
+ if (typeof v !== "object" || v === null || Array.isArray(v))
190
+ return false;
191
+ const keys = Object.keys(v);
192
+ if (keys.length !== 1)
193
+ return false;
194
+ const key = keys[0].toLowerCase();
195
+ const val = v[keys[0]];
196
+ return ((key === "string" && typeof val === "string") ||
197
+ (key === "long" && typeof val === "number") ||
198
+ (key === "boolean" && typeof val === "boolean") ||
199
+ key === "entityidentifier" ||
200
+ key === "entity_identifier" ||
201
+ key === "set" ||
202
+ key === "record" ||
203
+ key === "ipaddr" ||
204
+ key === "ipaddress" ||
205
+ key === "decimal" ||
206
+ key === "datetime" ||
207
+ key === "duration");
208
+ }
209
+ // ─── Private: value unwrapping ────────────────────────────────────────────────
210
+ function unwrapAvpValue(v) {
211
+ if (typeof v !== "object" || v === null || Array.isArray(v))
212
+ return v;
213
+ const keys = Object.keys(v);
214
+ if (keys.length !== 1)
215
+ return v; // Multi-key object = Cedar Record, not AVP wrapper
216
+ const key = keys[0];
217
+ const lowerKey = key.toLowerCase();
218
+ const val = v[key];
219
+ switch (lowerKey) {
220
+ case "string":
221
+ return typeof val === "string" ? val : v;
222
+ case "long":
223
+ return typeof val === "number" ? val : v;
224
+ case "boolean":
225
+ return typeof val === "boolean" ? val : v;
226
+ // Entity reference → WASM __entity
227
+ case "entityidentifier":
228
+ case "entity_identifier": {
229
+ const ref = resolveAvpEntityRef(val);
230
+ return ref ? { __entity: ref } : v;
231
+ }
232
+ // Set → array (recursively normalize values)
233
+ case "set":
234
+ return Array.isArray(val) ? val.map(unwrapAvpValue) : v;
235
+ // Record → object (recursively normalize values)
236
+ case "record":
237
+ if (typeof val === "object" && val !== null && !Array.isArray(val)) {
238
+ return unwrapAvpAttributes(val);
239
+ }
240
+ return v;
241
+ // Cedar extension types → WASM __extn format
242
+ case "ipaddr":
243
+ case "ipaddress":
244
+ return typeof val === "string" ? { __extn: { fn: "ip", arg: val } } : v;
245
+ case "decimal":
246
+ return typeof val === "string" ? { __extn: { fn: "decimal", arg: val } } : v;
247
+ case "datetime":
248
+ return typeof val === "string" ? { __extn: { fn: "datetime", arg: val } } : v;
249
+ case "duration":
250
+ return typeof val === "string" ? { __extn: { fn: "duration", arg: val } } : v;
251
+ default:
252
+ return v;
253
+ }
254
+ }
255
+ /** Resolves an AVP entity reference object (any casing) to { type, id }. */
256
+ function resolveAvpEntityRef(ref) {
257
+ if (typeof ref !== "object" || ref === null)
258
+ return null;
259
+ const obj = ref;
260
+ const type = (getCI(obj, "entity_type") ?? getCI(obj, "entityType") ?? getCI(obj, "EntityType"));
261
+ const id = (getCI(obj, "entity_id") ?? getCI(obj, "entityId") ?? getCI(obj, "EntityId"));
262
+ if (type && id)
263
+ return { type, id };
264
+ return null;
265
+ }
266
+ // ─── Private: entity normalization ───────────────────────────────────────────
267
+ function normalizeAvpEntity(entity) {
268
+ if (typeof entity !== "object" || entity === null)
269
+ return entity;
270
+ const e = entity;
271
+ // UID: find identifier/Identifier key (any casing), convert to { type, id }
272
+ const identifierKey = Object.keys(e).find((k) => k.toLowerCase() === "identifier");
273
+ let uid;
274
+ if (identifierKey) {
275
+ const idObj = e[identifierKey];
276
+ const type = getCI(idObj, "entity_type") ?? getCI(idObj, "entityType") ?? getCI(idObj, "EntityType");
277
+ const id = getCI(idObj, "entity_id") ?? getCI(idObj, "entityId") ?? getCI(idObj, "EntityId");
278
+ uid = { type, id };
279
+ }
280
+ else {
281
+ uid = e["uid"];
282
+ }
283
+ // Attrs: find attributes/Attributes key (any casing), fall back to attrs
284
+ const attrsKey = Object.keys(e).find((k) => k.toLowerCase() === "attributes");
285
+ const rawAttrs = (attrsKey ? e[attrsKey] : e["attrs"]) ?? {};
286
+ const attrs = unwrapAvpAttributes(rawAttrs);
287
+ // Parents: find parents/Parents key (any casing), convert entity_type/entityType/EntityType → type/id
288
+ const parentsKey = Object.keys(e).find((k) => k.toLowerCase() === "parents");
289
+ const rawParents = ((parentsKey ? e[parentsKey] : e["parents"]) ?? []);
290
+ const parents = rawParents.map((p) => {
291
+ if (typeof p !== "object" || p === null)
292
+ return p;
293
+ const ref = resolveAvpEntityRef(p);
294
+ return ref ?? p;
295
+ });
296
+ return { uid, attrs, parents };
297
+ }
298
+ //# sourceMappingURL=format-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-detector.js","sourceRoot":"","sources":["../../src/utils/format-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAmBH,iFAAiF;AAEjF,oEAAoE;AACpE,SAAS,KAAK,CAAC,GAA4B,EAAE,GAAW;IACtD,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,CAAC;IACtE,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACtD,CAAC;AAED,SAAS,QAAQ,CAAC,GAA4B,EAAE,GAAW;IACzD,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,CAAC;AACjE,CAAC;AAED,iFAAiF;AAEjF,MAAM,UAAU,YAAY,CAC1B,QAAmB,EACnB,SAAkB,EAClB,MAAe,EACf,QAAiB;IAEjB,kFAAkF;IAClF,IAAI,QAAQ,CAAC,SAAS,CAAC,IAAI,cAAc,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxE,OAAO;YACL,MAAM,EAAE,KAAK;YACb,UAAU,EAAE,MAAM;YAClB,IAAI,EAAE,oIAAoI;SAC3I,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,IAAI,QAAQ,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACvC,OAAO;YACL,MAAM,EAAE,KAAK;YACb,UAAU,EAAE,MAAM;YAClB,IAAI,EAAE,4HAA4H;SACnI,CAAC;IACJ,CAAC;IAED,kEAAkE;IAClE,IAAI,QAAQ,CAAC,IAAI,CAAC,0BAA0B,CAAC,EAAE,CAAC;QAC9C,OAAO;YACL,MAAM,EAAE,KAAK;YACb,UAAU,EAAE,QAAQ;YACpB,IAAI,EAAE,mIAAmI;SAC1I,CAAC;IACJ,CAAC;IAED,+EAA+E;IAC/E,IAAI,QAAQ,CAAC,IAAI,CAAC,wBAAwB,CAAC,EAAE,CAAC;QAC5C,OAAO;YACL,MAAM,EAAE,WAAW;YACnB,UAAU,EAAE,MAAM;YAClB,IAAI,EAAE,6GAA6G;SACpH,CAAC;IACJ,CAAC;IAED,OAAO;QACL,MAAM,EAAE,OAAO;QACf,UAAU,EAAE,MAAM;QAClB,IAAI,EAAE,gCAAgC;KACvC,CAAC;AACJ,CAAC;AAED,iFAAiF;AAEjF,MAAM,UAAU,iBAAiB,CAAC,QAAmB,EAAE,MAAmB;IACxE,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,WAAW;QAAE,OAAO,QAAQ,CAAC;IAClE,OAAO,QAAQ,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAY;IAChD,yCAAyC;IACzC,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,KAAK,EAAE,oCAAoC,GAAG,oCAAoC,EAAE,CAAC;QAC1G,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,CAAC;IAC5C,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC5C,OAAO,EAAE,KAAK,EAAE,yCAAyC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;IACnF,CAAC;IAED,MAAM,GAAG,GAAG,GAA8B,CAAC;IAE3C,4BAA4B;IAC5B,IAAI,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;QACrE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;IAC9C,CAAC;IAED,6CAA6C;IAC7C,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,OAAO,GAAG,CAAC,UAAU,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC3D,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,CAA4B,CAAC;QACzD,IAAI,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;YACzE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAClD,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,EAAE,aAAa,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IACrG,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC7F,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACnE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC;IAC5C,CAAC;IAED,6EAA6E;IAC7E,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,EAAE,aAAa,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IACrG,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC7F,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACnE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC;IAC5C,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,yCAAyC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,KAA8B;IAE9B,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,MAAM,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,iFAAiF;AAEjF,SAAS,QAAQ,CAAC,GAAY;IAC5B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC1D,MAAM,GAAG,GAAG,GAA8B,CAAC;IAC3C,MAAM,OAAO,GACX,OAAO,KAAK,CAAC,GAAG,EAAE,aAAa,CAAC,KAAK,QAAQ;QAC7C,OAAO,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,KAAK,QAAQ;QAC5C,OAAO,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,KAAK,QAAQ,CAAC;IAC/C,MAAM,KAAK,GACT,OAAO,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,KAAK,QAAQ;QAC3C,OAAO,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,QAAQ;QAC1C,OAAO,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,QAAQ,CAAC;IAC7C,OAAO,OAAO,IAAI,KAAK,CAAC;AAC1B,CAAC;AAED,SAAS,cAAc,CAAC,GAAY;IAClC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC1D,MAAM,GAAG,GAAG,GAA8B,CAAC;IAC3C,MAAM,OAAO,GACX,OAAO,KAAK,CAAC,GAAG,EAAE,aAAa,CAAC,KAAK,QAAQ;QAC7C,OAAO,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,KAAK,QAAQ;QAC5C,OAAO,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,KAAK,QAAQ,CAAC;IAC/C,MAAM,KAAK,GACT,OAAO,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,KAAK,QAAQ;QAC3C,OAAO,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,QAAQ;QAC1C,OAAO,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,QAAQ,CAAC;IAC7C,OAAO,OAAO,IAAI,KAAK,CAAC;AAC1B,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAe;IAC1C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAChE,OAAO,QAAQ,CAAC,MAAiC,EAAE,YAAY,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,0BAA0B,CAAC,MAAe;IACjD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAChE,MAAM,CAAC,GAAG,MAAiC,CAAC;IAC5C,MAAM,QAAQ,GACZ,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC;QACtB,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC;QACtB,CAAC,CAAC,OAAO,CAAC,CAAC;IACb,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5D,OAAO,MAAM,CAAC,MAAM,CAAC,QAAmC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;AAClF,CAAC;AAED,SAAS,wBAAwB,CAAC,MAAe;IAC/C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAChE,MAAM,CAAC,GAAG,MAAiC,CAAC;IAC5C,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IACrB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC1D,OAAO,UAAU,IAAK,GAA+B,CAAC;AACxD,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,CAAU;IACjC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1E,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAW,CAAC,CAAC;IACtC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC;IACnC,MAAM,GAAG,GAAI,CAA6B,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;IACrD,OAAO,CACL,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,KAAK,QAAQ,CAAC;QAC7C,CAAC,GAAG,KAAK,MAAM,IAAI,OAAO,GAAG,KAAK,QAAQ,CAAC;QAC3C,CAAC,GAAG,KAAK,SAAS,IAAI,OAAO,GAAG,KAAK,SAAS,CAAC;QAC/C,GAAG,KAAK,kBAAkB;QAC1B,GAAG,KAAK,mBAAmB;QAC3B,GAAG,KAAK,KAAK;QACb,GAAG,KAAK,QAAQ;QAChB,GAAG,KAAK,QAAQ;QAChB,GAAG,KAAK,WAAW;QACnB,GAAG,KAAK,SAAS;QACjB,GAAG,KAAK,UAAU;QAClB,GAAG,KAAK,UAAU,CACnB,CAAC;AACJ,CAAC;AAED,iFAAiF;AAEjF,SAAS,cAAc,CAAC,CAAU;IAChC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IAEtE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAW,CAAC,CAAC;IACtC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC,CAAC,mDAAmD;IAEpF,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;IACrB,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IACnC,MAAM,GAAG,GAAI,CAA6B,CAAC,GAAG,CAAC,CAAC;IAEhD,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ;YACX,OAAO,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,KAAK,MAAM;YACT,OAAO,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,KAAK,SAAS;YACZ,OAAO,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAE5C,mCAAmC;QACnC,KAAK,kBAAkB,CAAC;QACxB,KAAK,mBAAmB,CAAC,CAAC,CAAC;YACzB,MAAM,GAAG,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;YACrC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACrC,CAAC;QAED,6CAA6C;QAC7C,KAAK,KAAK;YACR,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE1D,iDAAiD;QACjD,KAAK,QAAQ;YACX,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBACnE,OAAO,mBAAmB,CAAC,GAA8B,CAAC,CAAC;YAC7D,CAAC;YACD,OAAO,CAAC,CAAC;QAEX,6CAA6C;QAC7C,KAAK,QAAQ,CAAC;QACd,KAAK,WAAW;YACd,OAAO,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1E,KAAK,SAAS;YACZ,OAAO,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/E,KAAK,UAAU;YACb,OAAO,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAChF,KAAK,UAAU;YACb,OAAO,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAEhF;YACE,OAAO,CAAC,CAAC;IACb,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,SAAS,mBAAmB,CAAC,GAAY;IACvC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACzD,MAAM,GAAG,GAAG,GAA8B,CAAC;IAC3C,MAAM,IAAI,GACR,CAAC,KAAK,CAAC,GAAG,EAAE,aAAa,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,CAAuB,CAAC;IAC5G,MAAM,EAAE,GACN,CAAC,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,CAAuB,CAAC;IACtG,IAAI,IAAI,IAAI,EAAE;QAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IACpC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gFAAgF;AAEhF,SAAS,kBAAkB,CAAC,MAAe;IACzC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IACjE,MAAM,CAAC,GAAG,MAAiC,CAAC;IAE5C,4EAA4E;IAC5E,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,CAAC;IACnF,IAAI,GAAY,CAAC;IACjB,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,KAAK,GAAG,CAAC,CAAC,aAAa,CAA4B,CAAC;QAC1D,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QACrG,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,WAAW,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAC7F,GAAG,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IACrB,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC;IAED,yEAAyE;IACzE,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,CAAC;IAC9E,MAAM,QAAQ,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7D,MAAM,KAAK,GAAG,mBAAmB,CAAC,QAAmC,CAAC,CAAC;IAEvE,sGAAsG;IACtG,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,CAAC;IAC7E,MAAM,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAc,CAAC;IACpF,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACnC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI;YAAE,OAAO,CAAC,CAAC;QAClD,MAAM,GAAG,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC;QACnC,OAAO,GAAG,IAAI,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,36 @@
1
+ # Examples
2
+
3
+ Three self-contained Cedar authorization scenarios. Each one has a schema, policy files, an entity store, a runnable script, and a README with copy-paste prompts for Claude Code.
4
+
5
+ ## Pick your pattern
6
+
7
+ | Example | Cedar patterns | Start here if... |
8
+ |---------|---------------|-----------------|
9
+ | [rbac-document-management](./rbac-document-management/) | Role membership, `forbid` + `unless`, default deny | You're new to Cedar or building a simple role-based system |
10
+ | [abac-multi-tenant](./abac-multi-tenant/) | Attribute conditions, `contains()`, optional attribute guards, plan-tier gating | You need decisions based on resource or user attributes, not just roles |
11
+ | [api-gateway-path-routing](./api-gateway-path-routing/) | Path matching with `like`, depth limiting via negation, method restriction | You're authorizing HTTP requests at a gateway or proxy |
12
+
13
+ ## Run all examples offline
14
+
15
+ Each example includes a `run.ts` that exercises all tools against the example files. No MCP client or AI assistant required.
16
+
17
+ ```bash
18
+ npx tsx examples/rbac-document-management/run.ts
19
+ npx tsx examples/abac-multi-tenant/run.ts
20
+ npx tsx examples/api-gateway-path-routing/run.ts
21
+ ```
22
+
23
+ ## Use with Claude Code
24
+
25
+ Add `cedar-mcp-server` to your MCP configuration, then paste the prompts from any example README directly into a conversation. The examples are designed so the tool inputs fit in a single message.
26
+
27
+ ```json
28
+ {
29
+ "mcpServers": {
30
+ "cedar": {
31
+ "command": "npx",
32
+ "args": ["-y", "cedar-mcp-server"]
33
+ }
34
+ }
35
+ }
36
+ ```
@@ -0,0 +1,150 @@
1
+ # ABAC Multi-Tenant (SaaS document collaboration)
2
+
3
+ Attribute-based access control for a multi-tenant SaaS app where decisions depend on document visibility, ownership, and user plan tier.
4
+
5
+ ## What this example covers
6
+
7
+ Four Cedar patterns that appear together in almost every real SaaS authorization model:
8
+
9
+ - **Name-based identity**: `principal.name == resource.owner_id` — matching a user to their own resources
10
+ - **Array containment**: `["internal", "public"].contains(resource.visibility)` — allowlist of values
11
+ - **Plan-tier gating**: `principal.plan == "pro" || principal.plan == "enterprise"` — feature flagging in Cedar
12
+ - **`forbid` with `unless`**: blocks private document access except for the owner
13
+
14
+ Demonstrates all five tools, with `cedar_check_policy_change` showing the principal-change trap.
15
+
16
+ ## Quick start
17
+
18
+ Configure the MCP server in Claude Code (`.claude/settings.json`):
19
+
20
+ ```json
21
+ {
22
+ "mcpServers": {
23
+ "cedar": {
24
+ "command": "npx",
25
+ "args": ["-y", "cedar-mcp-server"]
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ Or run offline:
32
+
33
+ ```bash
34
+ npx tsx examples/abac-multi-tenant/run.ts
35
+ ```
36
+
37
+ ## Files
38
+
39
+ ```
40
+ schema.json SaaS namespace: User (name, plan), Document (visibility, owner_id)
41
+ policies/
42
+ owner-full-access.cedar Owners have full access to their documents
43
+ member-read-internal.cedar Authenticated users can read internal/public docs
44
+ premium-share-guard.cedar Only pro/enterprise plan can share
45
+ private-doc-guard.cedar Private docs blocked for non-owners (forbid + unless)
46
+ entities/
47
+ users-and-docs.json Alice (enterprise), Bob (pro), Charlie (free) + documents
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Tool examples — copy and paste to Claude Code
53
+
54
+ ### cedar_authorize
55
+
56
+ ```
57
+ Is charlie allowed to read the salary-review document?
58
+
59
+ Policies: [paste all .cedar files]
60
+ Principal: SaaS::User::"charlie"
61
+ Action: SaaS::Action::"READ"
62
+ Resource: SaaS::Document::"salary-review"
63
+ Entities: [paste entities/users-and-docs.json]
64
+ Schema: [paste schema.json]
65
+ ```
66
+
67
+ Expected: **Deny** — `salary-review` has `visibility: "private"` and `owner_id: "alice"`. Charlie is not the owner, so the `private-doc-guard` forbid fires.
68
+
69
+ ```
70
+ Can charlie share the public-changelog?
71
+ ```
72
+
73
+ Expected: **Deny** — `premium-share-guard` requires `plan == "pro"` or `"enterprise"`. Charlie is on the free plan.
74
+
75
+ ### cedar_explain
76
+
77
+ ```
78
+ Explain this Cedar policy:
79
+
80
+ permit (
81
+ principal,
82
+ action == SaaS::Action::"READ",
83
+ resource
84
+ )
85
+ when {
86
+ ["internal", "public"].contains(resource.visibility)
87
+ };
88
+ ```
89
+
90
+ Expected: plain-English breakdown including the `contains()` containment check and the pattern detection `attribute_containment_check`.
91
+
92
+ ### cedar_generate_sample_request
93
+
94
+ ```
95
+ Generate a sample request that would be ALLOWED by this policy:
96
+
97
+ permit (
98
+ principal,
99
+ action == SaaS::Action::"SHARE",
100
+ resource
101
+ )
102
+ when {
103
+ principal.plan == "pro" || principal.plan == "enterprise"
104
+ };
105
+
106
+ Schema: [paste schema.json]
107
+ ```
108
+
109
+ Expected: a principal with `plan: "pro"` or `"enterprise"`, with an explanation of which condition was satisfied.
110
+
111
+ ### cedar_check_policy_change
112
+
113
+ ```
114
+ Can this change be applied in-place in Amazon Verified Permissions?
115
+
116
+ Old policy:
117
+ permit(principal, action, resource) when { principal.name == resource.owner_id };
118
+
119
+ New policy:
120
+ permit(principal in SaaS::Role::"editor", action, resource);
121
+ ```
122
+
123
+ Expected: **cannot update in-place** — switching from a condition-based identity check to a role-based principal scope changes the principal clause, which AVP treats as immutable.
124
+
125
+ ---
126
+
127
+ ## Test cases
128
+
129
+ | Principal | Action | Resource | Expected | Reason |
130
+ |-----------|--------|----------|----------|--------|
131
+ | alice (owner) | READ | salary-review (private) | **Allow** | Owner exempt from private-doc-guard |
132
+ | alice (owner) | DELETE | q4-roadmap | **Allow** | Owner full access |
133
+ | bob (non-owner, pro) | READ | q4-roadmap (internal) | **Allow** | member-read-internal matches |
134
+ | bob (non-owner, pro) | READ | salary-review (private) | **Deny** | private-doc-guard forbid, bob is not owner |
135
+ | bob (pro) | SHARE | public-changelog | **Allow** | pro plan satisfies premium-share-guard |
136
+ | charlie (non-owner, free) | READ | public-changelog (public) | **Allow** | member-read-internal: "public" in allowlist |
137
+ | charlie (free) | SHARE | public-changelog | **Deny** | free plan fails premium-share-guard |
138
+ | charlie (non-owner, free) | READ | salary-review (private) | **Deny** | private-doc-guard forbid |
139
+
140
+ ---
141
+
142
+ ## Common pitfalls in this pattern
143
+
144
+ **`principal.name` is not authentication.** Cedar doesn't verify identity — it evaluates the entity attributes you provide. If you pass `attrs: { name: "alice" }`, Cedar trusts it. The security boundary is in how you build your entity store, not in the Cedar policy.
145
+
146
+ **Array containment goes array-first.** Cedar uses `["a", "b"].contains(value)` — the array is on the LEFT, the value is on the right. Writing `value.contains("a")` is a type error because strings don't have a `contains` method in Cedar.
147
+
148
+ **`forbid` + `unless` is not the same as two separate permits.** The `unless` clause in `private-doc-guard` means "forbid UNLESS owner". Without the `unless`, the forbid would block the owner too and no `permit` could override it. Never assume a `permit` can override a `forbid` — it cannot.
149
+
150
+ **Plan-tier changes need policy updates.** If a user upgrades from free to pro, their `plan` attribute in the entity store must be updated before Cedar reflects the change. Cedar has no concept of time — it evaluates the current entity state only.
@@ -0,0 +1,33 @@
1
+ [
2
+ {
3
+ "uid": { "type": "SaaS::User", "id": "alice" },
4
+ "attrs": { "name": "alice", "plan": "enterprise" },
5
+ "parents": []
6
+ },
7
+ {
8
+ "uid": { "type": "SaaS::User", "id": "bob" },
9
+ "attrs": { "name": "bob", "plan": "pro" },
10
+ "parents": []
11
+ },
12
+ {
13
+ "uid": { "type": "SaaS::User", "id": "charlie" },
14
+ "attrs": { "name": "charlie", "plan": "free" },
15
+ "parents": []
16
+ },
17
+ { "uid": { "type": "SaaS::Workspace", "id": "acme-corp" }, "attrs": {}, "parents": [] },
18
+ {
19
+ "uid": { "type": "SaaS::Document", "id": "q4-roadmap" },
20
+ "attrs": { "visibility": "internal", "owner_id": "alice" },
21
+ "parents": [{ "type": "SaaS::Workspace", "id": "acme-corp" }]
22
+ },
23
+ {
24
+ "uid": { "type": "SaaS::Document", "id": "public-changelog" },
25
+ "attrs": { "visibility": "public", "owner_id": "alice" },
26
+ "parents": [{ "type": "SaaS::Workspace", "id": "acme-corp" }]
27
+ },
28
+ {
29
+ "uid": { "type": "SaaS::Document", "id": "salary-review" },
30
+ "attrs": { "visibility": "private", "owner_id": "alice" },
31
+ "parents": [{ "type": "SaaS::Workspace", "id": "acme-corp" }]
32
+ }
33
+ ]
@@ -0,0 +1,9 @@
1
+ // Any authenticated user can read internal and public documents
2
+ permit (
3
+ principal,
4
+ action == SaaS::Action::"READ",
5
+ resource
6
+ )
7
+ when {
8
+ ["internal", "public"].contains(resource.visibility)
9
+ };
@@ -0,0 +1,9 @@
1
+ // Document owners have full access to their own documents
2
+ permit (
3
+ principal,
4
+ action,
5
+ resource
6
+ )
7
+ when {
8
+ principal.name == resource.owner_id
9
+ };
@@ -0,0 +1,9 @@
1
+ // Only pro and enterprise plan users can share documents
2
+ permit (
3
+ principal,
4
+ action == SaaS::Action::"SHARE",
5
+ resource
6
+ )
7
+ when {
8
+ principal.plan == "pro" || principal.plan == "enterprise"
9
+ };
@@ -0,0 +1,13 @@
1
+ // Private documents are blocked for everyone except the owner
2
+ // Uses optional attribute guard: check visibility before accessing
3
+ forbid (
4
+ principal,
5
+ action,
6
+ resource
7
+ )
8
+ when {
9
+ resource.visibility == "private"
10
+ }
11
+ unless {
12
+ principal.name == resource.owner_id
13
+ };